|
a/src/sc2mpd.cpp |
|
b/src/sc2mpd.cpp |
|
... |
|
... |
18 |
*
|
18 |
*
|
19 |
* You should have received a copy of the GNU General Public License
|
19 |
* You should have received a copy of the GNU General Public License
|
20 |
* along with this program; if not, write to the
|
20 |
* along with this program; if not, write to the
|
21 |
* Free Software Foundation, Inc.,
|
21 |
* Free Software Foundation, Inc.,
|
22 |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
22 |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
23 |
*
|
|
|
24 |
* Modified from ohSongcast/Receiver/Receiver.cpp
|
23 |
*/
|
25 |
*/
|
24 |
#include "config.h"
|
26 |
#include "config.h"
|
25 |
|
27 |
|
26 |
#include <OpenHome/OhNetTypes.h>
|
28 |
#include <OpenHome/OhNetTypes.h>
|
27 |
#include <OpenHome/Private/Ascii.h>
|
29 |
#include <OpenHome/Private/Ascii.h>
|
|
... |
|
... |
35 |
|
37 |
|
36 |
#include "workqueue.h"
|
38 |
#include "workqueue.h"
|
37 |
#include "rcvqueue.h"
|
39 |
#include "rcvqueue.h"
|
38 |
#include "log.h"
|
40 |
#include "log.h"
|
39 |
#include "conftree.h"
|
41 |
#include "conftree.h"
|
|
|
42 |
#include "chrono.h"
|
40 |
|
43 |
|
41 |
#include <vector>
|
44 |
#include <vector>
|
42 |
#include <stdio.h>
|
45 |
#include <stdio.h>
|
43 |
#include <iostream>
|
46 |
#include <iostream>
|
|
|
47 |
|
|
|
48 |
#include <sys/types.h>
|
|
|
49 |
#include <sys/stat.h>
|
|
|
50 |
#include <fcntl.h>
|
|
|
51 |
|
44 |
using namespace std;
|
52 |
using namespace std;
|
45 |
|
53 |
|
46 |
WorkQueue<AudioMessage*> audioqueue("audioqueue", 2);
|
54 |
WorkQueue<AudioMessage*> audioqueue("audioqueue", 2);
|
47 |
|
55 |
|
48 |
#ifdef _WIN32
|
56 |
#ifdef _WIN32
|
49 |
|
57 |
|
50 |
#pragma warning(disable:4355) // use of 'this' in ctor lists safe in this case
|
58 |
#pragma warning(disable:4355) // use of 'this' in ctor lists safe in this case
|
51 |
|
59 |
|
52 |
#define CDECL __cdecl
|
60 |
#define CDECL __cdecl
|
53 |
|
|
|
54 |
#include <conio.h>
|
|
|
55 |
|
61 |
|
56 |
int mygetch()
|
62 |
int mygetch()
|
57 |
{
|
63 |
{
|
58 |
return (_getch());
|
64 |
return (_getch());
|
59 |
}
|
65 |
}
|
|
... |
|
... |
105 |
virtual void Process(OhmMsgAudio& aMsg);
|
111 |
virtual void Process(OhmMsgAudio& aMsg);
|
106 |
virtual void Process(OhmMsgTrack& aMsg);
|
112 |
virtual void Process(OhmMsgTrack& aMsg);
|
107 |
virtual void Process(OhmMsgMetatext& aMsg);
|
113 |
virtual void Process(OhmMsgMetatext& aMsg);
|
108 |
|
114 |
|
109 |
private:
|
115 |
private:
|
|
|
116 |
// Debug, stats, etc while we get to understand the Songcast streams
|
|
|
117 |
class Observer {
|
|
|
118 |
public:
|
110 |
TBool iReset;
|
119 |
TBool iReset;
|
111 |
TUint iCount;
|
120 |
TUint iCount;
|
112 |
TUint iFrame;
|
121 |
TUint iFrame;
|
|
|
122 |
int dumpfd;
|
|
|
123 |
Chrono chron;
|
|
|
124 |
Observer() : iReset(true), iCount(0), iFrame(0), dumpfd(-1) {
|
|
|
125 |
#if 0
|
|
|
126 |
dumpfd =
|
|
|
127 |
open("/y/av/tmp/sc2dump", O_WRONLY|O_CREAT|O_TRUNC, 0666);
|
|
|
128 |
if (dumpfd < 0) {
|
|
|
129 |
LOGERR("OhmReceiverDriver::Open dump file failed\n");
|
|
|
130 |
}
|
|
|
131 |
#endif
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
void reset() {
|
|
|
135 |
iReset = true;
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
void process(OhmMsgAudio& aMsg);
|
|
|
139 |
};
|
|
|
140 |
Observer obs;
|
113 |
};
|
141 |
};
|
114 |
|
142 |
|
115 |
|
|
|
116 |
OhmReceiverDriver::OhmReceiverDriver(int port)
|
143 |
OhmReceiverDriver::OhmReceiverDriver(int port)
|
117 |
{
|
144 |
{
|
118 |
iReset = true;
|
|
|
119 |
iCount = 0;
|
|
|
120 |
AudioEaterContext *ctxt = new AudioEaterContext(port);
|
145 |
AudioEaterContext *ctxt = new AudioEaterContext(port);
|
121 |
audioqueue.start(1, &audioEater, ctxt);
|
146 |
audioqueue.start(1, &audioEater, ctxt);
|
122 |
}
|
147 |
}
|
123 |
|
148 |
|
124 |
void OhmReceiverDriver::Add(OhmMsg& aMsg)
|
149 |
void OhmReceiverDriver::Add(OhmMsg& aMsg)
|
|
... |
|
... |
136 |
LOGDEB("=== STARTED ====\n");
|
161 |
LOGDEB("=== STARTED ====\n");
|
137 |
}
|
162 |
}
|
138 |
|
163 |
|
139 |
void OhmReceiverDriver::Connected()
|
164 |
void OhmReceiverDriver::Connected()
|
140 |
{
|
165 |
{
|
141 |
iReset = true;
|
166 |
obs.reset();
|
142 |
LOGDEB("=== CONNECTED ====\n");
|
167 |
LOGDEB("=== CONNECTED ====\n");
|
143 |
}
|
168 |
}
|
144 |
|
169 |
|
145 |
void OhmReceiverDriver::Playing()
|
170 |
void OhmReceiverDriver::Playing()
|
146 |
{
|
171 |
{
|
|
... |
|
... |
157 |
void OhmReceiverDriver::Stopped()
|
182 |
void OhmReceiverDriver::Stopped()
|
158 |
{
|
183 |
{
|
159 |
LOGDEB("=== STOPPED ====\n");
|
184 |
LOGDEB("=== STOPPED ====\n");
|
160 |
}
|
185 |
}
|
161 |
|
186 |
|
162 |
void OhmReceiverDriver::Process(OhmMsgAudio& aMsg)
|
187 |
void OhmReceiverDriver::Observer::process(OhmMsgAudio& aMsg)
|
163 |
{
|
188 |
{
|
164 |
if (++iCount == 400 || aMsg.Halt()) {
|
189 |
if (++iCount == 400 || aMsg.Halt()) {
|
|
|
190 |
static unsigned long long last_timestamp;
|
|
|
191 |
unsigned long long timestamp = aMsg.MediaTimestamp();
|
165 |
LOGDEB("OhmRcvDrv::Process:audio: samplerate " << aMsg.SampleRate() <<
|
192 |
LOGDEB("OhmRcvDrv::Process:audio: samplerate " << aMsg.SampleRate() <<
|
166 |
" bitdepth " << aMsg.BitDepth() << " channels " <<
|
193 |
" bitdepth " << aMsg.BitDepth() << " channels " <<
|
167 |
aMsg.Channels() << " samples " << aMsg.Samples() <<
|
194 |
aMsg.Channels() << " samples " << aMsg.Samples() <<
|
168 |
" Halted ? " << aMsg.Halt() << endl);
|
195 |
" Halted ? " << aMsg.Halt() << endl);
|
|
|
196 |
|
|
|
197 |
if (last_timestamp) {
|
|
|
198 |
long long intervalus =
|
|
|
199 |
((timestamp - last_timestamp) * 1000000) / (256*48000);
|
|
|
200 |
long long atsus =
|
|
|
201 |
((timestamp) * 1000000) / (256*48000);
|
|
|
202 |
long long absus = chron.amicros() - 1430477861905884LL;
|
|
|
203 |
LOGDEB("Computed-uS: " << intervalus <<
|
|
|
204 |
" Elapsed-uS: " << chron.urestart() <<
|
|
|
205 |
" Timestamp-uS: " << atsus <<
|
|
|
206 |
" Abs-uS: " << absus <<
|
|
|
207 |
" Diff-mS " << (absus - atsus) / 1000 <<
|
|
|
208 |
endl);
|
|
|
209 |
}
|
|
|
210 |
last_timestamp = timestamp;
|
|
|
211 |
|
|
|
212 |
if (!aMsg.Halt()) {
|
|
|
213 |
unsigned int bytes =
|
|
|
214 |
aMsg.Samples() * (aMsg.BitDepth() / 8) * aMsg.Channels();
|
|
|
215 |
|
|
|
216 |
if (bytes != aMsg.Audio().Bytes()) {
|
|
|
217 |
LOGERR("OhmRcvDrv::Process:audio: computed bytes " << bytes <<
|
|
|
218 |
" != bufer's " << aMsg.Audio().Bytes() << endl);
|
|
|
219 |
bytes = aMsg.Audio().Bytes();
|
|
|
220 |
}
|
|
|
221 |
const unsigned char *icp =
|
|
|
222 |
(const unsigned char *)aMsg.Audio().Ptr();
|
|
|
223 |
bool silence = true;
|
|
|
224 |
for (unsigned int i = 0; i < bytes; i++) {
|
|
|
225 |
if (icp[i]) {
|
|
|
226 |
silence = false;
|
|
|
227 |
break;
|
|
|
228 |
}
|
|
|
229 |
}
|
|
|
230 |
if (silence) {
|
|
|
231 |
LOGDEB("OhmRcvDrv::Process:audio: silence buffer" << endl);
|
|
|
232 |
}
|
|
|
233 |
if (dumpfd >= 0) {
|
|
|
234 |
if (write(dumpfd, icp, bytes) != int(bytes)) {
|
|
|
235 |
;
|
|
|
236 |
}
|
|
|
237 |
}
|
|
|
238 |
}
|
|
|
239 |
|
169 |
iCount = 0;
|
240 |
iCount = 0;
|
170 |
}
|
241 |
}
|
171 |
|
242 |
|
172 |
if (iReset) {
|
243 |
if (iReset) {
|
173 |
iFrame = aMsg.Frame();
|
244 |
iFrame = aMsg.Frame();
|
|
... |
|
... |
177 |
LOGINF("Missed frames between " << iFrame << " and " <<
|
248 |
LOGINF("Missed frames between " << iFrame << " and " <<
|
178 |
aMsg.Frame());
|
249 |
aMsg.Frame());
|
179 |
}
|
250 |
}
|
180 |
iFrame = aMsg.Frame();
|
251 |
iFrame = aMsg.Frame();
|
181 |
}
|
252 |
}
|
|
|
253 |
}
|
182 |
|
254 |
|
|
|
255 |
void OhmReceiverDriver::Process(OhmMsgAudio& aMsg)
|
|
|
256 |
{
|
|
|
257 |
|
183 |
if (aMsg.Audio().Bytes() == 0)
|
258 |
if (aMsg.Audio().Bytes() == 0) {
|
|
|
259 |
LOGDEB("OhmReceiverDriver::Process: empty message\n");
|
184 |
return;
|
260 |
return;
|
|
|
261 |
}
|
185 |
|
262 |
|
186 |
unsigned int bytes =
|
|
|
187 |
aMsg.Samples() * (aMsg.BitDepth() / 8) * aMsg.Channels();
|
|
|
188 |
|
|
|
189 |
if (bytes != aMsg.Audio().Bytes()) {
|
|
|
190 |
LOGERR("OhmRcvDrv::Process:audio: computed bytes " << bytes <<
|
|
|
191 |
" != bufer's " << aMsg.Audio().Bytes() << endl);
|
|
|
192 |
bytes = aMsg.Audio().Bytes();
|
263 |
unsigned int bytes = aMsg.Audio().Bytes();
|
193 |
}
|
|
|
194 |
|
|
|
195 |
char *buf = (char *)malloc(bytes);
|
264 |
char *buf = (char *)malloc(bytes);
|
|
|
265 |
if (buf == 0) {
|
|
|
266 |
LOGERR("OhmReceiverDriver::Process: can't allocate " <<
|
|
|
267 |
bytes << " bytes\n");
|
|
|
268 |
return;
|
|
|
269 |
}
|
|
|
270 |
|
|
|
271 |
#ifdef WORDS_BIGENDIAN
|
|
|
272 |
memcpy(buf, aMsg.Audio().Ptr(), bytes);
|
|
|
273 |
#else
|
196 |
if (aMsg.BitDepth() == 16) {
|
274 |
if (aMsg.BitDepth() == 16) {
|
197 |
swab(aMsg.Audio().Ptr(), buf, bytes);
|
275 |
swab(aMsg.Audio().Ptr(), buf, bytes);
|
198 |
} else if (aMsg.BitDepth() == 24) {
|
276 |
} else if (aMsg.BitDepth() == 24) {
|
199 |
unsigned char *ocp = (unsigned char *)buf;
|
277 |
unsigned char *ocp = (unsigned char *)buf;
|
200 |
const unsigned char *icp = (const unsigned char *)aMsg.Audio().Ptr();
|
278 |
const unsigned char *icp = (const unsigned char *)aMsg.Audio().Ptr();
|
|
... |
|
... |
204 |
*ocp++ = icp[1];
|
282 |
*ocp++ = icp[1];
|
205 |
*ocp++ = *icp;
|
283 |
*ocp++ = *icp;
|
206 |
icp += 3;
|
284 |
icp += 3;
|
207 |
}
|
285 |
}
|
208 |
} else if (aMsg.BitDepth() == 32) {
|
286 |
} else if (aMsg.BitDepth() == 32) {
|
|
|
287 |
// Never seen this but whatever...
|
|
|
288 |
unsigned char *ocp = (unsigned char *)buf;
|
|
|
289 |
const unsigned char *icp = (const unsigned char *)aMsg.Audio().Ptr();
|
|
|
290 |
const unsigned char *icp0 = icp;
|
|
|
291 |
while (icp - icp0 <= int(bytes) - 4) {
|
|
|
292 |
*ocp++ = icp[3];
|
|
|
293 |
*ocp++ = icp[2];
|
|
|
294 |
*ocp++ = icp[1];
|
|
|
295 |
*ocp++ = *icp;
|
|
|
296 |
icp += 4;
|
209 |
}
|
297 |
}
|
|
|
298 |
}
|
|
|
299 |
#endif
|
210 |
|
300 |
|
211 |
AudioMessage *ap = new
|
301 |
AudioMessage *ap = new
|
212 |
AudioMessage(aMsg.BitDepth(), aMsg.Channels(), aMsg.Samples(),
|
302 |
AudioMessage(aMsg.BitDepth(), aMsg.Channels(), aMsg.Samples(),
|
213 |
aMsg.SampleRate(), buf);
|
303 |
aMsg.SampleRate(), buf);
|
214 |
|
304 |
|