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