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.
10 #include "OpenSoundNode.h"
14 #include <BufferGroup.h>
16 #include <MediaAddOn.h>
17 #include <MediaRoster.h>
18 #include <scheduler.h>
32 #include "OpenSoundDevice.h"
33 #include "OpenSoundDeviceEngine.h"
34 #include "OpenSoundDeviceMixer.h"
35 #include "SupportFunctions.h"
40 class FunctionTracer
{
42 FunctionTracer(const char* functionName
)
43 : fFunctionName(functionName
)
45 printf("OpenSoundNode::%s()\n", fFunctionName
.String());
49 printf("OpenSoundNode::%s() - leave\n", fFunctionName
.String());
51 BString fFunctionName
;
62 //#define TRACE_OSS_NODE
64 # define TRACE(x...) printf(x)
65 # define CALLED(x...) FunctionTracer _ft(__FUNCTION__)
73 class OpenSoundNode::NodeInput
{
75 NodeInput(const media_input
& input
, int engineIndex
, int ossFormatFlags
,
78 fEngineIndex(engineIndex
),
80 fOSSFormatFlags(ossFormatFlags
),
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"
94 fInput
.format
.u
.raw_audio
.format
95 = media_raw_audio_format::wildcard
.format
;
102 fNode
->_StopPlayThread(this);
107 status_t
Write(void* data
, size_t size
)
111 ssize_t written
= fRealEngine
->Write(data
, size
);
114 return (status_t
)written
;
115 if (written
< (ssize_t
)size
)
121 void WriteTestTone(size_t bytes
)
123 // phase of the sine wave
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];
151 ssize_t written
= fRealEngine
->Write(buffer
, bytes
);
152 if (written
!= (ssize_t
)bytes
) {
157 void RecycleAllBuffers()
161 // make sure all buffers are recycled, or we might hang
163 while (BBuffer
* buffer
= (BBuffer
*)fBuffers
.RemoveItem((int32
)0))
167 OpenSoundNode
* fNode
;
169 OpenSoundDeviceEngine
* fRealEngine
;
170 // engine it's connected to. can be a shadow one (!= fEngineIndex)
172 // AFMT_* flags for this input
174 media_format fPreferredFormat
;
178 // contains BBuffer* pointers that have not yet played
180 uint32 fTestTonePhase
;
184 class OpenSoundNode::NodeOutput
{
186 NodeOutput(const media_output
& output
, const media_format
& format
)
193 fPreferredFormat(format
),
197 fUsingOwnBufferGroup(true),
198 fOutputEnabled(true),
209 fNode
->_StopRecThread(this);
214 status_t
AllocateBuffers(bigtime_t bufferDuration
, bigtime_t latency
)
216 TRACE("NodeOutput::AllocateBuffers(bufferDuration = %lld, "
217 "latency = %lld)\n", bufferDuration
, latency
);
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();
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)
260 BBuffer
* FillNextBuffer(bigtime_t bufferDuration
)
262 if (fBufferGroup
== 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
274 // now fill it with data
275 ssize_t sizeUsed
= fRealEngine
->Read(buffer
->Data(),
276 fOutput
.format
.u
.raw_audio
.buffer_size
);
278 TRACE("NodeOutput::%s: %s\n", __FUNCTION__
,
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();
290 hdr
->type
= B_MEDIA_RAW_AUDIO
;
291 hdr
->size_used
= sizeUsed
;
297 OpenSoundNode
* fNode
;
299 OpenSoundDeviceEngine
* fRealEngine
;
300 // engine it's connected to. can be a shadow one (!= fEngineIndex)
302 // AFMT_* flags for this output
303 media_output fOutput
;
304 media_format fPreferredFormat
;
307 BBufferGroup
* fBufferGroup
;
308 bool fUsingOwnBufferGroup
;
314 // #pragma mark - OpenSoundNode
317 OpenSoundNode::OpenSoundNode(BMediaAddOn
* addon
, const char* name
,
318 OpenSoundDevice
* device
, int32 internal_id
, BMessage
* config
)
320 BBufferConsumer(B_MEDIA_RAW_AUDIO
),
321 BBufferProducer(B_MEDIA_RAW_AUDIO
),
325 fInitCheckStatus(B_NO_INIT
),
328 fTimeSourceStarted(false),
329 fTimeSourceStartTime(0),
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.
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);
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
;
374 if (config
!= NULL
) {
376 PRINT_OBJECT(fConfig
);
379 fInitCheckStatus
= B_OK
;
383 OpenSoundNode::~OpenSoundNode()
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();
403 OpenSoundNode::InitCheck() const
406 return fInitCheckStatus
;
410 // #pragma mark - BMediaNode
414 OpenSoundNode::AddOn(int32
* internal_id
) const
417 // BeBook says this only gets called if we were in an add-on.
419 // If we get a null pointer then we just won't write.
420 if (internal_id
!= 0) {
429 OpenSoundNode::Preroll()
432 // XXX:Performance opportunity
433 BMediaNode::Preroll();
438 OpenSoundNode::HandleMessage(int32 message
, const void* data
, size_t size
)
446 OpenSoundNode::NodeRegistered()
450 if (fInitCheckStatus
!= B_OK
) {
451 ReportError(B_NODE_IN_DISTRESS
);
455 TRACE("NodeRegistered: %d engines\n", fDevice
->CountEngines());
456 for (int32 i
= 0; i
< fDevice
->CountEngines(); i
++) {
457 OpenSoundDeviceEngine
* engine
= fDevice
->EngineAt(i
);
460 // skip shadow engines
461 if (engine
->Caps() & PCM_CAP_SHADOW
)
463 // skip engines that don't have outputs
464 if ((engine
->Caps() & PCM_CAP_OUTPUT
) == 0)
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
;
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
);
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"))
491 sprintf(mediaInput
.name
, "%sOutput %" B_PRId32
" (%s)", prefix
,
492 mediaInput
.destination
.id
, gSupportedFormatsNames
[f
]);
494 NodeInput
* input
= new (nothrow
) NodeInput(mediaInput
, i
, fmt
,
496 if (input
== NULL
|| !fInputs
.AddItem(input
)) {
503 for (int32 i
= 0; i
< fDevice
->CountEngines(); i
++) {
504 OpenSoundDeviceEngine
* engine
= fDevice
->EngineAt(i
);
507 // skip shadow engines
508 if (engine
->Caps() & PCM_CAP_SHADOW
)
510 // skip engines that don't have inputs
511 if ((engine
->Caps() & PCM_CAP_INPUT
) == 0)
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
;
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
);
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"))
539 sprintf(mediaOutput
.name
, "%sInput %" B_PRId32
" (%s)", prefix
,
540 mediaOutput
.source
.id
, gSupportedFormatsNames
[f
]);
542 NodeOutput
* output
= new (nothrow
) NodeOutput(mediaOutput
,
544 if (output
== NULL
|| !fOutputs
.AddItem(output
)) {
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();
567 int32 parameterID
= 0;
568 while (fConfig
.FindInt32("parameterID", index
, ¶meterID
) == B_OK
) {
571 if (fConfig
.FindData("parameterData", B_RAW_TYPE
, index
, &data
,
573 SetParameterValue(parameterID
, TimeSource()->Now(), data
, size
);
578 TRACE("apply configuration in : %lldµs\n", system_time() - start
);
580 SetPriority(B_REAL_TIME_PRIORITY
);
586 OpenSoundNode::RequestCompleted(const media_request_info
& info
)
594 OpenSoundNode::SetTimeSource(BTimeSource
* timeSource
)
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.
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.
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");
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);
647 status_t err
= engine
->AcceptFormatFor(channel
->fOSSFormatFlags
, *format
,
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;
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;
700 OpenSoundNode::GetNextInput(int32
* cookie
, media_input
* out_input
)
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");
711 if (*cookie
>= fInputs
.CountItems() || *cookie
< 0)
714 NodeInput
* channel
= (NodeInput
*)fInputs
.ItemAt(*cookie
);
715 *out_input
= channel
->fInput
;
718 TRACE("input.format : %u\n", channel
->fInput
.format
.u
.raw_audio
.format
);
725 OpenSoundNode::DisposeInputCookie(int32 cookie
)
728 // nothing to do since our cookies are just integers
733 OpenSoundNode::BufferReceived(BBuffer
* buffer
)
737 switch (buffer
->Header()->type
) {
738 // case B_MEDIA_PARAMETERS:
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));
747 // buffer->Recycle();
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
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",
770 fprintf(stderr
, "OpenSoundNode::BufferReceived() - unexpected "
779 OpenSoundNode::ProducerDataStatus(const media_destination
& for_whom
,
780 int32 status
, bigtime_t at_performance_time
)
784 NodeInput
* channel
= _FindInput(for_whom
);
786 if (channel
== NULL
) {
787 fprintf(stderr
,"OpenSoundNode::ProducerDataStatus() - "
788 "invalid destination\n");
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
);
803 OpenSoundNode::GetLatencyFor(const media_destination
& for_whom
,
804 bigtime_t
* out_latency
, media_node_id
* out_timesource
)
808 if (out_latency
== NULL
|| out_timesource
== NULL
) {
809 fprintf(stderr
,"OpenSoundNode::GetLatencyFor() - B_BAD_VALUE\n");
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();
838 OpenSoundNode::Connected(const media_source
& producer
,
839 const media_destination
& where
, const media_format
& with_format
,
840 media_input
* out_input
)
844 if (out_input
== NULL
) {
845 fprintf(stderr
,"OpenSoundNode::Connected() - B_BAD_VALUE\n");
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
);
883 OpenSoundNode::Disconnected(const media_source
& producer
,
884 const media_destination
& where
)
888 NodeInput
* channel
= _FindInput(where
);
889 if (channel
== NULL
) {
890 fprintf(stderr
,"OpenSoundNode::Disconnected() - "
891 "B_MEDIA_BAD_DESTINATION\n");
894 if (channel
->fInput
.source
!= producer
) {
895 fprintf(stderr
,"OpenSoundNode::Disconnected() - "
896 "B_MEDIA_BAD_SOURCE\n");
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
916 OpenSoundNode::FormatChanged(const media_source
& producer
,
917 const media_destination
& consumer
, int32 change_tag
,
918 const media_format
& format
)
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?
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.
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
)
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
963 OpenSoundNode::FormatSuggestionRequested(media_type type
, int32
/*quality*/,
964 media_format
* format
)
968 if (format
== NULL
) {
969 fprintf(stderr
, "\tERROR - NULL format pointer passed in!\n");
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
;
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.
993 OpenSoundNode::FormatProposal(const media_source
& output
, media_format
* format
)
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
;
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);
1017 *format
= fPreferredFormat
;
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
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
1037 OpenSoundNode::FormatChangeRequested(const media_source
& source
,
1038 const media_destination
& destination
, media_format
* io_format
,
1039 int32
* _deprecated_
)
1043 // we don't support any other formats, so we just reject any format
1044 // changes. TODO: implement?
1050 OpenSoundNode::GetNextOutput(int32
* cookie
, media_output
* out_output
)
1054 if (*cookie
>= fOutputs
.CountItems() || *cookie
< 0)
1057 NodeOutput
*channel
= (NodeOutput
*)fOutputs
.ItemAt(*cookie
);
1058 *out_output
= channel
->fOutput
;
1065 OpenSoundNode::DisposeOutputCookie(int32 cookie
)
1068 // do nothing because we don't use the cookie for anything special
1074 OpenSoundNode::SetBufferGroup(const media_source
& for_source
,
1075 BBufferGroup
* newGroup
)
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
)
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
);
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
1118 OpenSoundNode::PrepareToConnect(const media_source
& what
,
1119 const media_destination
& where
, media_format
* format
,
1120 media_source
* out_source
, char* out_name
)
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);
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);
1159 channel
->fRealEngine
= engine
;
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
);
1170 fprintf(stderr
, "\tconsumer suggested buffer_size %lu\n",
1171 format
->u
.raw_audio
.buffer_size
);
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
);
1185 OpenSoundNode::Connect(status_t error
, const media_source
& source
,
1186 const media_destination
& destination
, const media_format
& format
,
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");
1200 OpenSoundDeviceEngine
*engine
= channel
->fRealEngine
;
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.
1208 channel
->fOutput
.destination
= media_destination::null
;
1209 channel
->fOutput
.format
= channel
->fPreferredFormat
;
1211 channel
->fRealEngine
= NULL
;
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
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.
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",
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
);
1257 OpenSoundNode::Disconnect(const media_source
& what
,
1258 const media_destination
& where
)
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");
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
) {
1282 channel
->fRealEngine
= NULL
;
1283 channel
->fOutput
.destination
= media_destination::null
;
1284 channel
->fOutput
.format
= channel
->fPreferredFormat
;
1285 channel
->FreeBuffers();
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
);
1296 OpenSoundNode::LateNoticeReceived(const media_source
& what
, bigtime_t how_much
,
1297 bigtime_t performance_time
)
1301 // is this our output?
1302 NodeOutput
*channel
= _FindOutput(what
);
1303 if (channel
== NULL
)
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
);
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
1329 // size_t nSamples = fOutput.format.u.raw_audio.buffer_size
1331 // mSamplesSent += nSamples;
1333 fprintf(stderr
, "\tskipping a buffer to try to catch up\n");
1339 OpenSoundNode::EnableOutput(const media_source
& what
, bool enabled
,
1340 int32
* _deprecated_
)
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
;
1357 OpenSoundNode::AdditionalBufferRequested(const media_source
& source
,
1358 media_buffer_id prev_buffer
, bigtime_t prev_time
,
1359 const media_seek_tag
* prev_tag
)
1362 // we don't support offline mode
1367 // #pragma mark - BMediaEventLooper
1371 OpenSoundNode::HandleEvent(const media_timed_event
* event
, bigtime_t lateness
,
1376 switch (event
->type
) {
1377 case BTimedEventQueue::B_START
:
1378 HandleStart(event
,lateness
,realTimeEvent
);
1380 case BTimedEventQueue::B_SEEK
:
1381 HandleSeek(event
,lateness
,realTimeEvent
);
1383 case BTimedEventQueue::B_WARP
:
1384 HandleWarp(event
,lateness
,realTimeEvent
);
1386 case BTimedEventQueue::B_STOP
:
1387 HandleStop(event
,lateness
,realTimeEvent
);
1389 case BTimedEventQueue::B_HANDLE_BUFFER
:
1390 // TRACE("HandleEvent: B_HANDLE_BUFFER, RunState= %d\n",
1392 if (RunState() == BMediaEventLooper::B_STARTED
) {
1393 HandleBuffer(event
,lateness
,realTimeEvent
);
1396 case BTimedEventQueue::B_DATA_STATUS
:
1397 HandleDataStatus(event
,lateness
,realTimeEvent
);
1399 case BTimedEventQueue::B_PARAMETER
:
1400 HandleParameter(event
,lateness
,realTimeEvent
);
1403 fprintf(stderr
," unknown event type: %" B_PRId32
"\n",
1412 OpenSoundNode::HandleBuffer(const media_timed_event
* event
,
1413 bigtime_t lateness
, bool realTimeEvent
)
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");
1426 NodeInput
*channel
= _FindInput(buffer
->Header()->destination
);
1427 // TRACE("buffer->Header()->destination : %i\n",
1428 // buffer->Header()->destination);
1430 if (channel
== NULL
) {
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
1455 NotifyLateProducer(channel
->fInput
.source
, -how_early
, perf_time
);
1456 fprintf(stderr
," <- LATE BUFFER : %" B_PRIdBIGTIME
"\n", how_early
);
1459 fDevice
->Locker()->Lock();
1460 if (channel
->fBuffers
.CountItems() > 10) {
1461 fDevice
->Locker()->Unlock();
1462 TRACE("OpenSoundNode::HandleBuffer too many buffers, "
1466 // TRACE("OpenSoundNode::HandleBuffer writing channelId : %i,
1467 // how_early:%lli\n", channel->fEngineIndex, how_early);
1468 if (!channel
->fBuffers
.AddItem(buffer
))
1470 fDevice
->Locker()->Unlock();
1478 OpenSoundNode::HandleDataStatus(const media_timed_event
* event
,
1479 bigtime_t lateness
, bool realTimeEvent
)
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";
1499 case B_DATA_AVAILABLE
:
1502 case B_PRODUCER_STOPPED
:
1503 message
<< "Stopped";
1510 message
<< ", lateness: " << lateness
;
1511 printf("%s\n", message
.String());
1518 OpenSoundNode::HandleStart(const media_timed_event
* event
, bigtime_t lateness
,
1522 if (RunState() != B_STARTED
) {
1523 // TODO: What should happen here?
1530 OpenSoundNode::HandleSeek(const media_timed_event
* event
, bigtime_t lateness
,
1534 TRACE("OpenSoundNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n",
1535 event
->event_time
,event
->data
,event
->bigdata
);
1541 OpenSoundNode::HandleWarp(const media_timed_event
* event
,
1542 bigtime_t lateness
, bool realTimeEvent
)
1550 OpenSoundNode::HandleStop(const media_timed_event
* event
, bigtime_t lateness
,
1554 // flush the queue so downstreamers don't get any more
1555 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS
, true,
1556 BTimedEventQueue::B_HANDLE_BUFFER
);
1563 OpenSoundNode::HandleParameter(const media_timed_event
* event
,
1564 bigtime_t lateness
, bool realTimeEvent
)
1571 // #pragma mark - BTimeSource
1575 OpenSoundNode::SetRunMode(run_mode mode
)
1578 TRACE("OpenSoundNode::SetRunMode(%d)\n", mode
);
1579 //BTimeSource::SetRunMode(mode);
1584 OpenSoundNode::TimeSourceOp(const time_source_op_info
& op
, void* _reserved
)
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
);
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);
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);
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
);
1629 // #pragma mark - BControllable
1633 OpenSoundNode::GetParameterValue(int32 id
, bigtime_t
* last_change
, void* value
,
1638 int channelCount
= 1;
1639 int sliderShift
= 8;
1641 OpenSoundDeviceMixer
* mixer
= fDevice
->MixerAt(0);
1645 TRACE("id : %i, *ioSize=%d\n", id
, *ioSize
);
1648 status_t err
= mixer
->GetExtInfo(id
, &mixext
);
1652 oss_mixer_value mixval
;
1653 mixval
.ctrl
= mixext
.ctrl
;
1654 mixval
.timestamp
= mixext
.timestamp
;
1656 err
= mixer
->GetMixerValue(&mixval
);
1660 if (!(mixext
.flags
& MIXF_READABLE
))
1663 BParameter
*parameter
= NULL
;
1664 for (int32 i
= 0; i
< fWeb
->CountParameters(); i
++) {
1665 parameter
= fWeb
->ParameterAt(i
);
1666 if(parameter
->ID() == id
)
1673 TRACE("%s: value = 0x%08x\n", __FUNCTION__
, mixval
.value
);
1675 *last_change
= system_time();//??
1677 switch (mixext
.type
) {
1682 if (*ioSize
< sizeof(bool))
1684 *(int32
*)value
= mixval
.value
?true:false;
1685 *ioSize
= sizeof(bool);
1688 if (*ioSize
< sizeof(int32
))
1690 *(int32
*)value
= mixval
.value
;
1691 *ioSize
= sizeof(int32
);
1695 case MIXT_STEREOSLIDER16
:
1696 case MIXT_STEREOSLIDER
:
1700 case MIXT_MONOSLIDER16
:
1701 case MIXT_MONOSLIDER
:
1702 if (*ioSize
< channelCount
* sizeof(float))
1704 if (parameter
->Type() != BParameter::B_CONTINUOUS_PARAMETER
)
1706 if (mixext
.type
== MIXT_STEREOSLIDER16
||
1707 mixext
.type
== MIXT_MONOSLIDER16
)
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)
1714 ((float *)value
)[1] = (float)((mixval
.value
>> sliderShift
)
1715 & ((1 << sliderShift
) - 1));
1716 TRACE("%s: value[1] = %f\n", __FUNCTION__
, ((float *)value
)[1]);
1727 case MIXT_STEREOPEAK
:
1729 case MIXT_RADIOGROUP
:
1732 break;// separator item: ignore
1737 /* case MIXT_MONODB:
1743 /* case MIXT_MONOSLIDER16:
1745 case MIXT_STEREOSLIDER16:
1748 TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1749 __FUNCTION__
, mixext
.type
);
1757 OpenSoundNode::SetParameterValue(int32 id
, bigtime_t performance_time
,
1758 const void* value
, size_t size
)
1762 TRACE("id : %i, performance_time : %lld, size : %i\n", id
,
1763 performance_time
, size
);
1765 OpenSoundDeviceMixer
*mixer
= fDevice
->MixerAt(0);
1770 if (mixer
->GetExtInfo(id
, &mixext
) < B_OK
)
1772 if (!(mixext
.flags
& MIXF_WRITEABLE
))
1775 oss_mixer_value mixval
;
1776 mixval
.ctrl
= mixext
.ctrl
;
1777 mixval
.timestamp
= mixext
.timestamp
;
1779 status_t err
= mixer
->GetMixerValue(&mixval
);
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
)
1796 int channelCount
= 1;
1797 int sliderShift
= 8;
1799 switch (mixext
.type
) {
1804 if (size
< sizeof(bool))
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
);
1818 if (size
< sizeof(int32
))
1820 mixval
.value
= (int)*(int32
*)value
;
1821 mixer
->SetMixerValue(&mixval
);
1824 case MIXT_STEREOSLIDER16
:
1825 case MIXT_STEREOSLIDER
:
1829 case MIXT_MONOSLIDER16
:
1830 case MIXT_MONOSLIDER
:
1831 if (size
< channelCount
* sizeof(float))
1833 if (parameter
->Type() != BParameter::B_CONTINUOUS_PARAMETER
)
1835 if (mixext
.type
== MIXT_STEREOSLIDER16
||
1836 mixext
.type
== MIXT_MONOSLIDER16
)
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
);
1863 case MIXT_STEREOPEAK
:
1865 case MIXT_RADIOGROUP
:
1868 break;// separator item: ignore
1873 // case MIXT_MONODB:
1875 // case MIXT_STEREODB:
1879 // case MIXT_MONOSLIDER16:
1881 // case MIXT_STEREOSLIDER16:
1884 TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1885 __FUNCTION__
, mixext
.type
);
1893 OpenSoundNode::MakeParameterWeb()
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
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",
1913 int mixext_count
= mixer
->CountExtInfos();
1914 TRACE("OpenSoundNode::MakeParameterWeb %i ExtInfos\n", mixext_count
);
1916 for (int32 i
= 0; i
< mixext_count
; i
++) {
1918 if (mixer
->GetExtInfo(i
, &mixext
) < B_OK
)
1921 if (mixext
.type
== MIXT_DEVROOT
) {
1922 oss_mixext_root
* extroot
= (oss_mixext_root
*)mixext
.data
;
1923 TRACE("OpenSoundNode: mixext[%d]: ROOT\n", i
);
1925 const char* childName
= mixext
.extname
;
1926 childName
= extroot
->id
; // extroot->name;
1927 BParameterGroup
*child
= web
->MakeGroup(childName
);
1928 _ProcessGroup(child
, i
, nb
);
1936 // #pragma mark - OpenSoundNode specific
1940 OpenSoundNode::_ProcessGroup(BParameterGroup
*group
, int32 index
,
1941 int32
& nbParameters
)
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
++) {
1950 if (mixer
->GetExtInfo(i
, &mixext
) < B_OK
)
1952 // only keep direct children of that group
1953 if (mixext
.parent
!= index
)
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
)
1977 const char *continuousKind
= B_GAIN
;
1978 BParameterGroup
* child
;
1980 switch (mixext
.type
) {
1982 // root item, should be done already
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
);
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
,
1998 group
->MakeDiscreteParameter(i
, B_MEDIA_RAW_AUDIO
, childName
,
2001 if (nbParameters
> 0) {
2002 (group
->ParameterAt(nbParameters
- 1))->AddOutput(
2003 group
->ParameterAt(nbParameters
));
2009 TRACE("OpenSoundNode: mixext[%d]: ENUM\n", i
);
2010 BDiscreteParameter
*parameter
=
2011 group
->MakeDiscreteParameter(i
, B_MEDIA_RAW_AUDIO
, childName
,
2013 if (nbParameters
> 0) {
2014 (group
->ParameterAt(nbParameters
- 1))->AddOutput(
2015 group
->ParameterAt(nbParameters
));
2018 _ProcessMux(parameter
, i
);
2025 case MIXT_MONOSLIDER16
:
2026 case MIXT_STEREOSLIDER16
:
2027 case MIXT_MONOSLIDER
:
2028 //TRACE("OpenSoundNode: mixext[%d]: MONOSLIDER\n", i);
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)
2040 // if (mixext.flags & MIXF_DECIBEL)
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
));
2068 case MIXT_STEREOPEAK
:
2070 case MIXT_RADIOGROUP
:
2073 break;// separator item: ignore
2078 // case MIXT_MONODB:
2079 // TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2080 // "MIXT_MONODB\n");
2082 // case MIXT_STEREODB:
2083 // TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2084 // "MIXT_STEREODB\n");
2086 // case MIXT_SLIDER:
2090 // case MIXT_MONOSLIDER16:
2092 // case MIXT_STEREOSLIDER16:
2095 TRACE("OpenSoundNode::_ProcessGroup: unknown mixer control "
2096 "type %d\n", mixext
.type
);
2104 OpenSoundNode::_ProcessMux(BDiscreteParameter
* parameter
, int32 index
)
2107 OpenSoundDeviceMixer
*mixer
= fDevice
->MixerAt(0);
2108 oss_mixer_enuminfo enuminfo
;
2109 status_t err
= mixer
->GetEnumInfo(index
, &enuminfo
);
2111 // maybe there is no list.
2112 // generate a count form 0
2114 if (mixer
->GetExtInfo(index
, &mixext
) < B_OK
)
2117 for (int32 i
= 0; i
< mixext
.maxvalue
; i
++) {
2120 parameter
->AddItem(i
, s
.String());
2125 for (int32 i
= 0; i
< enuminfo
.nvalues
; i
++) {
2126 parameter
->AddItem(i
, &enuminfo
.strings
[enuminfo
.strindex
[i
]]);
2133 OpenSoundNode::_PropagateParameterChanges(int from
, int type
, const char* id
)
2137 TRACE("OpenSoundNode::_PropagateParameterChanges(from %i, type %i, "
2138 "id %s)\n", from
, type
, id
);
2140 OpenSoundDeviceMixer
* mixer
= fDevice
->MixerAt(0);
2144 // TODO: Cortex doesn't like that!
2146 // try checking update_counter+caching
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
++) {
2156 status_t err
= mixer
->GetExtInfo(i
, &mixext
);
2161 //if (mixext.ctrl == from)
2164 if (!(mixext
.flags
& MIXF_READABLE
))
2168 if (type
> -1 && mixext
.type
!= type
)
2171 // match internal ID string
2172 if (id
&& strncmp(mixext
.id
, id
, 16))
2175 // BParameter *parameter = NULL;
2176 // for(int32 i=0; i<fWeb->CountParameters(); i++) {
2177 // parameter = fWeb->ParameterAt(i);
2178 // if(parameter->ID() == mixext.ctrl)
2185 // oldValuesSize = 128;
2186 newValuesSize
= 128;
2188 // TRACE("OpenSoundNode::%s: comparing mixer control %d\n",
2189 // __FUNCTION__, mixext.ctrl);
2190 // if (parameter->GetValue(oldValues, &oldValuesSize, &last) < B_OK)
2192 if (GetParameterValue(mixext
.ctrl
, &last
, newValues
,
2193 &newValuesSize
) < B_OK
) {
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
,
2202 // BroadcastChangedParameter(mixext.ctrl);
2210 OpenSoundNode::_PlayThread(NodeInput
* input
)
2213 //set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2214 signal(SIGUSR1
, &_SignalHandler
);
2216 OpenSoundDeviceEngine
* engine
= input
->fRealEngine
;
2217 if (!engine
|| !engine
->InUse())
2219 // skip unconnected or non-busy engines
2220 if (input
->fInput
.source
== media_source::null
2221 && input
->fEngineIndex
== 0)
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
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;
2259 if (!fDevice
->Locker()->Lock())
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) {
2275 //input->WriteTestTone();
2277 // buffer->Recycle();
2280 int32 additionalBytesWritten
= 0;
2281 if (buffer
!= NULL
) {
2282 if (input
->Write(buffer
->Data(), buffer
->SizeUsed()) == B_OK
)
2283 additionalBytesWritten
= buffer
->SizeUsed();
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;
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);
2327 OpenSoundNode::_RecThread(NodeOutput
* output
)
2331 //set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2332 signal(SIGUSR1
, &_SignalHandler
);
2334 OpenSoundDeviceEngine
*engine
= output
->fRealEngine
;
2335 if (!engine
|| !engine
->InUse())
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
)) {
2343 // must be open for read
2344 ASSERT(engine
->OpenMode() & OPEN_READ
);
2348 fDevice
->Locker()->Lock();
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)
2358 fDevice
->Locker()->Unlock();
2359 // Get the next buffer of data
2360 BBuffer
* buffer
= _FillNextBuffer(&abinfo
, *output
);
2361 fDevice
->Locker()->Lock();
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));
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();
2394 OpenSoundNode::_StartPlayThread(NodeInput
* input
)
2397 BAutolock
L(fDevice
->Locker());
2398 // the thread is already started ?
2399 if (input
->fThread
> 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)
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);
2421 resume_thread(input
->fThread
);
2427 OpenSoundNode::_StopPlayThread(NodeInput
* input
)
2429 if (input
->fThread
< 0)
2436 BAutolock
L(fDevice
->Locker());
2437 th
= input
->fThread
;
2438 input
->fThread
= -1;
2439 //kill(th, SIGUSR1);
2442 wait_for_thread(th
, &ret
);
2449 OpenSoundNode::_StartRecThread(NodeOutput
* output
)
2452 // the thread is already started ?
2453 if (output
->fThread
> 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)
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);
2475 resume_thread(output
->fThread
);
2481 OpenSoundNode::_StopRecThread(NodeOutput
* output
)
2483 if (output
->fThread
< 0)
2488 thread_id th
= output
->fThread
;
2489 output
->fThread
= -1;
2491 BAutolock
L(fDevice
->Locker());
2492 //kill(th, SIGUSR1);
2495 wait_for_thread(th
, &ret
);
2502 OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime
,
2503 bigtime_t realTime
, float drift
)
2507 if (!fTimeSourceStarted
)
2510 PublishTime(performanceTime
, realTime
, drift
);
2512 // TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli, "
2513 // "drift : %f\n", perfTime, realTime, drift);
2518 OpenSoundNode::_FillNextBuffer(audio_buf_info
* abinfo
, NodeOutput
& channel
)
2522 BBuffer
* buffer
= channel
.FillNextBuffer(BufferDuration());
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();
2540 hdr
->time_source
= TimeSource()->ID();
2541 // TODO: should be system_time() - latency_as_per(abinfo)
2542 hdr
->start_time
= PerformanceTimeFor(system_time());
2550 OpenSoundNode::GetConfigurationFor(BMessage
* into_message
)
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
)
2566 TRACE("getting parameter %i\n", parameter
->ID());
2568 bigtime_t last_change
;
2570 while ((err
= GetParameterValue(parameter
->ID(), &last_change
, buffer
,
2571 &size
)) == B_NO_MEMORY
) {
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
,
2582 TRACE("parameter err : %s\n", strerror(err
));
2588 PRINT_OBJECT(*into_message
);
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
)
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
)
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
)
2633 // pragma mark - static
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)
2645 OpenSoundNode::_PlayThreadEntry(void* data
)
2648 NodeInput
* channel
= static_cast<NodeInput
*>(data
);
2649 return channel
->fNode
->_PlayThread(channel
);
2654 OpenSoundNode::_RecThreadEntry(void* data
)
2657 NodeOutput
* channel
= static_cast<NodeOutput
*>(data
);
2658 return channel
->fNode
->_RecThread(channel
);
2663 OpenSoundNode::GetFlavor(flavor_info
* outInfo
, int32 id
)
2666 if (outInfo
== NULL
)
2669 outInfo
->flavor_flags
= 0;
2670 outInfo
->possible_count
= 1;
2671 // one flavor at a time
2672 outInfo
->in_format_count
= 0;
2674 outInfo
->in_formats
= 0;
2675 outInfo
->out_format_count
= 0;
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 "
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;
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;
2695 media_format
* outformats
= new media_format
[outInfo
->out_format_count
];
2696 GetFormat(&outformats
[0]);
2697 outInfo
->out_formats
= outformats
;
2702 OpenSoundNode::GetFormat(media_format
* outFormat
)
2705 if (outFormat
== NULL
)
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
;