1 /*
2 * OpenSound media addon for BeOS and Haiku
3 *
4 * Copyright (c) 2007, François Revol (revol@free.fr)
5 * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
6 * Distributed under the terms of the MIT License.
7 */
8
9
10 #include "OpenSoundNode.h"
11
12 #include <AutoDeleter.h>
13 #include <Autolock.h>
14 #include <Buffer.h>
15 #include <BufferGroup.h>
16 #include <Debug.h>
17 #include <MediaAddOn.h>
18 #include <MediaRoster.h>
19 #include <scheduler.h>
20 #include <String.h>
21
22 #include <new>
23 #include <limits.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #ifdef DEBUG
29 #define PRINTING
30 #endif
31 #include "debug.h"
32
33 #include "OpenSoundDevice.h"
34 #include "OpenSoundDeviceEngine.h"
35 #include "OpenSoundDeviceMixer.h"
36 #include "SupportFunctions.h"
37
38 using std::nothrow;
39
40
41 class FunctionTracer {
42 public:
FunctionTracer(const char * functionName)43 FunctionTracer(const char* functionName)
44 : fFunctionName(functionName)
45 {
46 printf("OpenSoundNode::%s()\n", fFunctionName.String());
47 }
~FunctionTracer()48 ~FunctionTracer()
49 {
50 printf("OpenSoundNode::%s() - leave\n", fFunctionName.String());
51 }
52 BString fFunctionName;
53 };
54
55
56 // debugging
57 #ifdef TRACE
58 # undef TRACE
59 #endif
60 #ifdef CALLED
61 # undef CALLED
62 #endif
63 //#define TRACE_OSS_NODE
64 #ifdef TRACE_OSS_NODE
65 # define TRACE(x...) printf(x)
66 # define CALLED(x...) FunctionTracer _ft(__FUNCTION__)
67 # define PRINTING
68 #else
69 # define TRACE(x...)
70 # define CALLED(x...)
71 #endif
72
73
74 class OpenSoundNode::NodeInput {
75 public:
NodeInput(const media_input & input,int engineIndex,int ossFormatFlags,OpenSoundNode * node)76 NodeInput(const media_input& input, int engineIndex, int ossFormatFlags,
77 OpenSoundNode* node)
78 : fNode(node),
79 fEngineIndex(engineIndex),
80 fRealEngine(NULL),
81 fOSSFormatFlags(ossFormatFlags),
82
83 fInput(input),
84 fPreferredFormat(input.format),
85 // Keep a version of the original preferred format,
86 // in case we are re-connected and need to start "clean"
87
88 fThread(-1),
89 fBuffers(4),
90
91 fTestTonePhase(0)
92 {
93 CALLED();
94
95 fInput.format.u.raw_audio.format
96 = media_raw_audio_format::wildcard.format;
97 }
98
~NodeInput()99 ~NodeInput()
100 {
101 CALLED();
102
103 fNode->_StopPlayThread(this);
104
105 RecycleAllBuffers();
106 }
107
Write(void * data,size_t size)108 status_t Write(void* data, size_t size)
109 {
110 CALLED();
111
112 ssize_t written = fRealEngine->Write(data, size);
113
114 if (written < 0)
115 return (status_t)written;
116 if (written < (ssize_t)size)
117 return B_IO_ERROR;
118
119 return B_OK;
120 }
121
WriteTestTone(size_t bytes)122 void WriteTestTone(size_t bytes)
123 {
124 // phase of the sine wave
125 uint8 buffer[bytes];
126 float sampleRate = fInput.format.u.raw_audio.frame_rate;
127
128 const static int kSineBuffer[48] = {
129 0, 4276, 8480, 12539, 16383, 19947, 23169, 25995,
130 28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272,
131 28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276,
132 0, -4276, -8480, -12539, -16383, -19947, -23169, -25995,
133 -28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272,
134 -28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276
135 };
136
137 short* b = (short*)buffer;
138 // TODO: assumes 16 bit samples!
139 int32 channels = fInput.format.u.raw_audio.channel_count;
140 int32 frames = bytes / bytes_per_frame(fInput.format);
141 for (int32 i = 0; i < frames; i ++) {
142 // convert sample rate from 48000 to connected format
143 uint32 p = (uint32)((fTestTonePhase * sampleRate) / 48000);
144
145 // prevent phase from integer overflow
146 fTestTonePhase = (fTestTonePhase + 1) % 4800;
147 for (int32 k = 0; k < channels; k++)
148 b[k] = kSineBuffer[p % 48];
149 b += channels;
150 }
151
152 ssize_t written = fRealEngine->Write(buffer, bytes);
153 if (written != (ssize_t)bytes) {
154 // error
155 }
156 }
157
RecycleAllBuffers()158 void RecycleAllBuffers()
159 {
160 CALLED();
161
162 // make sure all buffers are recycled, or we might hang
163 // when told to quit
164 while (BBuffer* buffer = (BBuffer*)fBuffers.RemoveItem((int32)0))
165 buffer->Recycle();
166 }
167
168 OpenSoundNode* fNode;
169 int32 fEngineIndex;
170 OpenSoundDeviceEngine* fRealEngine;
171 // engine it's connected to. can be a shadow one (!= fEngineIndex)
172 int fOSSFormatFlags;
173 // AFMT_* flags for this input
174 media_input fInput;
175 media_format fPreferredFormat;
176
177 thread_id fThread;
178 BList fBuffers;
179 // contains BBuffer* pointers that have not yet played
180
181 uint32 fTestTonePhase;
182 };
183
184
185 class OpenSoundNode::NodeOutput {
186 public:
NodeOutput(const media_output & output,const media_format & format)187 NodeOutput(const media_output& output, const media_format& format)
188 : fNode(NULL),
189 fEngineIndex(-1),
190 fRealEngine(NULL),
191 fOSSFormatFlags(0),
192
193 fOutput(output),
194 fPreferredFormat(format),
195
196 fThread(-1),
197 fBufferGroup(NULL),
198 fUsingOwnBufferGroup(true),
199 fOutputEnabled(true),
200
201 fSamplesSent(0)
202 {
203 CALLED();
204 }
205
~NodeOutput()206 ~NodeOutput()
207 {
208 CALLED();
209
210 fNode->_StopRecThread(this);
211
212 FreeBuffers();
213 }
214
AllocateBuffers(bigtime_t bufferDuration,bigtime_t latency)215 status_t AllocateBuffers(bigtime_t bufferDuration, bigtime_t latency)
216 {
217 TRACE("NodeOutput::AllocateBuffers(bufferDuration = %lld, "
218 "latency = %lld)\n", bufferDuration, latency);
219
220 FreeBuffers();
221
222 // allocate enough buffers to span our downstream latency, plus one
223 size_t size = fOutput.format.u.raw_audio.buffer_size;
224 int32 count = int32(latency / bufferDuration + 1 + 1);
225
226 fBufferGroup = new (nothrow) BBufferGroup(size, count);
227 fUsingOwnBufferGroup = true;
228 return fBufferGroup != NULL ? fBufferGroup->InitCheck() : B_NO_MEMORY;
229 }
230
SetExternalBuffers(BBufferGroup * bufferGroup)231 status_t SetExternalBuffers(BBufferGroup* bufferGroup)
232 {
233 TRACE("NodeOutput::SetExternalBuffers(%p)\n", bufferGroup);
234
235 fBufferGroup = bufferGroup;
236 fUsingOwnBufferGroup = false;
237 return fBufferGroup->InitCheck();
238 }
239
FreeBuffers()240 void FreeBuffers()
241 {
242 TRACE("NodeOutput::FreeBuffers(): %p (own %d)\n", fBufferGroup,
243 fUsingOwnBufferGroup);
244 // TODO: it is not clear to me how buffer group responsibility is supposed
245 // to work properly. Appearantly, a consumer can use SetOutputBuffers(),
246 // which is a deprecated call in the BeOS API, with "willReclaim == true".
247 // In that case, we would not be responsible for deleting these buffers,
248 // but I don't understand what mechanism makes sure that we know about this.
249 // The documentation for SetBufferGroup() says you are supposed to delete
250 // the given buffer group. In any case, the fUsingOwnBufferGroup is correclty
251 // maintained as far as we are concerned, but I delete the buffers anyways,
252 // which is what the code was doing from the beginning and that worked. I
253 // have not tested yet, whether an external buffer group is passed to the node
254 // from the system mixer.
255
256 // if (fUsingOwnBufferGroup)
257 delete fBufferGroup;
258 fBufferGroup = NULL;
259 }
260
FillNextBuffer(bigtime_t bufferDuration)261 BBuffer* FillNextBuffer(bigtime_t bufferDuration)
262 {
263 if (fBufferGroup == NULL)
264 return NULL;
265
266 BBuffer* buffer = fBufferGroup->RequestBuffer(
267 fOutput.format.u.raw_audio.buffer_size, bufferDuration);
268
269 // if we fail to get a buffer (for example, if the request times out),
270 // we skip this buffer and go on to the next, to avoid locking up the
271 // control thread
272 if (!buffer)
273 return NULL;
274
275 // now fill it with data
276 ssize_t sizeUsed = fRealEngine->Read(buffer->Data(),
277 fOutput.format.u.raw_audio.buffer_size);
278 if (sizeUsed < 0) {
279 TRACE("NodeOutput::%s: %s\n", __FUNCTION__,
280 strerror(sizeUsed));
281 buffer->Recycle();
282 return NULL;
283 }
284 if (sizeUsed < (ssize_t)fOutput.format.u.raw_audio.buffer_size) {
285 TRACE("NodeOutput::%s: requested %d, got %d\n", __FUNCTION__,
286 fOutput.format.u.raw_audio.buffer_size, sizeUsed);
287 }
288
289 media_header* hdr = buffer->Header();
290 if (hdr != NULL) {
291 hdr->type = B_MEDIA_RAW_AUDIO;
292 hdr->size_used = sizeUsed;
293 }
294
295 return buffer;
296 }
297
298 OpenSoundNode* fNode;
299 int32 fEngineIndex;
300 OpenSoundDeviceEngine* fRealEngine;
301 // engine it's connected to. can be a shadow one (!= fEngineIndex)
302 int fOSSFormatFlags;
303 // AFMT_* flags for this output
304 media_output fOutput;
305 media_format fPreferredFormat;
306
307 thread_id fThread;
308 BBufferGroup* fBufferGroup;
309 bool fUsingOwnBufferGroup;
310 bool fOutputEnabled;
311 uint64 fSamplesSent;
312 };
313
314
315 // #pragma mark - OpenSoundNode
316
317
OpenSoundNode(BMediaAddOn * addon,const char * name,OpenSoundDevice * device,int32 internal_id,BMessage * config)318 OpenSoundNode::OpenSoundNode(BMediaAddOn* addon, const char* name,
319 OpenSoundDevice* device, int32 internal_id, BMessage* config)
320 : BMediaNode(name),
321 BBufferConsumer(B_MEDIA_RAW_AUDIO),
322 BBufferProducer(B_MEDIA_RAW_AUDIO),
323 BTimeSource(),
324 BMediaEventLooper(),
325
326 fInitCheckStatus(B_NO_INIT),
327 fDevice(device),
328
329 fTimeSourceStarted(false),
330 fTimeSourceStartTime(0),
331
332 fWeb(NULL),
333 fConfig((uint32)0)
334 {
335 CALLED();
336
337 if (fDevice == NULL)
338 return;
339
340 fAddOn = addon;
341 fId = internal_id;
342
343 AddNodeKind(B_PHYSICAL_OUTPUT);
344 AddNodeKind(B_PHYSICAL_INPUT);
345
346 // initialize our preferred format object
347 // TODO: this should go away! should use engine's preferred for each afmt.
348 #if 1
349 fPreferredFormat = media_format();
350 // set everything to wildcard first
351 fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
352 fPreferredFormat.u.raw_audio = media_multi_audio_format::wildcard;
353 fPreferredFormat.u.raw_audio.channel_count = 2;
354 fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
355 OpenSoundDeviceEngine *engine = fDevice->EngineAt(0);
356 if (engine) {
357 const oss_audioinfo* ai = engine->Info();
358 int fmt = OpenSoundDevice::select_oss_format(ai->oformats);
359 fPreferredFormat.u.raw_audio.format
360 = OpenSoundDevice::convert_oss_format_to_media_format(fmt);
361 fPreferredFormat.u.raw_audio.valid_bits
362 = OpenSoundDevice::convert_oss_format_to_valid_bits(fmt);
363 // TODO: engine->PreferredChannels() ? (caps & DSP_CH*)
364 fPreferredFormat.u.raw_audio.frame_rate
365 = OpenSoundDevice::convert_oss_rate_to_media_rate(ai->max_rate); // measured in Hertz
366 }
367
368 // TODO: Use the OSS suggested buffer size via SNDCTL_DSP_GETBLKSIZE ?
369 fPreferredFormat.u.raw_audio.buffer_size = DEFAULT_BUFFER_SIZE
370 * (fPreferredFormat.u.raw_audio.format
371 & media_raw_audio_format::B_AUDIO_SIZE_MASK)
372 * fPreferredFormat.u.raw_audio.channel_count;
373 #endif
374
375 if (config != NULL) {
376 fConfig = *config;
377 PRINT_OBJECT(fConfig);
378 }
379
380 fInitCheckStatus = B_OK;
381 }
382
383
~OpenSoundNode()384 OpenSoundNode::~OpenSoundNode()
385 {
386 CALLED();
387
388 fAddOn->GetConfigurationFor(this, NULL);
389
390 int32 count = fInputs.CountItems();
391 for (int32 i = 0; i < count; i++)
392 delete (NodeInput*)fInputs.ItemAtFast(i);
393 count = fOutputs.CountItems();
394 for (int32 i = 0; i < count; i++)
395 delete (NodeOutput*)fOutputs.ItemAtFast(i);
396
397 BMediaEventLooper::Quit();
398
399 fWeb = NULL;
400 }
401
402
403 status_t
InitCheck() const404 OpenSoundNode::InitCheck() const
405 {
406 CALLED();
407 return fInitCheckStatus;
408 }
409
410
411 // #pragma mark - BMediaNode
412
413
414 BMediaAddOn*
AddOn(int32 * internal_id) const415 OpenSoundNode::AddOn(int32* internal_id) const
416 {
417 CALLED();
418 // BeBook says this only gets called if we were in an add-on.
419 if (fAddOn != 0) {
420 // If we get a null pointer then we just won't write.
421 if (internal_id != 0) {
422 *internal_id = fId;
423 }
424 }
425 return fAddOn;
426 }
427
428
429 void
Preroll()430 OpenSoundNode::Preroll()
431 {
432 CALLED();
433 // XXX:Performance opportunity
434 BMediaNode::Preroll();
435 }
436
437
438 status_t
HandleMessage(int32 message,const void * data,size_t size)439 OpenSoundNode::HandleMessage(int32 message, const void* data, size_t size)
440 {
441 CALLED();
442 return B_ERROR;
443 }
444
445
446 void
NodeRegistered()447 OpenSoundNode::NodeRegistered()
448 {
449 CALLED();
450
451 if (fInitCheckStatus != B_OK) {
452 ReportError(B_NODE_IN_DISTRESS);
453 return;
454 }
455
456 TRACE("NodeRegistered: %d engines\n", fDevice->CountEngines());
457 for (int32 i = 0; i < fDevice->CountEngines(); i++) {
458 OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
459 if (engine == NULL)
460 continue;
461 // skip shadow engines
462 if (engine->Caps() & PCM_CAP_SHADOW)
463 continue;
464 // skip engines that don't have outputs
465 if ((engine->Caps() & PCM_CAP_OUTPUT) == 0)
466 continue;
467
468 TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .oformats=0x%08x\n",
469 i, engine->Caps(), engine->Info()->oformats);
470
471 // iterate over all possible OSS formats/encodings and
472 // create a NodeInput for each
473 for (int32 f = 0; gSupportedFormats[f]; f++) {
474 // figure out if the engine supports the given format
475 int fmt = gSupportedFormats[f] & engine->Info()->oformats;
476 if (fmt == 0)
477 continue;
478 TRACE("NodeRegistered() : creating an input for engine %i, "
479 "format[%i]\n", i, f);
480
481 media_input mediaInput;
482 status_t err = engine->PreferredFormatFor(fmt, mediaInput.format);
483 if (err < B_OK)
484 continue;
485
486 mediaInput.destination.port = ControlPort();
487 mediaInput.destination.id = fInputs.CountItems();
488 mediaInput.node = Node();
489 const char *prefix = "";
490 if (strstr(engine->Info()->name, "SPDIF"))
491 prefix = "S/PDIF ";
492 sprintf(mediaInput.name, "%sOutput %" B_PRId32 " (%s)", prefix,
493 mediaInput.destination.id, gSupportedFormatsNames[f]);
494
495 NodeInput* input = new (nothrow) NodeInput(mediaInput, i, fmt,
496 this);
497 if (input == NULL || !fInputs.AddItem(input)) {
498 delete input;
499 continue;
500 }
501 }
502 }
503
504 for (int32 i = 0; i < fDevice->CountEngines(); i++) {
505 OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
506 if (engine == NULL)
507 continue;
508 // skip shadow engines
509 if (engine->Caps() & PCM_CAP_SHADOW)
510 continue;
511 // skip engines that don't have inputs
512 if ((engine->Caps() & PCM_CAP_INPUT) == 0)
513 continue;
514
515 TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .iformats=0x%08x\n",
516 i, engine->Caps(), engine->Info()->iformats);
517
518 for (int32 f = 0; gSupportedFormats[f]; f++) {
519 int fmt = gSupportedFormats[f] & engine->Info()->iformats;
520 if (fmt == 0)
521 continue;
522 TRACE("NodeRegistered() : creating an output for engine %i, "
523 "format[%i]\n", i, f);
524
525 media_format preferredFormat;
526 status_t err = engine->PreferredFormatFor(fmt, preferredFormat);
527 if (err < B_OK)
528 continue;
529
530 media_output mediaOutput;
531
532 mediaOutput.format = preferredFormat;
533 mediaOutput.destination = media_destination::null;
534 mediaOutput.source.port = ControlPort();
535 mediaOutput.source.id = fOutputs.CountItems();
536 mediaOutput.node = Node();
537 const char *prefix = "";
538 if (strstr(engine->Info()->name, "SPDIF"))
539 prefix = "S/PDIF ";
540 sprintf(mediaOutput.name, "%sInput %" B_PRId32 " (%s)", prefix,
541 mediaOutput.source.id, gSupportedFormatsNames[f]);
542
543 NodeOutput* output = new (nothrow) NodeOutput(mediaOutput,
544 preferredFormat);
545 if (output == NULL || !fOutputs.AddItem(output)) {
546 delete output;
547 continue;
548 }
549 // output->fPreferredFormat.u.raw_audio.channel_count
550 // = engine->Info()->max_channels;
551 output->fOutput.format = output->fPreferredFormat;
552 output->fEngineIndex = i;
553 output->fOSSFormatFlags = fmt;
554 output->fNode = this;
555 }
556 }
557
558 // set up our parameter web
559 fWeb = MakeParameterWeb();
560 SetParameterWeb(fWeb);
561
562 // apply configuration
563 #ifdef TRACE_OSS_NODE
564 bigtime_t start = system_time();
565 #endif
566
567 int32 index = 0;
568 int32 parameterID = 0;
569 while (fConfig.FindInt32("parameterID", index, ¶meterID) == B_OK) {
570 const void* data;
571 ssize_t size;
572 if (fConfig.FindData("parameterData", B_RAW_TYPE, index, &data,
573 &size) == B_OK) {
574 SetParameterValue(parameterID, TimeSource()->Now(), data, size);
575 }
576 index++;
577 }
578
579 TRACE("apply configuration in : %lldµs\n", system_time() - start);
580
581 SetPriority(B_REAL_TIME_PRIORITY);
582 Run();
583 }
584
585
586 status_t
RequestCompleted(const media_request_info & info)587 OpenSoundNode::RequestCompleted(const media_request_info& info)
588 {
589 CALLED();
590 return B_OK;
591 }
592
593
594 void
SetTimeSource(BTimeSource * timeSource)595 OpenSoundNode::SetTimeSource(BTimeSource* timeSource)
596 {
597 CALLED();
598 }
599
600
601 // #pragma mark - BBufferConsumer
602
603
604 //! Someone, probably the producer, is asking you about this format.
605 // Give your honest opinion, possibly modifying *format. Do not ask
606 // upstream producer about the format, since he's synchronously
607 // waiting for your reply.
608 status_t
AcceptFormat(const media_destination & dest,media_format * format)609 OpenSoundNode::AcceptFormat(const media_destination& dest,
610 media_format* format)
611 {
612 // Check to make sure the format is okay, then remove
613 // any wildcards corresponding to our requirements.
614
615 CALLED();
616
617 NodeInput* channel = _FindInput(dest);
618
619 if (channel == NULL) {
620 fprintf(stderr, "OpenSoundNode::AcceptFormat()"
621 " - B_MEDIA_BAD_DESTINATION");
622 return B_MEDIA_BAD_DESTINATION;
623 // we only have one input so that better be it
624 }
625
626 if (format == NULL) {
627 fprintf(stderr, "OpenSoundNode::AcceptFormat() - B_BAD_VALUE\n");
628 return B_BAD_VALUE;
629 }
630
631 /* media_format * myFormat = GetFormat();
632 fprintf(stderr,"proposed format: ");
633 print_media_format(format);
634 fprintf(stderr,"\n");
635 fprintf(stderr,"my format: ");
636 print_media_format(myFormat);
637 fprintf(stderr,"\n");*/
638 // Be's format_is_compatible doesn't work.
639 // if (!format_is_compatible(*format,*myFormat)) {
640
641 BAutolock L(fDevice->Locker());
642
643 OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
644 channel->fEngineIndex, false);
645 if (!engine)
646 return B_BUSY;
647
648 status_t err = engine->AcceptFormatFor(channel->fOSSFormatFlags, *format,
649 false);
650 if (err < B_OK)
651 return err;
652
653 channel->fRealEngine = engine;
654
655 /*
656 if ( format->type != B_MEDIA_RAW_AUDIO ) {
657 fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
658 return B_MEDIA_BAD_FORMAT;
659 }
660 */
661
662 //channel->fInput.format = channel->fPreferredFormat;
663 channel->fInput.format = *format;
664
665 /*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT
666 && channel->fPreferredFormat.u.raw_audio.format
667 == media_raw_audio_format::B_AUDIO_SHORT)
668 format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
669 else*/
670 /*
671 format->u.raw_audio.format = channel->fPreferredFormat.u.raw_audio.format;
672 format->u.raw_audio.valid_bits
673 = channel->fPreferredFormat.u.raw_audio.valid_bits;
674
675 format->u.raw_audio.frame_rate
676 = channel->fPreferredFormat.u.raw_audio.frame_rate;
677 format->u.raw_audio.channel_count
678 = channel->fPreferredFormat.u.raw_audio.channel_count;
679 format->u.raw_audio.byte_order
680 = B_MEDIA_HOST_ENDIAN;
681
682 format->u.raw_audio.buffer_size
683 = DEFAULT_BUFFER_SIZE
684 * (format->u.raw_audio.format
685 & media_raw_audio_format::B_AUDIO_SIZE_MASK)
686 * format->u.raw_audio.channel_count;
687 */
688
689 // media_format myFormat;
690 // GetFormat(&myFormat);
691 // if (!format_is_acceptible(*format,myFormat)) {
692 // fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
693 // return B_MEDIA_BAD_FORMAT;
694 // }
695
696 return B_OK;
697 }
698
699
700 status_t
GetNextInput(int32 * cookie,media_input * out_input)701 OpenSoundNode::GetNextInput(int32* cookie, media_input* out_input)
702 {
703 CALLED();
704
705 // let's not crash even if they are stupid
706 if (out_input == NULL) {
707 // no place to write!
708 fprintf(stderr,"OpenSoundNode::GetNextInput() - B_BAD_VALUE\n");
709 return B_BAD_VALUE;
710 }
711
712 if (*cookie >= fInputs.CountItems() || *cookie < 0)
713 return B_BAD_INDEX;
714
715 NodeInput* channel = (NodeInput*)fInputs.ItemAt(*cookie);
716 *out_input = channel->fInput;
717 *cookie += 1;
718
719 TRACE("input.format : %u\n", channel->fInput.format.u.raw_audio.format);
720
721 return B_OK;
722 }
723
724
725 void
DisposeInputCookie(int32 cookie)726 OpenSoundNode::DisposeInputCookie(int32 cookie)
727 {
728 CALLED();
729 // nothing to do since our cookies are just integers
730 }
731
732
733 void
BufferReceived(BBuffer * buffer)734 OpenSoundNode::BufferReceived(BBuffer* buffer)
735 {
736 CALLED();
737
738 switch (buffer->Header()->type) {
739 // case B_MEDIA_PARAMETERS:
740 // {
741 // status_t status = ApplyParameterData(buffer->Data(),
742 // buffer->SizeUsed());
743 // if (status != B_OK) {
744 // fprintf(stderr, "ApplyParameterData() in "
745 // "OpenSoundNode::BufferReceived() failed: %s\n",
746 // strerror(status));
747 // }
748 // buffer->Recycle();
749 // break;
750 // }
751 case B_MEDIA_RAW_AUDIO:
752 if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
753 fprintf(stderr, "OpenSoundNode::BufferReceived() - "
754 "B_SMALL_BUFFER not implemented\n");
755 // TODO: implement this part
756 buffer->Recycle();
757 } else {
758 media_timed_event event(buffer->Header()->start_time,
759 BTimedEventQueue::B_HANDLE_BUFFER, buffer,
760 BTimedEventQueue::B_RECYCLE_BUFFER);
761 status_t status = EventQueue()->AddEvent(event);
762 if (status != B_OK) {
763 fprintf(stderr, "OpenSoundNode::BufferReceived() - "
764 "EventQueue()->AddEvent() failed: %s\n",
765 strerror(status));
766 buffer->Recycle();
767 }
768 }
769 break;
770 default:
771 fprintf(stderr, "OpenSoundNode::BufferReceived() - unexpected "
772 "buffer type\n");
773 buffer->Recycle();
774 break;
775 }
776 }
777
778
779 void
ProducerDataStatus(const media_destination & for_whom,int32 status,bigtime_t at_performance_time)780 OpenSoundNode::ProducerDataStatus(const media_destination& for_whom,
781 int32 status, bigtime_t at_performance_time)
782 {
783 CALLED();
784
785 NodeInput* channel = _FindInput(for_whom);
786
787 if (channel == NULL) {
788 fprintf(stderr,"OpenSoundNode::ProducerDataStatus() - "
789 "invalid destination\n");
790 return;
791 }
792
793 // TRACE("************ ProducerDataStatus: queuing event ************\n");
794 // TRACE("************ status=%d ************\n", status);
795
796 media_timed_event event(at_performance_time,
797 BTimedEventQueue::B_DATA_STATUS, &channel->fInput,
798 BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
799 EventQueue()->AddEvent(event);
800 }
801
802
803 status_t
GetLatencyFor(const media_destination & for_whom,bigtime_t * out_latency,media_node_id * out_timesource)804 OpenSoundNode::GetLatencyFor(const media_destination& for_whom,
805 bigtime_t* out_latency, media_node_id* out_timesource)
806 {
807 CALLED();
808
809 if (out_latency == NULL || out_timesource == NULL) {
810 fprintf(stderr,"OpenSoundNode::GetLatencyFor() - B_BAD_VALUE\n");
811 return B_BAD_VALUE;
812 }
813
814 NodeInput* channel = _FindInput(for_whom);
815
816 if (channel == NULL || channel->fRealEngine == NULL) {
817 fprintf(stderr,"OpenSoundNode::GetLatencyFor() - "
818 "B_MEDIA_BAD_DESTINATION\n");
819 return B_MEDIA_BAD_DESTINATION;
820 }
821
822 // start with the node latency (1 buffer duration)
823 *out_latency = EventLatency();
824
825 // add the OSS driver buffer's latency as well
826 bigtime_t bufferLatency = channel->fRealEngine->PlaybackLatency();
827 *out_latency += bufferLatency;
828
829 TRACE("OpenSoundNode::GetLatencyFor() - EventLatency %lld, OSS %lld\n",
830 EventLatency(), bufferLatency);
831
832 *out_timesource = TimeSource()->ID();
833
834 return B_OK;
835 }
836
837
838 status_t
Connected(const media_source & producer,const media_destination & where,const media_format & with_format,media_input * out_input)839 OpenSoundNode::Connected(const media_source& producer,
840 const media_destination& where, const media_format& with_format,
841 media_input* out_input)
842 {
843 CALLED();
844
845 if (out_input == NULL) {
846 fprintf(stderr,"OpenSoundNode::Connected() - B_BAD_VALUE\n");
847 return B_BAD_VALUE;
848 }
849
850 NodeInput* channel = _FindInput(where);
851
852 if (channel == NULL) {
853 fprintf(stderr,"OpenSoundNode::Connected() - "
854 "B_MEDIA_BAD_DESTINATION\n");
855 return B_MEDIA_BAD_DESTINATION;
856 }
857
858 BAutolock L(fDevice->Locker());
859
860 // use one half buffer length latency
861 size_t bufferSize = channel->fRealEngine->DriverBufferSize() / 2;
862 fInternalLatency = time_for_buffer(bufferSize, with_format);
863 TRACE(" internal latency = %lld\n", fInternalLatency);
864
865 // TODO: A global node value is assigned a channel specific value!
866 // That can't be correct. For as long as there is only one output
867 // in use at a time, this will not matter of course.
868 SetEventLatency(fInternalLatency);
869
870 // record the agreed upon values
871 channel->fInput.source = producer;
872 channel->fInput.format = with_format;
873
874 *out_input = channel->fInput;
875
876 // we are sure the thread is started
877 _StartPlayThread(channel);
878
879 return B_OK;
880 }
881
882
883 void
Disconnected(const media_source & producer,const media_destination & where)884 OpenSoundNode::Disconnected(const media_source& producer,
885 const media_destination& where)
886 {
887 CALLED();
888
889 NodeInput* channel = _FindInput(where);
890 if (channel == NULL) {
891 fprintf(stderr,"OpenSoundNode::Disconnected() - "
892 "B_MEDIA_BAD_DESTINATION\n");
893 return;
894 }
895 if (channel->fInput.source != producer) {
896 fprintf(stderr,"OpenSoundNode::Disconnected() - "
897 "B_MEDIA_BAD_SOURCE\n");
898 return;
899 }
900
901 _StopPlayThread(channel);
902
903 channel->RecycleAllBuffers();
904
905 channel->fInput.source = media_source::null;
906 channel->fInput.format = channel->fPreferredFormat;
907 if (channel->fRealEngine)
908 channel->fRealEngine->Close();
909 channel->fRealEngine = NULL;
910 }
911
912
913 //! The notification comes from the upstream producer, so he's
914 // already cool with the format; you should not ask him about it
915 // in here.
916 status_t
FormatChanged(const media_source & producer,const media_destination & consumer,int32 change_tag,const media_format & format)917 OpenSoundNode::FormatChanged(const media_source& producer,
918 const media_destination& consumer, int32 change_tag,
919 const media_format& format)
920 {
921 CALLED();
922 NodeInput* channel = _FindInput(consumer);
923
924 if (channel == NULL) {
925 fprintf(stderr,"OpenSoundNode::FormatChanged() - "
926 "B_MEDIA_BAD_DESTINATION\n");
927 return B_MEDIA_BAD_DESTINATION;
928 }
929
930 if (channel->fInput.source != producer) {
931 fprintf(stderr,"OpenSoundNode::FormatChanged() - "
932 "B_MEDIA_BAD_SOURCE\n");
933 return B_MEDIA_BAD_SOURCE;
934 }
935
936 // currently not supported, TODO: implement?
937 return B_ERROR;
938 }
939
940
941 //! Given a performance time of some previous buffer, retrieve the
942 // remembered tag of the closest (previous or exact) performance
943 // time. Set *out_flags to 0; the idea being that flags can be
944 // added later, and the understood flags returned in *out_flags.
945 status_t
SeekTagRequested(const media_destination & destination,bigtime_t in_target_time,uint32 in_flags,media_seek_tag * out_seek_tag,bigtime_t * out_tagged_time,uint32 * out_flags)946 OpenSoundNode::SeekTagRequested(const media_destination& destination,
947 bigtime_t in_target_time, uint32 in_flags, media_seek_tag* out_seek_tag,
948 bigtime_t* out_tagged_time, uint32* out_flags)
949 {
950 CALLED();
951 return BBufferConsumer::SeekTagRequested(destination, in_target_time,
952 in_flags, out_seek_tag, out_tagged_time, out_flags);
953 }
954
955
956 // #pragma mark - BBufferProducer
957
958
959 //! FormatSuggestionRequested() is not necessarily part of the format
960 // negotiation process; it's simply an interrogation -- the caller wants
961 // to see what the node's preferred data format is, given a suggestion by
962 // the caller.
963 status_t
FormatSuggestionRequested(media_type type,int32,media_format * format)964 OpenSoundNode::FormatSuggestionRequested(media_type type, int32 /*quality*/,
965 media_format* format)
966 {
967 CALLED();
968
969 if (format == NULL) {
970 fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
971 return B_BAD_VALUE;
972 }
973
974 // this is the format we'll be returning (our preferred format)
975 *format = fPreferredFormat;
976
977 // a wildcard type is okay; we can specialize it
978 if (type == B_MEDIA_UNKNOWN_TYPE)
979 type = B_MEDIA_RAW_AUDIO;
980
981 // TODO: For OSS engines that support encoded formats, we could
982 // handle this here. For the time being, we only support raw audio.
983 if (type != B_MEDIA_RAW_AUDIO)
984 return B_MEDIA_BAD_FORMAT;
985
986 return B_OK;
987 }
988
989
990 //! FormatProposal() is the first stage in the BMediaRoster::Connect()
991 // process. We hand out a suggested format, with wildcards for any
992 // variations we support.
993 status_t
FormatProposal(const media_source & output,media_format * format)994 OpenSoundNode::FormatProposal(const media_source& output, media_format* format)
995 {
996 CALLED();
997
998 NodeOutput* channel = _FindOutput(output);
999
1000 // is this a proposal for our select output?
1001 if (channel == NULL) {
1002 fprintf(stderr, "OpenSoundNode::FormatProposal returning "
1003 "B_MEDIA_BAD_SOURCE\n");
1004 return B_MEDIA_BAD_SOURCE;
1005 }
1006
1007 media_type requestedType = format->type;
1008 #ifdef ENABLE_REC
1009 OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
1010 channel->fEngineIndex, true);
1011
1012 // We only support raw audio, so we always return that, but we supply an
1013 // error code depending on whether we found the proposal acceptable.
1014 status_t err = engine->PreferredFormatFor(channel->fOSSFormatFlags, *format, true);
1015 if (err < B_OK)
1016 return err;
1017 #else
1018 *format = fPreferredFormat;
1019 #endif
1020 if (requestedType != B_MEDIA_UNKNOWN_TYPE
1021 && requestedType != B_MEDIA_RAW_AUDIO
1022 #ifdef ENABLE_NON_RAW_SUPPORT
1023 && requestedType != B_MEDIA_ENCODED_AUDIO
1024 #endif
1025 )
1026 {
1027 fprintf(stderr, "OpenSoundNode::FormatProposal returning "
1028 "B_MEDIA_BAD_FORMAT\n");
1029 return B_MEDIA_BAD_FORMAT;
1030 }
1031
1032 // raw audio or wildcard type, either is okay by us
1033 return B_OK;
1034 }
1035
1036
1037 status_t
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * io_format,int32 * _deprecated_)1038 OpenSoundNode::FormatChangeRequested(const media_source& source,
1039 const media_destination& destination, media_format* io_format,
1040 int32* _deprecated_)
1041 {
1042 CALLED();
1043
1044 // we don't support any other formats, so we just reject any format
1045 // changes. TODO: implement?
1046 return B_ERROR;
1047 }
1048
1049
1050 status_t
GetNextOutput(int32 * cookie,media_output * out_output)1051 OpenSoundNode::GetNextOutput(int32* cookie, media_output* out_output)
1052 {
1053 CALLED();
1054
1055 if (*cookie >= fOutputs.CountItems() || *cookie < 0)
1056 return B_BAD_INDEX;
1057
1058 NodeOutput *channel = (NodeOutput*)fOutputs.ItemAt(*cookie);
1059 *out_output = channel->fOutput;
1060 *cookie += 1;
1061 return B_OK;
1062 }
1063
1064
1065 status_t
DisposeOutputCookie(int32 cookie)1066 OpenSoundNode::DisposeOutputCookie(int32 cookie)
1067 {
1068 CALLED();
1069 // do nothing because we don't use the cookie for anything special
1070 return B_OK;
1071 }
1072
1073
1074 status_t
SetBufferGroup(const media_source & for_source,BBufferGroup * newGroup)1075 OpenSoundNode::SetBufferGroup(const media_source& for_source,
1076 BBufferGroup* newGroup)
1077 {
1078 CALLED();
1079
1080 NodeOutput* channel = _FindOutput(for_source);
1081
1082 // is this our output?
1083 if (channel == NULL) {
1084 fprintf(stderr, "OpenSoundNode::SetBufferGroup() returning "
1085 "B_MEDIA_BAD_SOURCE\n");
1086 return B_MEDIA_BAD_SOURCE;
1087 }
1088
1089 // Are we being passed the buffer group we're already using?
1090 if (newGroup == channel->fBufferGroup)
1091 return B_OK;
1092
1093 // Ahh, someone wants us to use a different buffer group. At this point
1094 // we delete the one we are using and use the specified one instead. If
1095 // the specified group is NULL, we need to recreate one ourselves, and
1096 // use *that*. Note that if we're caching a BBuffer that we requested
1097 // earlier, we have to Recycle() that buffer *before* deleting the buffer
1098 // group, otherwise we'll deadlock waiting for that buffer to be recycled!
1099 channel->FreeBuffers();
1100 // waits for all buffers to recycle
1101 if (newGroup != NULL) {
1102 // we were given a valid group; just use that one from now on
1103 return channel->SetExternalBuffers(newGroup);
1104 } else {
1105 // we were passed a NULL group pointer; that means we construct
1106 // our own buffer group to use from now on
1107 return channel->AllocateBuffers(BufferDuration(), fLatency);
1108 }
1109 }
1110
1111
1112 //! PrepareToConnect() is the second stage of format negotiations that happens
1113 // inside BMediaRoster::Connect(). At this point, the consumer's
1114 // AcceptFormat() method has been called, and that node has potentially
1115 // changed the proposed format. It may also have left wildcards in the
1116 // format. PrepareToConnect() *must* fully specialize the format before
1117 // returning!
1118 status_t
PrepareToConnect(const media_source & what,const media_destination & where,media_format * format,media_source * out_source,char * out_name)1119 OpenSoundNode::PrepareToConnect(const media_source& what,
1120 const media_destination& where, media_format* format,
1121 media_source* out_source, char* out_name)
1122 {
1123 CALLED();
1124
1125 status_t err;
1126 NodeOutput *channel = _FindOutput(what);
1127
1128 // is this our output?
1129 if (channel == NULL) {
1130 fprintf(stderr, "OpenSoundNode::PrepareToConnect returning "
1131 "B_MEDIA_BAD_SOURCE\n");
1132 return B_MEDIA_BAD_SOURCE;
1133 }
1134
1135 // are we already connected?
1136 if (channel->fOutput.destination != media_destination::null)
1137 return B_MEDIA_ALREADY_CONNECTED;
1138
1139 BAutolock L(fDevice->Locker());
1140
1141 OpenSoundDeviceEngine *engine = fDevice->NextFreeEngineAt(
1142 channel->fEngineIndex, false);
1143 if (engine == NULL)
1144 return B_BUSY;
1145
1146 // the format may not yet be fully specialized (the consumer might have
1147 // passed back some wildcards). Finish specializing it now, and return an
1148 // error if we don't support the requested format.
1149 if (format->type != B_MEDIA_RAW_AUDIO) {
1150 fprintf(stderr, "\tnon-raw-audio format?!\n");
1151 return B_MEDIA_BAD_FORMAT;
1152 }
1153
1154 // !!! validate all other fields except for buffer_size here, because the
1155 // consumer might have supplied different values from AcceptFormat()?
1156 err = engine->SpecializeFormatFor(channel->fOSSFormatFlags, *format, true);
1157 if (err < B_OK)
1158 return err;
1159
1160 channel->fRealEngine = engine;
1161
1162 #if 0
1163 // check the buffer size, which may still be wildcarded
1164 if (format->u.raw_audio.buffer_size
1165 == media_raw_audio_format::wildcard.buffer_size) {
1166 format->u.raw_audio.buffer_size = 2048;
1167 // pick something comfortable to suggest
1168 fprintf(stderr, "\tno buffer size provided, suggesting %lu\n",
1169 format->u.raw_audio.buffer_size);
1170 } else {
1171 fprintf(stderr, "\tconsumer suggested buffer_size %lu\n",
1172 format->u.raw_audio.buffer_size);
1173 }
1174 #endif
1175
1176 // Now reserve the connection, and return information about it
1177 channel->fOutput.destination = where;
1178 channel->fOutput.format = *format;
1179 *out_source = channel->fOutput.source;
1180 strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1181 return B_OK;
1182 }
1183
1184
1185 void
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * io_name)1186 OpenSoundNode::Connect(status_t error, const media_source& source,
1187 const media_destination& destination, const media_format& format,
1188 char* io_name)
1189 {
1190 CALLED();
1191
1192 NodeOutput *channel = _FindOutput(source);
1193
1194 // is this our output?
1195 if (channel == NULL) {
1196 fprintf(stderr, "OpenSoundNode::Connect returning (cause : "
1197 "B_MEDIA_BAD_SOURCE)\n");
1198 return;
1199 }
1200
1201 OpenSoundDeviceEngine *engine = channel->fRealEngine;
1202 if (engine == NULL)
1203 return;
1204
1205 // If something earlier failed, Connect() might still be called, but with
1206 // a non-zero error code. When that happens we simply unreserve the
1207 // connection and do nothing else.
1208 if (error) {
1209 channel->fOutput.destination = media_destination::null;
1210 channel->fOutput.format = channel->fPreferredFormat;
1211 engine->Close();
1212 channel->fRealEngine = NULL;
1213 return;
1214 }
1215
1216 // Okay, the connection has been confirmed. Record the destination and
1217 // format that we agreed on, and report our connection name again.
1218 channel->fOutput.destination = destination;
1219 channel->fOutput.format = format;
1220 strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1221
1222 // reset our buffer duration, etc. to avoid later calculations
1223 bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size
1224 * 10000
1225 / ( (channel->fOutput.format.u.raw_audio.format
1226 & media_raw_audio_format::B_AUDIO_SIZE_MASK)
1227 * channel->fOutput.format.u.raw_audio.channel_count)
1228 / ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100));
1229
1230 SetBufferDuration(duration);
1231
1232 // Now that we're connected, we can determine our downstream latency.
1233 // Do so, then make sure we get our events early enough.
1234 media_node_id id;
1235 FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
1236 TRACE("\tdownstream latency = %lld\n", fLatency);
1237
1238 fInternalLatency = BufferDuration();
1239 TRACE("\tbuffer-filling took %lld usec on this machine\n",
1240 fInternalLatency);
1241 //SetEventLatency(fLatency + fInternalLatency);
1242
1243 // Set up the buffer group for our connection, as long as nobody handed us
1244 // a buffer group (via SetBufferGroup()) prior to this. That can happen,
1245 // for example, if the consumer calls SetOutputBuffersFor() on us from
1246 // within its Connected() method.
1247 if (channel->fBufferGroup == NULL)
1248 channel->AllocateBuffers(BufferDuration(), fLatency);
1249
1250 engine->StartRecording();
1251
1252 // we are sure the thread is started
1253 _StartRecThread(channel);
1254 }
1255
1256
1257 void
Disconnect(const media_source & what,const media_destination & where)1258 OpenSoundNode::Disconnect(const media_source& what,
1259 const media_destination& where)
1260 {
1261 CALLED();
1262
1263 NodeOutput *channel = _FindOutput(what);
1264
1265 // is this our output?
1266 if (channel == NULL) {
1267 fprintf(stderr, "OpenSoundNode::Disconnect() returning (cause : "
1268 "B_MEDIA_BAD_SOURCE)\n");
1269 return;
1270 }
1271
1272 _StopRecThread(channel);
1273
1274 BAutolock L(fDevice->Locker());
1275
1276 OpenSoundDeviceEngine* engine = channel->fRealEngine;
1277
1278 // Make sure that our connection is the one being disconnected
1279 if (where == channel->fOutput.destination
1280 && what == channel->fOutput.source) {
1281 if (engine)
1282 engine->Close();
1283 channel->fRealEngine = NULL;
1284 channel->fOutput.destination = media_destination::null;
1285 channel->fOutput.format = channel->fPreferredFormat;
1286 channel->FreeBuffers();
1287 } else {
1288 fprintf(stderr, "\tDisconnect() called with wrong source/destination "
1289 "(%" B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32
1290 ")\n", what.id, where.id, channel->fOutput.source.id,
1291 channel->fOutput.destination.id);
1292 }
1293 }
1294
1295
1296 void
LateNoticeReceived(const media_source & what,bigtime_t how_much,bigtime_t performance_time)1297 OpenSoundNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
1298 bigtime_t performance_time)
1299 {
1300 CALLED();
1301
1302 // is this our output?
1303 NodeOutput *channel = _FindOutput(what);
1304 if (channel == NULL)
1305 return;
1306
1307 // If we're late, we need to catch up. Respond in a manner appropriate
1308 // to our current run mode.
1309 if (RunMode() == B_RECORDING) {
1310 // A hardware capture node can't adjust; it simply emits buffers at
1311 // appropriate points. We (partially) simulate this by not adjusting
1312 // our behavior upon receiving late notices -- after all, the hardware
1313 // can't choose to capture "sooner"....
1314 } else if (RunMode() == B_INCREASE_LATENCY) {
1315 // We're late, and our run mode dictates that we try to produce buffers
1316 // earlier in order to catch up. This argues that the downstream nodes
1317 // are not properly reporting their latency, but there's not much we
1318 // can do about that at the moment, so we try to start producing
1319 // buffers earlier to compensate.
1320 fInternalLatency += how_much;
1321 SetEventLatency(fLatency + fInternalLatency);
1322
1323 fprintf(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n",
1324 fLatency + fInternalLatency);
1325 } else {
1326 // The other run modes dictate various strategies for sacrificing data
1327 // quality in the interests of timely data delivery. The way *we* do
1328 // this is to skip a buffer, which catches us up in time by one buffer
1329 // duration.
1330 // size_t nSamples = fOutput.format.u.raw_audio.buffer_size
1331 // / sizeof(float);
1332 // mSamplesSent += nSamples;
1333
1334 fprintf(stderr, "\tskipping a buffer to try to catch up\n");
1335 }
1336 }
1337
1338
1339 void
EnableOutput(const media_source & what,bool enabled,int32 * _deprecated_)1340 OpenSoundNode::EnableOutput(const media_source& what, bool enabled,
1341 int32* _deprecated_)
1342 {
1343 CALLED();
1344
1345 // If I had more than one output, I'd have to walk my list of output records to see
1346 // which one matched the given source, and then enable/disable that one. But this
1347 // node only has one output, so I just make sure the given source matches, then set
1348 // the enable state accordingly.
1349 NodeOutput *channel = _FindOutput(what);
1350
1351 if (channel != NULL)
1352 {
1353 channel->fOutputEnabled = enabled;
1354 }
1355 }
1356
1357 void
AdditionalBufferRequested(const media_source & source,media_buffer_id prev_buffer,bigtime_t prev_time,const media_seek_tag * prev_tag)1358 OpenSoundNode::AdditionalBufferRequested(const media_source& source,
1359 media_buffer_id prev_buffer, bigtime_t prev_time,
1360 const media_seek_tag* prev_tag)
1361 {
1362 CALLED();
1363 // we don't support offline mode
1364 return;
1365 }
1366
1367
1368 // #pragma mark - BMediaEventLooper
1369
1370
1371 void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1372 OpenSoundNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
1373 bool realTimeEvent)
1374 {
1375 CALLED();
1376
1377 switch (event->type) {
1378 case BTimedEventQueue::B_START:
1379 HandleStart(event,lateness,realTimeEvent);
1380 break;
1381 case BTimedEventQueue::B_SEEK:
1382 HandleSeek(event,lateness,realTimeEvent);
1383 break;
1384 case BTimedEventQueue::B_WARP:
1385 HandleWarp(event,lateness,realTimeEvent);
1386 break;
1387 case BTimedEventQueue::B_STOP:
1388 HandleStop(event,lateness,realTimeEvent);
1389 break;
1390 case BTimedEventQueue::B_HANDLE_BUFFER:
1391 // TRACE("HandleEvent: B_HANDLE_BUFFER, RunState= %d\n",
1392 // RunState());
1393 if (RunState() == BMediaEventLooper::B_STARTED) {
1394 HandleBuffer(event,lateness,realTimeEvent);
1395 }
1396 break;
1397 case BTimedEventQueue::B_DATA_STATUS:
1398 HandleDataStatus(event,lateness,realTimeEvent);
1399 break;
1400 case BTimedEventQueue::B_PARAMETER:
1401 HandleParameter(event,lateness,realTimeEvent);
1402 break;
1403 default:
1404 fprintf(stderr," unknown event type: %" B_PRId32 "\n",
1405 event->type);
1406 break;
1407 }
1408 }
1409
1410
1411 // protected
1412 status_t
HandleBuffer(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1413 OpenSoundNode::HandleBuffer(const media_timed_event* event,
1414 bigtime_t lateness, bool realTimeEvent)
1415 {
1416 CALLED();
1417
1418 // TODO: How should we handle late buffers? Drop them?
1419 // Notify the producer?
1420
1421 BBuffer* buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
1422 if (buffer == NULL) {
1423 fprintf(stderr,"OpenSoundNode::HandleBuffer() - B_BAD_VALUE\n");
1424 return B_BAD_VALUE;
1425 }
1426
1427 NodeInput *channel = _FindInput(buffer->Header()->destination);
1428 // TRACE("buffer->Header()->destination : %i\n",
1429 // buffer->Header()->destination);
1430
1431 if (channel == NULL) {
1432 buffer->Recycle();
1433 fprintf(stderr,"OpenSoundNode::HandleBuffer() - "
1434 "B_MEDIA_BAD_DESTINATION\n");
1435 return B_MEDIA_BAD_DESTINATION;
1436 }
1437
1438 media_header* hdr = buffer->Header();
1439 bigtime_t now = TimeSource()->Now();
1440 bigtime_t perf_time = hdr->start_time;
1441
1442 // the how_early calculated here doesn't include scheduling latency
1443 // because we've already been scheduled to handle the buffer
1444 bigtime_t how_early = perf_time - EventLatency() - now;
1445
1446 // if the buffer is late, we ignore it and report the fact to the producer
1447 // who sent it to us
1448 if (RunMode() != B_OFFLINE
1449 // lateness doesn't matter in offline mode...
1450 && RunMode() != B_RECORDING
1451 // ...or in recording mode
1452 && how_early < 0LL
1453 && false) {
1454 // TODO: Debug
1455 //mLateBuffers++;
1456 NotifyLateProducer(channel->fInput.source, -how_early, perf_time);
1457 fprintf(stderr," <- LATE BUFFER : %" B_PRIdBIGTIME "\n", how_early);
1458 buffer->Recycle();
1459 } else {
1460 fDevice->Locker()->Lock();
1461 if (channel->fBuffers.CountItems() > 10) {
1462 fDevice->Locker()->Unlock();
1463 TRACE("OpenSoundNode::HandleBuffer too many buffers, "
1464 "recycling\n");
1465 buffer->Recycle();
1466 } else {
1467 // TRACE("OpenSoundNode::HandleBuffer writing channelId : %i,
1468 // how_early:%lli\n", channel->fEngineIndex, how_early);
1469 if (!channel->fBuffers.AddItem(buffer))
1470 buffer->Recycle();
1471 fDevice->Locker()->Unlock();
1472 }
1473 }
1474 return B_OK;
1475 }
1476
1477
1478 status_t
HandleDataStatus(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1479 OpenSoundNode::HandleDataStatus(const media_timed_event* event,
1480 bigtime_t lateness, bool realTimeEvent)
1481 {
1482 // CALLED();
1483
1484 // TODO: this is called mostly whenever the system mixer
1485 // switches from not sending us buffers (no client connected)
1486 // to sending buffers, and vice versa. In a Terminal, this
1487 // can be nicely demonstrated by provoking a system beep while
1488 // nothing else is using audio playback. Any first beep will
1489 // start with a small glitch, while more beeps before the last
1490 // one finished will not have the glitch. I am not sure, but
1491 // I seem to remember that other audio nodes have the same
1492 // problem, so it might not be a problem of this implementation.
1493
1494 BString message("OpenSoundNode::HandleDataStatus status: ");
1495
1496 switch(event->data) {
1497 case B_DATA_NOT_AVAILABLE:
1498 message << "No data";
1499 break;
1500 case B_DATA_AVAILABLE:
1501 message << "Data";
1502 break;
1503 case B_PRODUCER_STOPPED:
1504 message << "Stopped";
1505 break;
1506 default:
1507 message << "???";
1508 break;
1509 }
1510
1511 message << ", lateness: " << lateness;
1512 printf("%s\n", message.String());
1513
1514 return B_OK;
1515 }
1516
1517
1518 status_t
HandleStart(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1519 OpenSoundNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
1520 bool realTimeEvent)
1521 {
1522 CALLED();
1523 if (RunState() != B_STARTED) {
1524 // TODO: What should happen here?
1525 }
1526 return B_OK;
1527 }
1528
1529
1530 status_t
HandleSeek(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1531 OpenSoundNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
1532 bool realTimeEvent)
1533 {
1534 CALLED();
1535 TRACE("OpenSoundNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n",
1536 event->event_time,event->data,event->bigdata);
1537 return B_OK;
1538 }
1539
1540
1541 status_t
HandleWarp(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1542 OpenSoundNode::HandleWarp(const media_timed_event* event,
1543 bigtime_t lateness, bool realTimeEvent)
1544 {
1545 CALLED();
1546 return B_OK;
1547 }
1548
1549
1550 status_t
HandleStop(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1551 OpenSoundNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
1552 bool realTimeEvent)
1553 {
1554 CALLED();
1555 // flush the queue so downstreamers don't get any more
1556 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1557 BTimedEventQueue::B_HANDLE_BUFFER);
1558
1559 return B_OK;
1560 }
1561
1562
1563 status_t
HandleParameter(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1564 OpenSoundNode::HandleParameter(const media_timed_event* event,
1565 bigtime_t lateness, bool realTimeEvent)
1566 {
1567 CALLED();
1568 return B_OK;
1569 }
1570
1571
1572 // #pragma mark - BTimeSource
1573
1574
1575 void
SetRunMode(run_mode mode)1576 OpenSoundNode::SetRunMode(run_mode mode)
1577 {
1578 CALLED();
1579 TRACE("OpenSoundNode::SetRunMode(%d)\n", mode);
1580 //BTimeSource::SetRunMode(mode);
1581 }
1582
1583
1584 status_t
TimeSourceOp(const time_source_op_info & op,void * _reserved)1585 OpenSoundNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
1586 {
1587 CALLED();
1588 switch(op.op) {
1589 case B_TIMESOURCE_START:
1590 TRACE("TimeSourceOp op B_TIMESOURCE_START\n");
1591 if (RunState() != BMediaEventLooper::B_STARTED) {
1592 fTimeSourceStarted = true;
1593 fTimeSourceStartTime = RealTime();
1594
1595 media_timed_event startEvent(0, BTimedEventQueue::B_START);
1596 EventQueue()->AddEvent(startEvent);
1597 }
1598 break;
1599 case B_TIMESOURCE_STOP:
1600 TRACE("TimeSourceOp op B_TIMESOURCE_STOP\n");
1601 if (RunState() == BMediaEventLooper::B_STARTED) {
1602 media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1603 EventQueue()->AddEvent(stopEvent);
1604 fTimeSourceStarted = false;
1605 PublishTime(0, 0, 1.0f);
1606 }
1607 break;
1608 case B_TIMESOURCE_STOP_IMMEDIATELY:
1609 TRACE("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n");
1610 if (RunState() == BMediaEventLooper::B_STARTED) {
1611 media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1612 EventQueue()->AddEvent(stopEvent);
1613 fTimeSourceStarted = false;
1614 PublishTime(0, 0, 1.0f);
1615 }
1616 break;
1617 case B_TIMESOURCE_SEEK:
1618 // TRACE("TimeSourceOp op B_TIMESOURCE_SEEK\n");
1619 printf("TimeSourceOp op B_TIMESOURCE_SEEK, real %" B_PRIdBIGTIME ", "
1620 "perf %" B_PRIdBIGTIME "\n", op.real_time, op.performance_time);
1621 BroadcastTimeWarp(op.real_time, op.performance_time);
1622 break;
1623 default:
1624 break;
1625 }
1626 return B_OK;
1627 }
1628
1629
1630 // #pragma mark - BControllable
1631
1632
1633 status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * ioSize)1634 OpenSoundNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value,
1635 size_t* ioSize)
1636 {
1637 CALLED();
1638
1639 int channelCount = 1;
1640 int sliderShift = 8;
1641
1642 OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1643 if (!mixer)
1644 return ENODEV;
1645
1646 TRACE("id : %i, *ioSize=%d\n", id, *ioSize);
1647
1648 oss_mixext mixext;
1649 status_t err = mixer->GetExtInfo(id, &mixext);
1650 if (err < B_OK)
1651 return err;
1652
1653 oss_mixer_value mixval;
1654 mixval.ctrl = mixext.ctrl;
1655 mixval.timestamp = mixext.timestamp;
1656
1657 err = mixer->GetMixerValue(&mixval);
1658 if (err < B_OK)
1659 return err;
1660
1661 if (!(mixext.flags & MIXF_READABLE))
1662 return EINVAL;
1663
1664 BParameter *parameter = NULL;
1665 for (int32 i = 0; i < fWeb->CountParameters(); i++) {
1666 parameter = fWeb->ParameterAt(i);
1667 if(parameter->ID() == id)
1668 break;
1669 }
1670
1671 if (!parameter)
1672 return ENODEV;
1673
1674 TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1675
1676 *last_change = system_time();//??
1677
1678 switch (mixext.type) {
1679 case MIXT_DEVROOT:
1680 case MIXT_GROUP:
1681 break;
1682 case MIXT_ONOFF:
1683 if (*ioSize < sizeof(bool))
1684 return EINVAL;
1685 *(int32 *)value = mixval.value?true:false;
1686 *ioSize = sizeof(bool);
1687 return B_OK;
1688 case MIXT_ENUM:
1689 if (*ioSize < sizeof(int32))
1690 return EINVAL;
1691 *(int32 *)value = mixval.value;
1692 *ioSize = sizeof(int32);
1693 return B_OK;
1694 break;
1695 case MIXT_STEREODB:
1696 case MIXT_STEREOSLIDER16:
1697 case MIXT_STEREOSLIDER:
1698 channelCount = 2;
1699 case MIXT_SLIDER:
1700 case MIXT_MONODB:
1701 case MIXT_MONOSLIDER16:
1702 case MIXT_MONOSLIDER:
1703 if (*ioSize < channelCount * sizeof(float))
1704 return EINVAL;
1705 if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1706 return EINVAL;
1707 if (mixext.type == MIXT_STEREOSLIDER16 ||
1708 mixext.type == MIXT_MONOSLIDER16)
1709 sliderShift = 16;
1710 *ioSize = channelCount * sizeof(float);
1711 ((float *)value)[0] = (float)(mixval.value & ((1 << sliderShift) - 1));
1712 TRACE("%s: value[O] = %f\n", __FUNCTION__, ((float *)value)[0]);
1713 if (channelCount < 2)
1714 return B_OK;
1715 ((float *)value)[1] = (float)((mixval.value >> sliderShift)
1716 & ((1 << sliderShift) - 1));
1717 TRACE("%s: value[1] = %f\n", __FUNCTION__, ((float *)value)[1]);
1718 return B_OK;
1719 break;
1720 case MIXT_MESSAGE:
1721 break;
1722 case MIXT_MONOVU:
1723 break;
1724 case MIXT_STEREOVU:
1725 break;
1726 case MIXT_MONOPEAK:
1727 break;
1728 case MIXT_STEREOPEAK:
1729 break;
1730 case MIXT_RADIOGROUP:
1731 break;//??
1732 case MIXT_MARKER:
1733 break;// separator item: ignore
1734 case MIXT_VALUE:
1735 break;
1736 case MIXT_HEXVALUE:
1737 break;
1738 /* case MIXT_MONODB:
1739 break;
1740 case MIXT_STEREODB:
1741 break;*/
1742 case MIXT_3D:
1743 break;
1744 /* case MIXT_MONOSLIDER16:
1745 break;
1746 case MIXT_STEREOSLIDER16:
1747 break;*/
1748 default:
1749 TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1750 __FUNCTION__, mixext.type);
1751 }
1752 *ioSize = 0;
1753 return EINVAL;
1754 }
1755
1756
1757 void
SetParameterValue(int32 id,bigtime_t performance_time,const void * value,size_t size)1758 OpenSoundNode::SetParameterValue(int32 id, bigtime_t performance_time,
1759 const void* value, size_t size)
1760 {
1761 CALLED();
1762
1763 TRACE("id : %i, performance_time : %lld, size : %i\n", id,
1764 performance_time, size);
1765
1766 OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
1767 if (mixer == NULL)
1768 return;
1769
1770 oss_mixext mixext;
1771 if (mixer->GetExtInfo(id, &mixext) < B_OK)
1772 return;
1773 if (!(mixext.flags & MIXF_WRITEABLE))
1774 return;
1775
1776 oss_mixer_value mixval;
1777 mixval.ctrl = mixext.ctrl;
1778 mixval.timestamp = mixext.timestamp;
1779
1780 status_t err = mixer->GetMixerValue(&mixval);
1781 if (err < B_OK)
1782 return;
1783
1784 mixval.ctrl = mixext.ctrl;
1785 mixval.timestamp = mixext.timestamp;
1786
1787 BParameter *parameter = NULL;
1788 for(int32 i=0; i<fWeb->CountParameters(); i++) {
1789 parameter = fWeb->ParameterAt(i);
1790 if(parameter->ID() == id)
1791 break;
1792 }
1793
1794 if (!parameter)
1795 return;
1796
1797 int channelCount = 1;
1798 int sliderShift = 8;
1799
1800 switch (mixext.type) {
1801 case MIXT_DEVROOT:
1802 case MIXT_GROUP:
1803 break;
1804 case MIXT_ONOFF:
1805 if (size < sizeof(bool))
1806 return;
1807 mixval.value = (int)*(int32 *)value;
1808 mixer->SetMixerValue(&mixval);
1809 // At least on my ATI IXP, recording selection can't be set to OFF,
1810 // you have to set another one to ON to actually do it,
1811 // and setting to ON changes others to OFF
1812 // So we have to let users know about it.
1813 // XXX: find something better, doesn't work correctly here.
1814 // XXX: try a timed event ?
1815 _PropagateParameterChanges(mixext.ctrl, mixext.type, mixext.id);
1816
1817 return;
1818 case MIXT_ENUM:
1819 if (size < sizeof(int32))
1820 return;
1821 mixval.value = (int)*(int32 *)value;
1822 mixer->SetMixerValue(&mixval);
1823 break;
1824 case MIXT_STEREODB:
1825 case MIXT_STEREOSLIDER16:
1826 case MIXT_STEREOSLIDER:
1827 channelCount = 2;
1828 case MIXT_SLIDER:
1829 case MIXT_MONODB:
1830 case MIXT_MONOSLIDER16:
1831 case MIXT_MONOSLIDER:
1832 if (size < channelCount * sizeof(float))
1833 return;
1834 if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1835 return;
1836 if (mixext.type == MIXT_STEREOSLIDER16 ||
1837 mixext.type == MIXT_MONOSLIDER16)
1838 sliderShift = 16;
1839 mixval.value = 0;
1840
1841 TRACE("-------- sliderShift=%d, v = %08x, v & %08x = %08x\n",
1842 sliderShift, mixval.value, ((1 << sliderShift) - 1),
1843 mixval.value & ((1 << sliderShift) - 1));
1844
1845 mixval.value |= ((int)(((float *)value)[0]))
1846 & ((1 << sliderShift) - 1);
1847 if (channelCount > 1) {
1848 mixval.value |= (((int)(((float *)value)[1]))
1849 & ((1 << sliderShift) - 1)) << sliderShift;
1850 }
1851
1852 TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1853 mixer->SetMixerValue(&mixval);
1854 return;
1855 break;
1856 case MIXT_MESSAGE:
1857 break;
1858 case MIXT_MONOVU:
1859 break;
1860 case MIXT_STEREOVU:
1861 break;
1862 case MIXT_MONOPEAK:
1863 break;
1864 case MIXT_STEREOPEAK:
1865 break;
1866 case MIXT_RADIOGROUP:
1867 break;//??
1868 case MIXT_MARKER:
1869 break;// separator item: ignore
1870 case MIXT_VALUE:
1871 break;
1872 case MIXT_HEXVALUE:
1873 break;
1874 // case MIXT_MONODB:
1875 // break;
1876 // case MIXT_STEREODB:
1877 // break;
1878 case MIXT_3D:
1879 break;
1880 // case MIXT_MONOSLIDER16:
1881 // break;
1882 // case MIXT_STEREOSLIDER16:
1883 // break;
1884 default:
1885 TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1886 __FUNCTION__, mixext.type);
1887 }
1888
1889 return;
1890 }
1891
1892
1893 BParameterWeb*
MakeParameterWeb()1894 OpenSoundNode::MakeParameterWeb()
1895 {
1896 CALLED();
1897 BParameterWeb* web = new BParameterWeb;
1898
1899 // TODO: the card might change the mixer controls at some point,
1900 // we should detect it (poll) and recreate the parameter web and
1901 // re-set it.
1902
1903 // TODO: cache mixext[...] and poll for changes in their update_counter.
1904
1905 OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1906 if (mixer == NULL) {
1907 // some cards don't have a mixer, just put a placeholder then
1908 BParameterGroup* child = web->MakeGroup("No mixer");
1909 child->MakeNullParameter(1, B_MEDIA_UNKNOWN_TYPE, "No Mixer",
1910 B_GENERIC);
1911 return web;
1912 }
1913
1914 int mixext_count = mixer->CountExtInfos();
1915 TRACE("OpenSoundNode::MakeParameterWeb %i ExtInfos\n", mixext_count);
1916
1917 for (int32 i = 0; i < mixext_count; i++) {
1918 oss_mixext mixext;
1919 if (mixer->GetExtInfo(i, &mixext) < B_OK)
1920 continue;
1921
1922 if (mixext.type == MIXT_DEVROOT) {
1923 oss_mixext_root* extroot = (oss_mixext_root*)mixext.data;
1924 TRACE("OpenSoundNode: mixext[%d]: ROOT\n", i);
1925 int32 nb = 0;
1926 const char* childName = mixext.extname;
1927 childName = extroot->id; // extroot->name;
1928 BParameterGroup *child = web->MakeGroup(childName);
1929 _ProcessGroup(child, i, nb);
1930 }
1931 }
1932
1933 return web;
1934 }
1935
1936
1937 // #pragma mark - OpenSoundNode specific
1938
1939
1940 void
_ProcessGroup(BParameterGroup * group,int32 index,int32 & nbParameters)1941 OpenSoundNode::_ProcessGroup(BParameterGroup *group, int32 index,
1942 int32& nbParameters)
1943 {
1944 CALLED();
1945 // TODO: It looks wrong to use the same mixer in a recursive function!
1946 OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1947
1948 int mixext_count = mixer->CountExtInfos();
1949 for (int32 i = 0; i < mixext_count; i++) {
1950 oss_mixext mixext;
1951 if (mixer->GetExtInfo(i, &mixext) < B_OK)
1952 continue;
1953 // only keep direct children of that group
1954 if (mixext.parent != index)
1955 continue;
1956
1957 int32 nb = 1;
1958
1959 TRACE("OpenSoundNode: mixext[%d]: { %s/%s, type=%d, parent=%d, "
1960 "min=%d, max=%d, flags=0x%08x, control_no=%d, desc=%d, "
1961 "update_counter=%d }\n", i,
1962 (mixext.type != MIXT_MARKER) ? mixext.id : "",
1963 (mixext.type != MIXT_MARKER) ? mixext.extname : "",
1964 mixext.type, mixext.parent,
1965 mixext.minvalue, mixext.maxvalue,
1966 mixext.flags, mixext.control_no,
1967 mixext.desc, mixext.update_counter);
1968
1969 // should actually rename the whole group but it's too late there.
1970 const char *childName = mixext.extname;
1971 if (mixext.flags & MIXF_MAINVOL)
1972 childName = "Master Gain";
1973
1974 const char *sliderUnit = "";//"(linear)";
1975 if (mixext.flags & MIXF_HZ)
1976 sliderUnit = "Hz";
1977
1978 const char *continuousKind = B_GAIN;
1979 BParameterGroup* child;
1980
1981 switch (mixext.type) {
1982 case MIXT_DEVROOT:
1983 // root item, should be done already
1984 break;
1985 case MIXT_GROUP:
1986 TRACE("OpenSoundNode: mixext[%d]: GROUP\n", i);
1987 child = group->MakeGroup(childName);
1988 child->MakeNullParameter(i, B_MEDIA_RAW_AUDIO, childName,
1989 B_WEB_BUFFER_OUTPUT);
1990 _ProcessGroup(child, i, nb);
1991 break;
1992 case MIXT_ONOFF:
1993 TRACE("OpenSoundNode: mixext[%d]: ONOFF\n", i);
1994 // multiaudio node adds 100 to IDs !?
1995 if (0/*MMC[i].string == S_MUTE*/) {
1996 group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1997 B_MUTE);
1998 } else {
1999 group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
2000 B_ENABLE);
2001 }
2002 if (nbParameters > 0) {
2003 (group->ParameterAt(nbParameters - 1))->AddOutput(
2004 group->ParameterAt(nbParameters));
2005 nbParameters++;
2006 }
2007 break;
2008 case MIXT_ENUM:
2009 {
2010 TRACE("OpenSoundNode: mixext[%d]: ENUM\n", i);
2011 BDiscreteParameter *parameter =
2012 group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
2013 B_INPUT_MUX);
2014 if (nbParameters > 0) {
2015 (group->ParameterAt(nbParameters - 1))->AddOutput(
2016 group->ParameterAt(nbParameters));
2017 nbParameters++;
2018 }
2019 _ProcessMux(parameter, i);
2020 break;
2021 }
2022 case MIXT_MONODB:
2023 case MIXT_STEREODB:
2024 sliderUnit = "dB";
2025 case MIXT_SLIDER:
2026 case MIXT_MONOSLIDER16:
2027 case MIXT_STEREOSLIDER16:
2028 case MIXT_MONOSLIDER:
2029 //TRACE("OpenSoundNode: mixext[%d]: MONOSLIDER\n", i);
2030 //break;
2031 // fall through
2032 case MIXT_STEREOSLIDER:
2033 TRACE("OpenSoundNode: mixext[%d]: [MONO|STEREO]SLIDER\n", i);
2034
2035 if (mixext.flags & MIXF_MAINVOL)
2036 continuousKind = B_MASTER_GAIN;
2037
2038 // TODO: find out what this was supposed to do:
2039 // if (mixext.flags & MIXF_CENTIBEL)
2040 // true;//step size
2041 // if (mixext.flags & MIXF_DECIBEL)
2042 // true;//step size
2043
2044 group->MakeContinuousParameter(i, B_MEDIA_RAW_AUDIO, childName,
2045 continuousKind, sliderUnit, mixext.minvalue, mixext.maxvalue,
2046 /*TODO: should be "granularity"*/1);
2047
2048 if (mixext.type == MIXT_STEREOSLIDER ||
2049 mixext.type == MIXT_STEREOSLIDER16 ||
2050 mixext.type == MIXT_STEREODB)
2051 group->ParameterAt(nbParameters)->SetChannelCount(2);
2052
2053 TRACE("nb parameters : %d\n", nbParameters);
2054 if (nbParameters > 0) {
2055 (group->ParameterAt(nbParameters - 1))->AddOutput(
2056 group->ParameterAt(nbParameters));
2057 nbParameters++;
2058 }
2059
2060 break;
2061 case MIXT_MESSAGE:
2062 break;
2063 case MIXT_MONOVU:
2064 break;
2065 case MIXT_STEREOVU:
2066 break;
2067 case MIXT_MONOPEAK:
2068 break;
2069 case MIXT_STEREOPEAK:
2070 break;
2071 case MIXT_RADIOGROUP:
2072 break;//??
2073 case MIXT_MARKER:
2074 break;// separator item: ignore
2075 case MIXT_VALUE:
2076 break;
2077 case MIXT_HEXVALUE:
2078 break;
2079 // case MIXT_MONODB:
2080 // TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2081 // "MIXT_MONODB\n");
2082 // break;
2083 // case MIXT_STEREODB:
2084 // TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2085 // "MIXT_STEREODB\n");
2086 // break;
2087 // case MIXT_SLIDER:
2088 // break;
2089 case MIXT_3D:
2090 break;
2091 // case MIXT_MONOSLIDER16:
2092 // break;
2093 // case MIXT_STEREOSLIDER16:
2094 // break;
2095 default:
2096 TRACE("OpenSoundNode::_ProcessGroup: unknown mixer control "
2097 "type %d\n", mixext.type);
2098 }
2099 }
2100
2101 }
2102
2103
2104 void
_ProcessMux(BDiscreteParameter * parameter,int32 index)2105 OpenSoundNode::_ProcessMux(BDiscreteParameter* parameter, int32 index)
2106 {
2107 CALLED();
2108 OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
2109 oss_mixer_enuminfo enuminfo;
2110 status_t err = mixer->GetEnumInfo(index, &enuminfo);
2111 if (err < B_OK) {
2112 // maybe there is no list.
2113 // generate a count form 0
2114 oss_mixext mixext;
2115 if (mixer->GetExtInfo(index, &mixext) < B_OK)
2116 return;
2117
2118 for (int32 i = 0; i < mixext.maxvalue; i++) {
2119 BString s;
2120 s << i;
2121 parameter->AddItem(i, s.String());
2122 }
2123 return;
2124 }
2125
2126 for (int32 i = 0; i < enuminfo.nvalues; i++) {
2127 parameter->AddItem(i, &enuminfo.strings[enuminfo.strindex[i]]);
2128 }
2129 return;
2130 }
2131
2132
2133 status_t
_PropagateParameterChanges(int from,int type,const char * id)2134 OpenSoundNode::_PropagateParameterChanges(int from, int type, const char* id)
2135 {
2136 CALLED();
2137
2138 TRACE("OpenSoundNode::_PropagateParameterChanges(from %i, type %i, "
2139 "id %s)\n", from, type, id);
2140
2141 OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
2142 if (mixer == NULL)
2143 return ENODEV;
2144
2145 // TODO: Cortex doesn't like that!
2146 // try timed event
2147 // try checking update_counter+caching
2148 return B_OK;
2149
2150 // char oldValues[128];
2151 char newValues[128];
2152 // size_t oldValuesSize;
2153 size_t newValuesSize;
2154
2155 for (int i = 0; i < mixer->CountExtInfos(); i++) {
2156 oss_mixext mixext;
2157 status_t err = mixer->GetExtInfo(i, &mixext);
2158 if (err < B_OK)
2159 continue;
2160
2161 // skip the caller
2162 //if (mixext.ctrl == from)
2163 // continue;
2164
2165 if (!(mixext.flags & MIXF_READABLE))
2166 continue;
2167
2168 // match type ?
2169 if (type > -1 && mixext.type != type)
2170 continue;
2171
2172 // match internal ID string
2173 if (id && strncmp(mixext.id, id, 16))
2174 continue;
2175
2176 // BParameter *parameter = NULL;
2177 // for(int32 i=0; i<fWeb->CountParameters(); i++) {
2178 // parameter = fWeb->ParameterAt(i);
2179 // if(parameter->ID() == mixext.ctrl)
2180 // break;
2181 // }
2182 //
2183 // if (!parameter)
2184 // continue;
2185
2186 // oldValuesSize = 128;
2187 newValuesSize = 128;
2188 bigtime_t last;
2189 // TRACE("OpenSoundNode::%s: comparing mixer control %d\n",
2190 // __FUNCTION__, mixext.ctrl);
2191 // if (parameter->GetValue(oldValues, &oldValuesSize, &last) < B_OK)
2192 // continue;
2193 if (GetParameterValue(mixext.ctrl, &last, newValues,
2194 &newValuesSize) < B_OK) {
2195 continue;
2196 }
2197 // if (oldValuesSize != newValuesSize || memcmp(oldValues, newValues,
2198 // MIN(oldValuesSize, newValuesSize))) {
2199 TRACE("OpenSoundNode::%s: updating mixer control %d\n",
2200 __FUNCTION__, mixext.ctrl);
2201 BroadcastNewParameterValue(last, mixext.ctrl, newValues,
2202 newValuesSize);
2203 // BroadcastChangedParameter(mixext.ctrl);
2204 // }
2205 }
2206 return B_OK;
2207 }
2208
2209
2210 int32
_PlayThread(NodeInput * input)2211 OpenSoundNode::_PlayThread(NodeInput* input)
2212 {
2213 CALLED();
2214 //set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2215 signal(SIGUSR1, &_SignalHandler);
2216
2217 OpenSoundDeviceEngine* engine = input->fRealEngine;
2218 if (!engine || !engine->InUse())
2219 return B_NO_INIT;
2220 // skip unconnected or non-busy engines
2221 if (input->fInput.source == media_source::null
2222 && input->fEngineIndex == 0)
2223 return B_NO_INIT;
2224 // must be open for write
2225 ASSERT(engine->OpenMode() & OPEN_WRITE);
2226
2227 // make writing actually block until the previous buffer played
2228 size_t driverBufferSize = engine->DriverBufferSize();
2229 size_t bufferSize = input->fInput.format.u.raw_audio.buffer_size;
2230 if (driverBufferSize != bufferSize) {
2231 printf("warning, OSS driver buffer size: %ld, audio buffer "
2232 "size: %ld", driverBufferSize, bufferSize);
2233 }
2234
2235 // cache a silence buffer
2236 uint8* silenceBuffer = (uint8*)malloc(bufferSize);
2237 if (silenceBuffer == NULL)
2238 return B_NO_MEMORY;
2239 MemoryDeleter deleter(silenceBuffer);
2240 uint8 formatSilence = 0;
2241 if (input->fInput.format.u.raw_audio.format
2242 == media_raw_audio_format::B_AUDIO_UCHAR)
2243 formatSilence = 128;
2244
2245 memset(silenceBuffer, formatSilence, bufferSize);
2246
2247 // start by writing the OSS driver buffer size of silence
2248 // so that the first call to write() already blocks for (almost) the
2249 // buffer duration
2250 input->Write(silenceBuffer, bufferSize);
2251
2252 int64 bytesWritten = 0;
2253 bigtime_t lastRealTime = RealTime();
2254 bigtime_t lastPerformanceTime = 0;
2255
2256 const int32 driftValueCount = 64;
2257 int32 currentDriftValueIndex = 0;
2258 float driftValues[driftValueCount];
2259 for (int32 i = 0; i < driftValueCount; i++)
2260 driftValues[i] = 1.0;
2261
2262 do {
2263 if (!fDevice->Locker()->Lock())
2264 break;
2265
2266 TRACE("OpenSoundNode::_PlayThread: buffers: %ld\n",
2267 input->fBuffers.CountItems());
2268
2269 BBuffer* buffer = (BBuffer*)input->fBuffers.RemoveItem((int32)0);
2270
2271 fDevice->Locker()->Unlock();
2272
2273 if (input->fThread < 0) {
2274 if (buffer)
2275 buffer->Recycle();
2276 break;
2277 }
2278
2279 //input->WriteTestTone();
2280 //if (buffer)
2281 // buffer->Recycle();
2282 //continue;
2283
2284 int32 additionalBytesWritten = 0;
2285 if (buffer != NULL) {
2286 if (input->Write(buffer->Data(), buffer->SizeUsed()) == B_OK)
2287 additionalBytesWritten = buffer->SizeUsed();
2288 buffer->Recycle();
2289 } else {
2290 input->Write(silenceBuffer, bufferSize);
2291 additionalBytesWritten = bufferSize;
2292 }
2293
2294 // TODO: do not assume channel 0 will always be running!
2295 // update the timesource
2296 if (input->fEngineIndex == 0 && input->fThread >= 0) {
2297
2298 bigtime_t realTime = RealTime();
2299 bigtime_t realPlaybackDuration = realTime - lastRealTime;
2300 bigtime_t performanceTime
2301 = time_for_buffer(bytesWritten, input->fInput.format);
2302 float drift = (double)(performanceTime
2303 - lastPerformanceTime) / realPlaybackDuration;
2304
2305 lastPerformanceTime = performanceTime;
2306 lastRealTime = realTime;
2307
2308 driftValues[currentDriftValueIndex++] = drift;
2309 if (currentDriftValueIndex == driftValueCount)
2310 currentDriftValueIndex = 0;
2311 drift = 0.0;
2312 for (int32 i = 0; i < driftValueCount; i++)
2313 drift += driftValues[i];
2314 drift /= driftValueCount;
2315
2316 if (fDevice->Locker()->Lock()) {
2317 if (input->fThread >= 0)
2318 _UpdateTimeSource(performanceTime, realTime, drift);
2319 fDevice->Locker()->Unlock();
2320 }
2321 }
2322 bytesWritten += additionalBytesWritten;
2323
2324 } while (input->fThread > -1);
2325
2326 return 0;
2327 }
2328
2329
2330 int32
_RecThread(NodeOutput * output)2331 OpenSoundNode::_RecThread(NodeOutput* output)
2332 {
2333 CALLED();
2334
2335 //set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2336 signal(SIGUSR1, &_SignalHandler);
2337
2338 OpenSoundDeviceEngine *engine = output->fRealEngine;
2339 if (!engine || !engine->InUse())
2340 return B_NO_INIT;
2341 // make sure we're both started *and* connected before delivering a buffer
2342 if ((RunState() != BMediaEventLooper::B_STARTED)
2343 || (output->fOutput.destination == media_destination::null)) {
2344 return B_NO_INIT;
2345 }
2346
2347 // must be open for read
2348 ASSERT(engine->OpenMode() & OPEN_READ);
2349
2350 #ifdef ENABLE_REC
2351
2352 fDevice->Locker()->Lock();
2353 do {
2354 audio_buf_info abinfo;
2355 // size_t avail = engine->GetISpace(&abinfo);
2356 // TRACE("OpenSoundNode::_RunThread: I avail: %d\n", avail);
2357 //
2358 // // skip if less than 1 buffer
2359 // if (avail < output->fOutput.format.u.raw_audio.buffer_size)
2360 // continue;
2361
2362 fDevice->Locker()->Unlock();
2363 // Get the next buffer of data
2364 BBuffer* buffer = _FillNextBuffer(&abinfo, *output);
2365 fDevice->Locker()->Lock();
2366
2367 if (buffer) {
2368 // send the buffer downstream if and only if output is enabled
2369 status_t err = B_ERROR;
2370 if (output->fOutputEnabled) {
2371 err = SendBuffer(buffer, output->fOutput.source,
2372 output->fOutput.destination);
2373 }
2374 // TRACE("OpenSoundNode::_RunThread: I avail: %d, OE %d, %s\n",
2375 // avail, output->fOutputEnabled, strerror(err));
2376 if (err != B_OK) {
2377 buffer->Recycle();
2378 } else {
2379 // track how much media we've delivered so far
2380 size_t nSamples = buffer->SizeUsed()
2381 / (output->fOutput.format.u.raw_audio.format
2382 & media_raw_audio_format::B_AUDIO_SIZE_MASK);
2383 output->fSamplesSent += nSamples;
2384 // TRACE("OpenSoundNode::%s: sent %d samples\n",
2385 // __FUNCTION__, nSamples);
2386 }
2387
2388 }
2389 } while (output->fThread > -1);
2390 fDevice->Locker()->Unlock();
2391
2392 #endif
2393 return 0;
2394 }
2395
2396
2397 status_t
_StartPlayThread(NodeInput * input)2398 OpenSoundNode::_StartPlayThread(NodeInput* input)
2399 {
2400 CALLED();
2401 BAutolock L(fDevice->Locker());
2402 // the thread is already started ?
2403 if (input->fThread > B_OK)
2404 return B_OK;
2405
2406 //allocate buffer free semaphore
2407 // int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2408
2409 // fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2410 // "multi_audio out buffer free");
2411 // fBufferAvailableSem = create_sem(bufferCount - 1,
2412 // "OpenSound out buffer free");
2413
2414 // if (fBufferAvailableSem < B_OK)
2415 // return B_ERROR;
2416
2417 input->fThread = spawn_thread(_PlayThreadEntry,
2418 "OpenSound audio output", B_REAL_TIME_PRIORITY, input);
2419
2420 if (input->fThread < B_OK) {
2421 // delete_sem(fBufferAvailableSem);
2422 return B_ERROR;
2423 }
2424
2425 resume_thread(input->fThread);
2426 return B_OK;
2427 }
2428
2429
2430 status_t
_StopPlayThread(NodeInput * input)2431 OpenSoundNode::_StopPlayThread(NodeInput* input)
2432 {
2433 if (input->fThread < 0)
2434 return B_OK;
2435
2436 CALLED();
2437
2438 thread_id th;
2439 {
2440 BAutolock L(fDevice->Locker());
2441 th = input->fThread;
2442 input->fThread = -1;
2443 //kill(th, SIGUSR1);
2444 }
2445 status_t ret;
2446 wait_for_thread(th, &ret);
2447
2448 return B_OK;
2449 }
2450
2451
2452 status_t
_StartRecThread(NodeOutput * output)2453 OpenSoundNode::_StartRecThread(NodeOutput* output)
2454 {
2455 CALLED();
2456 // the thread is already started ?
2457 if (output->fThread > B_OK)
2458 return B_OK;
2459
2460 //allocate buffer free semaphore
2461 // int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2462
2463 // fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2464 // "multi_audio out buffer free");
2465 // fBufferAvailableSem = create_sem(bufferCount - 1,
2466 // "OpenSound out buffer free");
2467
2468 // if (fBufferAvailableSem < B_OK)
2469 // return B_ERROR;
2470
2471 output->fThread = spawn_thread(_RecThreadEntry, "OpenSound audio input",
2472 B_REAL_TIME_PRIORITY, output);
2473
2474 if (output->fThread < B_OK) {
2475 //delete_sem(fBufferAvailableSem);
2476 return B_ERROR;
2477 }
2478
2479 resume_thread(output->fThread);
2480 return B_OK;
2481 }
2482
2483
2484 status_t
_StopRecThread(NodeOutput * output)2485 OpenSoundNode::_StopRecThread(NodeOutput* output)
2486 {
2487 if (output->fThread < 0)
2488 return B_OK;
2489
2490 CALLED();
2491
2492 thread_id th = output->fThread;
2493 output->fThread = -1;
2494 {
2495 BAutolock L(fDevice->Locker());
2496 //kill(th, SIGUSR1);
2497 }
2498 status_t ret;
2499 wait_for_thread(th, &ret);
2500
2501 return B_OK;
2502 }
2503
2504
2505 void
_UpdateTimeSource(bigtime_t performanceTime,bigtime_t realTime,float drift)2506 OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime,
2507 bigtime_t realTime, float drift)
2508 {
2509 // CALLED();
2510
2511 if (!fTimeSourceStarted)
2512 return;
2513
2514 PublishTime(performanceTime, realTime, drift);
2515
2516 // TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli, "
2517 // "drift : %f\n", perfTime, realTime, drift);
2518 }
2519
2520
2521 BBuffer*
_FillNextBuffer(audio_buf_info * abinfo,NodeOutput & channel)2522 OpenSoundNode::_FillNextBuffer(audio_buf_info* abinfo, NodeOutput& channel)
2523 {
2524 CALLED();
2525
2526 BBuffer* buffer = channel.FillNextBuffer(BufferDuration());
2527 if (!buffer)
2528 return NULL;
2529
2530 if (fDevice == NULL)
2531 fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - fDevice NULL\n");
2532 if (buffer->Header() == NULL) {
2533 fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2534 "buffer->Header() NULL\n");
2535 }
2536 if (TimeSource() == NULL) {
2537 fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2538 "TimeSource() NULL\n");
2539 }
2540
2541 // fill in the buffer header
2542 media_header* hdr = buffer->Header();
2543 if (hdr != NULL) {
2544 hdr->time_source = TimeSource()->ID();
2545 // TODO: should be system_time() - latency_as_per(abinfo)
2546 hdr->start_time = PerformanceTimeFor(system_time());
2547 }
2548
2549 return buffer;
2550 }
2551
2552
2553 status_t
GetConfigurationFor(BMessage * into_message)2554 OpenSoundNode::GetConfigurationFor(BMessage* into_message)
2555 {
2556 CALLED();
2557
2558 if (!into_message)
2559 return B_BAD_VALUE;
2560
2561 size_t size = 128;
2562 void* buffer = malloc(size);
2563
2564 for (int32 i = 0; i < fWeb->CountParameters(); i++) {
2565 BParameter* parameter = fWeb->ParameterAt(i);
2566 if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
2567 && parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
2568 continue;
2569
2570 TRACE("getting parameter %i\n", parameter->ID());
2571 size = 128;
2572 bigtime_t last_change;
2573 status_t err;
2574 while ((err = GetParameterValue(parameter->ID(), &last_change, buffer,
2575 &size)) == B_NO_MEMORY) {
2576 size += 128;
2577 free(buffer);
2578 buffer = malloc(size);
2579 }
2580
2581 if (err == B_OK && size > 0) {
2582 into_message->AddInt32("parameterID", parameter->ID());
2583 into_message->AddData("parameterData", B_RAW_TYPE, buffer, size,
2584 false);
2585 } else {
2586 TRACE("parameter err : %s\n", strerror(err));
2587 }
2588 }
2589
2590 free(buffer);
2591
2592 PRINT_OBJECT(*into_message);
2593
2594 return B_OK;
2595 }
2596
2597
2598 OpenSoundNode::NodeOutput*
_FindOutput(const media_source & source) const2599 OpenSoundNode::_FindOutput(const media_source& source) const
2600 {
2601 int32 count = fOutputs.CountItems();
2602 for (int32 i = 0; i < count; i++) {
2603 NodeOutput* channel = (NodeOutput*)fOutputs.ItemAtFast(i);
2604 if (source == channel->fOutput.source)
2605 return channel;
2606 }
2607 return NULL;
2608 }
2609
2610
2611 OpenSoundNode::NodeInput*
_FindInput(const media_destination & dest) const2612 OpenSoundNode::_FindInput(const media_destination& dest) const
2613 {
2614 int32 count = fInputs.CountItems();
2615 for (int32 i = 0; i < count; i++) {
2616 NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2617 if (dest == channel->fInput.destination)
2618 return channel;
2619 }
2620 return NULL;
2621 }
2622
2623
2624 OpenSoundNode::NodeInput*
_FindInput(int32 destinationId)2625 OpenSoundNode::_FindInput(int32 destinationId)
2626 {
2627 int32 count = fInputs.CountItems();
2628 for (int32 i = 0; i < count; i++) {
2629 NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2630 if (destinationId == channel->fInput.destination.id)
2631 return channel;
2632 }
2633 return NULL;
2634 }
2635
2636
2637 // pragma mark - static
2638
2639
2640 void
_SignalHandler(int sig)2641 OpenSoundNode::_SignalHandler(int sig)
2642 {
2643 // TODO: what was this intended for, just stopping the threads?
2644 // (see _StopThreadXXX(), there is a kill call commented out there)
2645 }
2646
2647
2648 int32
_PlayThreadEntry(void * data)2649 OpenSoundNode::_PlayThreadEntry(void* data)
2650 {
2651 CALLED();
2652 NodeInput* channel = static_cast<NodeInput*>(data);
2653 return channel->fNode->_PlayThread(channel);
2654 }
2655
2656
2657 int32
_RecThreadEntry(void * data)2658 OpenSoundNode::_RecThreadEntry(void* data)
2659 {
2660 CALLED();
2661 NodeOutput* channel = static_cast<NodeOutput*>(data);
2662 return channel->fNode->_RecThread(channel);
2663 }
2664
2665
2666 void
GetFlavor(flavor_info * outInfo,int32 id)2667 OpenSoundNode::GetFlavor(flavor_info* outInfo, int32 id)
2668 {
2669 CALLED();
2670 if (outInfo == NULL)
2671 return;
2672
2673 outInfo->flavor_flags = 0;
2674 outInfo->possible_count = 1;
2675 // one flavor at a time
2676 outInfo->in_format_count = 0;
2677 // no inputs
2678 outInfo->in_formats = 0;
2679 outInfo->out_format_count = 0;
2680 // no outputs
2681 outInfo->out_formats = 0;
2682 outInfo->internal_id = id;
2683
2684 outInfo->name = "OpenSoundNode Node";
2685 outInfo->info = "The OpenSoundNode outputs to OpenSound System v4 drivers.";
2686 outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE
2687 | B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE;
2688 // TODO: If the OSS engine supports outputing encoded audio,
2689 // we would need to setup a B_MEDIA_ENCODED_AUDIO format here
2690 outInfo->in_format_count = 1;
2691 // 1 input
2692 media_format * informats = new media_format[outInfo->in_format_count];
2693 GetFormat(&informats[0]);
2694 outInfo->in_formats = informats;
2695
2696 outInfo->out_format_count = 1;
2697 // 1 output
2698 media_format * outformats = new media_format[outInfo->out_format_count];
2699 GetFormat(&outformats[0]);
2700 outInfo->out_formats = outformats;
2701 }
2702
2703
2704 void
GetFormat(media_format * outFormat)2705 OpenSoundNode::GetFormat(media_format* outFormat)
2706 {
2707 CALLED();
2708 if (outFormat == NULL)
2709 return;
2710
2711 outFormat->type = B_MEDIA_RAW_AUDIO;
2712 outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2713 outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2714 outFormat->u.raw_audio = media_raw_audio_format::wildcard;
2715 }
2716