diff --git a/src/kits/media/SoundPlayNode.cpp b/src/kits/media/SoundPlayNode.cpp index d0c19386de..862117cc6c 100644 --- a/src/kits/media/SoundPlayNode.cpp +++ b/src/kits/media/SoundPlayNode.cpp @@ -1,5 +1,5 @@ /*********************************************************************** - * AUTHOR: Marcus Overhagen + * AUTHOR: Marcus Overhagen, Jérôme Duval * FILE: SoundPlayNode.cpp * DESCR: This is the BBufferProducer, used internally by BSoundPlayer * This belongs into a private namespace, but isn't for @@ -7,10 +7,9 @@ ***********************************************************************/ //#include -#include -#include -#include +#include #include "SoundPlayNode.h" +#include #include "debug.h" #include "ChannelMixer.h" #include "SampleConverter.h" @@ -18,116 +17,32 @@ #include #include -#include - -int driver; -sem_id sem; - -enum { - SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END, - SOUND_SET_PARAMS, - SOUND_SET_PLAYBACK_COMPLETION_SEM, - SOUND_SET_CAPTURE_COMPLETION_SEM, - SOUND_RESERVED_1, /* unused */ - SOUND_RESERVED_2, /* unused */ - SOUND_DEBUG_ON, /* unused */ - SOUND_DEBUG_OFF, /* unused */ - SOUND_WRITE_BUFFER, - SOUND_READ_BUFFER, - SOUND_LOCK_FOR_DMA -}; - -typedef struct audio_buffer_header { - int32 buffer_number; - int32 subscriber_count; - bigtime_t time; - int32 reserved_1; - int32 reserved_2; - int32 reserved_3; - int32 reserved_4; -} audio_buffer_header; - -enum adc_source { - line = 0, cd, mic, loopback -}; - -enum sample_rate { - kHz_8_0 = 0, kHz_5_51, kHz_16_0, kHz_11_025, kHz_27_42, - kHz_18_9, kHz_32_0, kHz_22_05, kHz_37_8 = 9, - kHz_44_1 = 11, kHz_48_0, kHz_33_075, kHz_9_6, kHz_6_62 -}; - -enum sample_format {}; /* obsolete */ - -struct channel { - enum adc_source adc_source; - char adc_gain; - char mic_gain_enable; - char cd_mix_gain; - char cd_mix_mute; - char aux2_mix_gain; - char aux2_mix_mute; - char line_mix_gain; - char line_mix_mute; - char dac_attn; - char dac_mute; -}; - -typedef struct sound_setup { - struct channel left; - struct channel right; - enum sample_rate sample_rate; - enum sample_format playback_format; - enum sample_format capture_format; - char dither_enable; - char mic_attn; - char mic_enable; - char output_boost; - char highpass_enable; - char mono_gain; - char mono_mute; -} sound_setup; - -sound_setup setup = -{ - { line, 15, 0, 31,0,0,0,31,0,6 /* dac attenuation */,0 }, /* left channel*/ - { line, 15, 0, 31,0,0,0,31,0,6 /* dac attenuation */,0 }, /* right channel*/ - kHz_44_1, /* sample rate */ - (sample_format)0, /* ignore (always 16bit-linear) */ - (sample_format)0, /* ignore (always 16bit-linear) */ - 0, /* non-zero enables dither on 16 => 8 bit */ - 0, /* 0..64 mic input level */ - 0, /* non-zero enables mic input */ - 0, /* ignore (always on) */ - 0, /* ignore (always on) */ - 9, /* 0..64 mono speaker gain */ - 0 /* non-zero mutes speaker */ -}; - _SoundPlayNode::_SoundPlayNode(const char *name, const media_multi_audio_format *format, BSoundPlayer *player) : + BMediaNode(name), BBufferProducer(B_MEDIA_RAW_AUDIO), - BMediaNode(name) + BMediaEventLooper(), + mPlayer(player), + mOutputEnabled(true), + mBufferGroup(NULL), + mFramesSent(0) { - TRACE("_SoundPlayNode\n"); - fFormat = *format; - fPlayer = player; - fThreadId = -1; - - fFramesPerBuffer = fFormat.buffer_size / (fFormat.channel_count * (fFormat.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)); - + CALLED(); + mPreferredFormat.type = B_MEDIA_RAW_AUDIO; + mPreferredFormat.u.raw_audio = *format; + printf("Format Info:\n"); - printf(" frame_rate: %f\n",fFormat.frame_rate); - printf(" channel_count: %ld\n",fFormat.channel_count); - printf(" byte_order: %ld (",fFormat.byte_order); - switch (fFormat.byte_order) { + printf(" frame_rate: %f\n",mFormat.u.raw_audio.frame_rate); + printf(" channel_count: %ld\n",mFormat.u.raw_audio.channel_count); + printf(" byte_order: %ld (",mFormat.u.raw_audio.byte_order); + switch (mFormat.u.raw_audio.byte_order) { case B_MEDIA_BIG_ENDIAN: printf("B_MEDIA_BIG_ENDIAN)\n"); break; case B_MEDIA_LITTLE_ENDIAN: printf("B_MEDIA_LITTLE_ENDIAN)\n"); break; default: printf("unknown)\n"); break; } - printf(" buffer_size: %ld\n",fFormat.buffer_size); - printf(" format: %ld (",fFormat.format); - switch (fFormat.format) { + printf(" buffer_size: %ld\n",mFormat.u.raw_audio.buffer_size); + printf(" format: %ld (",mFormat.u.raw_audio.format); + switch (mFormat.u.raw_audio.format) { case media_raw_audio_format::B_AUDIO_FLOAT: printf("B_AUDIO_FLOAT)\n"); break; case media_raw_audio_format::B_AUDIO_SHORT: printf("B_AUDIO_SHORT)\n"); break; case media_raw_audio_format::B_AUDIO_INT: printf("B_AUDIO_INT)\n"); break; @@ -135,304 +50,695 @@ _SoundPlayNode::_SoundPlayNode(const char *name, const media_multi_audio_format case media_raw_audio_format::B_AUDIO_UCHAR: printf("B_AUDIO_UCHAR)\n"); break; default: printf("unknown)\n"); break; } - - driver = open("/dev/audio/old/awe64/1", O_RDWR); - if (driver <= 0) - debugger("couldn't open soundcard device driver\n"); - - sem = create_sem(0,"sem"); - ioctl(driver,SOUND_SET_PARAMS,&setup); - ioctl(driver,SOUND_SET_PLAYBACK_COMPLETION_SEM,&sem); } _SoundPlayNode::~_SoundPlayNode() { - TRACE("~_SoundPlayNode\n"); - Stop(); - close(driver); + CALLED(); + Quit(); } media_multi_audio_format _SoundPlayNode::Format() const { - return fFormat; + return mFormat.u.raw_audio; } +// -------------------------------------------------------- // +// implementation of BMediaNode +// -------------------------------------------------------- // -/* virtual */ status_t -_SoundPlayNode::FormatSuggestionRequested( - media_type type, - int32 quality, - media_format * format) -{ - return B_ERROR; -} - - -/* virtual */ status_t -_SoundPlayNode::FormatProposal( - const media_source & output, - media_format * format) -{ - return B_ERROR; -} - - -/* If the format isn't good, put a good format into *io_format and return error */ -/* If format has wildcard, specialize to what you can do (and change). */ -/* If you can change the format, return OK. */ -/* The request comes from your destination sychronously, so you cannot ask it */ -/* whether it likes it -- you should assume it will since it asked. */ -/* virtual */ status_t -_SoundPlayNode::FormatChangeRequested( - const media_source & source, - const media_destination & destination, - media_format * io_format, - int32 * _deprecated_) -{ - return B_ERROR; -} - - -/* virtual */ status_t -_SoundPlayNode::GetNextOutput( /* cookie starts as 0 */ - int32 * cookie, - media_output * out_output) -{ - return B_ERROR; -} - - -/* virtual */ status_t -_SoundPlayNode::DisposeOutputCookie( - int32 cookie) -{ - return B_ERROR; -} - - -/* In this function, you should either pass on the group to your upstream guy, */ -/* or delete your current group and hang on to this group. Deleting the previous */ -/* group (unless you passed it on with the reclaim flag set to false) is very */ -/* important, else you will 1) leak memory and 2) block someone who may want */ -/* to reclaim the buffers living in that group. */ -/* virtual */ status_t -_SoundPlayNode::SetBufferGroup( - const media_source & for_source, - BBufferGroup * group) -{ - return B_ERROR; -} - - -/* Format of clipping is (as int16-s): . */ -/* Repeat for each line where the clipping is different from the previous line. */ -/* If is negative, use the data from line - (there are 0 pairs after */ -/* a negative . Yes, we only support 32k*32k frame buffers for clipping. */ -/* Any non-0 field of 'display' means that that field changed, and if you don't support */ -/* that change, you should return an error and ignore the request. Note that the buffer */ -/* offset values do not have wildcards; 0 (or -1, or whatever) are real values and must */ -/* be adhered to. */ -/* virtual */ status_t -_SoundPlayNode::VideoClippingChanged( - const media_source & for_source, - int16 num_shorts, - int16 * clip_data, - const media_video_display_info & display, - int32 * _deprecated_) -{ - return B_ERROR; -} - - -/* Iterates over all outputs and maxes the latency found */ -/* virtual */ status_t -_SoundPlayNode::GetLatency( - bigtime_t * out_lantency) -{ - return B_ERROR; -} - - -/* virtual */ status_t -_SoundPlayNode::PrepareToConnect( - const media_source & what, - const media_destination & where, - media_format * format, - media_source * out_source, - char * out_name) -{ - return B_ERROR; -} - - -/* virtual */ void -_SoundPlayNode::Connect( - status_t error, - const media_source & source, - const media_destination & destination, - const media_format & format, - char * io_name) -{ -} - - -/* virtual */ void -_SoundPlayNode::Disconnect( - const media_source & what, - const media_destination & where) -{ -} - - -/* virtual */ void -_SoundPlayNode::LateNoticeReceived( - const media_source & what, - bigtime_t how_much, - bigtime_t performance_time) -{ -} - - -/* virtual */ void -_SoundPlayNode::EnableOutput( - const media_source & what, - bool enabled, - int32 * _deprecated_) -{ -} - - -/* Who instantiated you -- or NULL for app class */ -/* virtual */ BMediaAddOn* -_SoundPlayNode::AddOn(int32 * internal_id) const +BMediaAddOn * _SoundPlayNode::AddOn( + int32 * internal_id) const { + CALLED(); + // BeBook says this only gets called if we were in an add-on. return NULL; } -void -_SoundPlayNode::Start() +void _SoundPlayNode::Preroll(void) { - TRACE("_SoundPlayNode::Start\n"); - if (fThreadId != -1) - return; + CALLED(); + // XXX:Performance opportunity + BMediaNode::Preroll(); +} + +status_t _SoundPlayNode::HandleMessage( + int32 message, + const void * data, + size_t size) +{ + CALLED(); + return B_ERROR; +} + +void _SoundPlayNode::NodeRegistered(void) +{ + CALLED(); - fStopThread = false; - fThreadId = spawn_thread(threadstart,"OpenBeOS play thread",105,this); - resume_thread(fThreadId); -} - -void -_SoundPlayNode::Stop() -{ - TRACE("_SoundPlayNode::Stop\n"); - if (fThreadId == -1) + if (mInitCheckStatus != B_OK) { + ReportError(B_NODE_IN_DISTRESS); return; - - fStopThread = true; + } - status_t dummy; - wait_for_thread(fThreadId,&dummy); - fThreadId = -1; + SetPriority(B_URGENT_PRIORITY); + + mOutput.format = mPreferredFormat; + mOutput.destination = media_destination::null; + mOutput.source.port = ControlPort(); + mOutput.source.id = 1; + mOutput.node = Node(); + sprintf(mOutput.name, "output %ld", mOutput.source.id); + + Run(); } - -int32 -_SoundPlayNode::threadstart(void *arg) +status_t _SoundPlayNode::RequestCompleted(const media_request_info &info) { - ((_SoundPlayNode *)arg)->PlayThread(); - return 0; + CALLED(); + return B_OK; } +void _SoundPlayNode::SetTimeSource(BTimeSource *timeSource) +{ + CALLED(); + BMediaNode::SetTimeSource(timeSource); +} void -_SoundPlayNode::PlayThread() +_SoundPlayNode::SetRunMode(run_mode mode) { - /* - * without real media nodes, timesources, and other stuff, - * it's not easy to provide accurate audio playback - */ - double buffer_time = (1000000.0 * fFramesPerBuffer) / fFormat.frame_rate; - bool ChangeSampleformat = fFormat.format != media_raw_audio_format::B_AUDIO_SHORT; - bool ChangeSamplingrate = fFormat.frame_rate != 44100.0f; - bool ChangeChannelcount = fFormat.channel_count != 2; - int ResampledFramesPerBuffer = int(0.5f + (fFramesPerBuffer * (44100.0f / fFormat.frame_rate))); - void *temp1 = 0; - void *temp2 = 0; - void *temp3 = 0; - - if (ChangeSampleformat) - temp1 = new char [(fFormat.format & 0xf) * fFramesPerBuffer * fFormat.channel_count]; - if (ChangeSamplingrate) - temp2 = new char [sizeof(uint16) * ResampledFramesPerBuffer * fFormat.channel_count]; - if (ChangeChannelcount) - temp3 = new char [sizeof(uint16) * ResampledFramesPerBuffer * 2]; - - TRACE("_SoundPlayNode::PlayThread: ChangeSampleformat = %d, ChangeSamplingrate = %d, ChangeChannelcount = %d\n", - ChangeSampleformat,ChangeSamplingrate,ChangeChannelcount); - - double buf_nb; - bigtime_t start; - void *source; - - char *buf = new char [2 * 2 * ResampledFramesPerBuffer + sizeof(audio_buffer_header)]; - char *buffer = buf + sizeof(audio_buffer_header); - audio_buffer_header *header = (audio_buffer_header *)buf; - header->reserved_1 = 2 * 2 * ResampledFramesPerBuffer + sizeof(audio_buffer_header); - - start = system_time() + 50000; - buf_nb = 0; - while (!fStopThread) { - if (fPlayer->_m_has_data != 0) { - memset(fPlayer->_m_buf,0,fPlayer->_m_bufsize); - fPlayer->PlayBuffer(fPlayer->_m_buf,fPlayer->_m_bufsize,(const media_raw_audio_format)fFormat); - source = fPlayer->_m_buf; - if (ChangeSampleformat) { - MediaKitPrivate::SampleConverter::convert( - temp1, media_raw_audio_format::B_AUDIO_SHORT, - source, fFormat.format, - fFramesPerBuffer * fFormat.channel_count); - source = temp1; - } - if (ChangeSamplingrate) { - MediaKitPrivate::SamplingrateConverter::convert( - temp2, ResampledFramesPerBuffer, - source, fFramesPerBuffer, - media_raw_audio_format::B_AUDIO_SHORT, - fFormat.channel_count); - source = temp2; - } - if (ChangeChannelcount) { - MediaKitPrivate::ChannelMixer::mix( - temp3, 2, - source, fFormat.channel_count, - ResampledFramesPerBuffer, - media_raw_audio_format::B_AUDIO_SHORT); - source = temp3; - } -// if (fFormat.byte_order == B_MEDIA_LITTLE_ENDIAN) -// swap_data(B_INT16_TYPE,source,ResampledFramesPerBuffer * 2 * 2,B_SWAP_ALWAYS); - //at this place, we have a buffer with sound data - //of 2 channels, 16bit integer samples, at 44100 frames/sec - //pointer: source - //framecount: ResampledFramesPerBuffer - -// snooze_until(start + bigtime_t(buf_nb++ * buffer_time),B_SYSTEM_TIMEBASE); - buf_nb++; - memcpy(buffer,source,ResampledFramesPerBuffer * 2 * 2); - ioctl(driver,SOUND_WRITE_BUFFER,buf); - acquire_sem(sem); - - } else { - snooze_until(start + bigtime_t(buf_nb++ * buffer_time),B_SYSTEM_TIMEBASE); - } - } - TRACE("_SoundPlayNode::PlayThread: %.0f buffers processed\n",buf_nb); - delete [] temp1; - delete [] temp2; - delete [] temp3; - delete [] buf; + CALLED(); + PRINT(("_SoundPlayNode::SetRunMode mode:%i\n", mode)); + BMediaNode::SetRunMode(mode); } +// -------------------------------------------------------- // +// implementation for BBufferProducer +// -------------------------------------------------------- // + +status_t +_SoundPlayNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format) +{ + // FormatSuggestionRequested() is not necessarily part of the format negotiation + // process; it's simply an interrogation -- the caller wants to see what the node's + // preferred data format is, given a suggestion by the caller. + CALLED(); + + if (!format) + { + fprintf(stderr, "\tERROR - NULL format pointer passed in!\n"); + return B_BAD_VALUE; + } + + // this is the format we'll be returning (our preferred format) + *format = mPreferredFormat; + + // a wildcard type is okay; we can specialize it + if (type == B_MEDIA_UNKNOWN_TYPE) + type = B_MEDIA_RAW_AUDIO; + + // we only support raw audio + if (type != B_MEDIA_RAW_AUDIO) + return B_MEDIA_BAD_FORMAT; + + return B_OK; +} + +status_t +_SoundPlayNode::FormatProposal(const media_source& output, media_format* format) +{ + // FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand + // out a suggested format, with wildcards for any variations we support. + CALLED(); + // is this a proposal for our one output? + if (output != mOutput.source) + { + fprintf(stderr, "_SoundPlayNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // we only support floating-point raw audio, so we always return that, but we + // supply an error code depending on whether we found the proposal acceptable. + media_type requestedType = format->type; + *format = mPreferredFormat; + if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) + { + fprintf(stderr, "_SoundPlayNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + } + else return B_OK; // raw audio or wildcard type, either is okay by us +} + +status_t +_SoundPlayNode::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_) +{ + CALLED(); + + // we don't support any other formats, so we just reject any format changes. + return B_ERROR; +} + +status_t +_SoundPlayNode::GetNextOutput(int32* cookie, media_output* out_output) +{ + CALLED(); + + if ((*cookie < 1) && (*cookie >= 0)) { + *out_output = mOutput; + *cookie += 1; + return B_OK; + } else + return B_BAD_INDEX; +} + +status_t +_SoundPlayNode::DisposeOutputCookie(int32 cookie) +{ + CALLED(); + // do nothing because we don't use the cookie for anything special + return B_OK; +} + +status_t +_SoundPlayNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) +{ + CALLED(); + + // is this our output? + if (for_source != mOutput.source) + { + fprintf(stderr, "_SoundPlayNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // Are we being passed the buffer group we're already using? + if (newGroup == mBufferGroup) + return B_OK; + + // Ahh, someone wants us to use a different buffer group. At this point we delete + // the one we are using and use the specified one instead. If the specified group is + // NULL, we need to recreate one ourselves, and use *that*. Note that if we're + // caching a BBuffer that we requested earlier, we have to Recycle() that buffer + // *before* deleting the buffer group, otherwise we'll deadlock waiting for that + // buffer to be recycled! + delete mBufferGroup; // waits for all buffers to recycle + if (newGroup != NULL) + { + // we were given a valid group; just use that one from now on + mBufferGroup = newGroup; + } + else + { + // we were passed a NULL group pointer; that means we construct + // our own buffer group to use from now on + size_t size = mOutput.format.u.raw_audio.buffer_size; + int32 count = int32(mLatency / BufferDuration() + 1 + 1); + mBufferGroup = new BBufferGroup(size, count); + } + + return B_OK; +} + +status_t +_SoundPlayNode::GetLatency(bigtime_t* out_latency) +{ + CALLED(); + + // report our *total* latency: internal plus downstream plus scheduling + *out_latency = EventLatency() + SchedulingLatency(); + return B_OK; +} + +status_t +_SoundPlayNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name) +{ + // PrepareToConnect() is the second stage of format negotiations that happens + // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() + // method has been called, and that node has potentially changed the proposed + // format. It may also have left wildcards in the format. PrepareToConnect() + // *must* fully specialize the format before returning! + CALLED(); + + // is this our output? + if (what != mOutput.source) + { + fprintf(stderr, "_SoundPlayNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // are we already connected? + if (mOutput.destination != media_destination::null) + return B_MEDIA_ALREADY_CONNECTED; + + // the format may not yet be fully specialized (the consumer might have + // passed back some wildcards). Finish specializing it now, and return an + // error if we don't support the requested format. + if (format->type != B_MEDIA_RAW_AUDIO) + { + fprintf(stderr, "\tnon-raw-audio format?!\n"); + return B_MEDIA_BAD_FORMAT; + } + else if (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_SHORT) + { + fprintf(stderr, "\tnon-short-audio format?!\n"); + return B_MEDIA_BAD_FORMAT; + } + + // !!! validate all other fields except for buffer_size here, because the consumer might have + // supplied different values from AcceptFormat()? + + // check the buffer size, which may still be wildcarded + if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) + { + format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest + fprintf(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size); + } + else + { + fprintf(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size); + } + + // Now reserve the connection, and return information about it + mOutput.destination = where; + mOutput.format = *format; + *out_source = mOutput.source; + strncpy(out_name, mOutput.name, B_MEDIA_NAME_LENGTH); + return B_OK; +} + +void +_SoundPlayNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) +{ + CALLED(); + + // is this our output? + if (source != mOutput.source) + { + fprintf(stderr, "_SoundPlayNode::Connect returning\n"); + return; + } + + // If something earlier failed, Connect() might still be called, but with a non-zero + // error code. When that happens we simply unreserve the connection and do + // nothing else. + if (error) + { + mOutput.destination = media_destination::null; + mOutput.format = mPreferredFormat; + return; + } + + // Okay, the connection has been confirmed. Record the destination and format + // that we agreed on, and report our connection name again. + mOutput.destination = destination; + mOutput.format = format; + strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH); + + // Now that we're connected, we can determine our downstream latency. + // Do so, then make sure we get our events early enough. + media_node_id id; + FindLatencyFor(mOutput.destination, &mLatency, &id); + fprintf(stderr, "\tdownstream latency = %Ld\n", mLatency); + + mInternalLatency = 10LL; + fprintf(stderr, "\tbuffer-filling took %Ld usec on this machine\n", mInternalLatency); + SetEventLatency(mLatency + mInternalLatency); + + // reset our buffer duration, etc. to avoid later calculations + bigtime_t duration = mOutput.format.u.raw_audio.buffer_size * 10000 + / ( (mOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * mOutput.format.u.raw_audio.channel_count) + / ((int32)(mOutput.format.u.raw_audio.frame_rate / 100)); + //bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate); + SetBufferDuration(duration); + + // Set up the buffer group for our connection, as long as nobody handed us a + // buffer group (via SetBufferGroup()) prior to this. That can happen, for example, + // if the consumer calls SetOutputBuffersFor() on us from within its Connected() + // method. + if (!mBufferGroup) + AllocateBuffers(); +} + +void +_SoundPlayNode::Disconnect(const media_source& what, const media_destination& where) +{ + CALLED(); + + // is this our output? + if (what != mOutput.source) + { + fprintf(stderr, "_SoundPlayNode::Disconnect returning\n"); + return; + } + + // Make sure that our connection is the one being disconnected + if ((where == mOutput.destination) && (what == mOutput.source)) + { + mOutput.destination = media_destination::null; + mOutput.format = mPreferredFormat; + delete mBufferGroup; + mBufferGroup = NULL; + } + else + { + fprintf(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n", + what.id, where.id, mOutput.source.id, mOutput.destination.id); + } +} + +void +_SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) +{ + CALLED(); + + // is this our output? + if (what != mOutput.source) + { + fprintf(stderr, "_SoundPlayNode::LateNoticeReceived returning\n"); + return; + } + + // If we're late, we need to catch up. Respond in a manner appropriate to our + // current run mode. + if (RunMode() == B_RECORDING) + { + // A hardware capture node can't adjust; it simply emits buffers at + // appropriate points. We (partially) simulate this by not adjusting + // our behavior upon receiving late notices -- after all, the hardware + // can't choose to capture "sooner".... + } + else if (RunMode() == B_INCREASE_LATENCY) + { + // We're late, and our run mode dictates that we try to produce buffers + // earlier in order to catch up. This argues that the downstream nodes are + // not properly reporting their latency, but there's not much we can do about + // that at the moment, so we try to start producing buffers earlier to + // compensate. + mInternalLatency += how_much; + SetEventLatency(mLatency + mInternalLatency); + + fprintf(stderr, "\tincreasing latency to %Ld\n", mLatency + mInternalLatency); + } + else + { + // The other run modes dictate various strategies for sacrificing data quality + // in the interests of timely data delivery. The way *we* do this is to skip + // a buffer, which catches us up in time by one buffer duration. + /*size_t nSamples = mOutput.format.u.raw_audio.buffer_size / sizeof(float); + mSamplesSent += nSamples;*/ + + fprintf(stderr, "\tskipping a buffer to try to catch up\n"); + } +} + +void +_SoundPlayNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_) +{ + CALLED(); + + // If I had more than one output, I'd have to walk my list of output records to see + // which one matched the given source, and then enable/disable that one. But this + // node only has one output, so I just make sure the given source matches, then set + // the enable state accordingly. + // is this our output? + if (what != mOutput.source) + { + fprintf(stderr, "_SoundPlayNode::EnableOutput returning\n"); + return; + } + + mOutputEnabled = enabled; +} + +void +_SoundPlayNode::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag) +{ + CALLED(); + // we don't support offline mode + return; +} + +void +_SoundPlayNode::LatencyChanged(const media_source& source, const media_destination& destination, bigtime_t new_latency, uint32 flags) +{ + CALLED(); + + // something downstream changed latency, so we need to start producing + // buffers earlier (or later) than we were previously. Make sure that the + // connection that changed is ours, and adjust to the new downstream + // latency if so. + if ((source == mOutput.source) && (destination == mOutput.destination)) + { + mLatency = new_latency; + SetEventLatency(mLatency + mInternalLatency); + } +} + +// -------------------------------------------------------- // +// implementation for BMediaEventLooper +// -------------------------------------------------------- // + +void _SoundPlayNode::HandleEvent( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + switch (event->type) { + case BTimedEventQueue::B_START: + HandleStart(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_SEEK: + HandleSeek(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_WARP: + HandleWarp(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_STOP: + HandleStop(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + if (RunState() == BMediaEventLooper::B_STARTED) { + HandleBuffer(event,lateness,realTimeEvent); + } + break; + case BTimedEventQueue::B_DATA_STATUS: + HandleDataStatus(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_PARAMETER: + HandleParameter(event,lateness,realTimeEvent); + break; + default: + fprintf(stderr," unknown event type: %li\n",event->type); + break; + } +} + +// protected: + +// how should we handle late buffers? drop them? +// notify the producer? +status_t +_SoundPlayNode::HandleBuffer( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + + // make sure we're both started *and* connected before delivering a buffer + if ((RunState() == BMediaEventLooper::B_STARTED) && (mOutput.destination != media_destination::null)) + { + // Get the next buffer of data + BBuffer* buffer = FillNextBuffer(event->event_time); + if (buffer) + { + // send the buffer downstream if and only if output is enabled + status_t err = B_ERROR; + if (mOutputEnabled) err = SendBuffer(buffer, mOutput.destination); + if (err) + { + // we need to recycle the buffer ourselves if output is disabled or + // if the call to SendBuffer() fails + buffer->Recycle(); + } + } + + // track how much media we've delivered so far + size_t nFrames = mOutput.format.u.raw_audio.buffer_size + / (mOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + / mOutput.format.u.raw_audio.channel_count; + mFramesSent += nFrames; + + // The buffer is on its way; now schedule the next one to go + bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); + media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER); + EventQueue()->AddEvent(nextBufferEvent); + } + + return B_OK; +} + +status_t +_SoundPlayNode::HandleDataStatus( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + PRINT(("_SoundPlayNode::HandleDataStatus status:%li, lateness:%li\n", event->data, lateness)); + switch(event->data) { + case B_DATA_NOT_AVAILABLE: + break; + case B_DATA_AVAILABLE: + break; + case B_PRODUCER_STOPPED: + break; + default: + break; + } + return B_OK; +} + +status_t +_SoundPlayNode::HandleStart( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + // don't do anything if we're already running + if (RunState() != B_STARTED) + { + // We want to start sending buffers now, so we set up the buffer-sending bookkeeping + // and fire off the first "produce a buffer" event. + mStartTime = event->event_time; + media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER); + + // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through + // the event queue, like this: + // + // this->HandleEvent(&firstBufferEvent, 0, false); + // + EventQueue()->AddEvent(firstBufferEvent); + } + return B_OK; +} + +status_t +_SoundPlayNode::HandleSeek( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + PRINT(("_SoundPlayNode::HandleSeek(t=%lld,d=%li,bd=%lld)\n",event->event_time,event->data,event->bigdata)); + return B_OK; +} + +status_t +_SoundPlayNode::HandleWarp( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + return B_OK; +} + +status_t +_SoundPlayNode::HandleStop( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + // flush the queue so downstreamers don't get any more + EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); + + return B_OK; +} + +status_t +_SoundPlayNode::HandleParameter( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + return B_OK; +} + +void +_SoundPlayNode::AllocateBuffers() +{ + CALLED(); + + // allocate enough buffers to span our downstream latency, plus one + size_t size = mOutput.format.u.raw_audio.buffer_size; + int32 count = int32(mLatency / BufferDuration() + 1 + 1); + + PRINT(("\tlatency = %Ld, buffer duration = %Ld\n", mLatency, BufferDuration())); + PRINT(("\tcreating group of %ld buffers, size = %lu\n", count, size)); + mBufferGroup = new BBufferGroup(size, count); +} + +BBuffer* +_SoundPlayNode::FillNextBuffer(bigtime_t event_time) +{ + CALLED(); + + // get a buffer from our buffer group + BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration()); + + // if we fail to get a buffer (for example, if the request times out), we skip this + // buffer and go on to the next, to avoid locking up the control thread + if (!buf) + { + return NULL; + } + + memset(buf->Data(), 0, mOutput.format.u.raw_audio.buffer_size); + if(mPlayer->HasData()) { + mPlayer->PlayBuffer(buf->Data(), + mOutput.format.u.raw_audio.buffer_size, mOutput.format.u.raw_audio); + } + + // fill in the buffer header + media_header* hdr = buf->Header(); + hdr->type = B_MEDIA_RAW_AUDIO; + hdr->size_used = mOutput.format.u.raw_audio.buffer_size; + hdr->time_source = TimeSource()->ID(); + + bigtime_t stamp; + if (RunMode() == B_RECORDING) + { + // In B_RECORDING mode, we stamp with the capture time. We're not + // really a hardware capture node, but we simulate it by using the (precalculated) + // time at which this buffer "should" have been created. + stamp = event_time; + } + else + { + // okay, we're in one of the "live" performance run modes. in these modes, we + // stamp the buffer with the time at which the buffer should be rendered to the + // output, not with the capture time. mStartTime is the cached value of the + // first buffer's performance time; we calculate this buffer's performance time as + // an offset from that time, based on the amount of media we've created so far. + // Recalculating every buffer like this avoids accumulation of error. + stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); + } + hdr->start_time = stamp; + PRINT(("TimeSource()->Now() : %li\n", TimeSource()->Now())); + PRINT(("hdr->start_time : %li\n", hdr->start_time)); + PRINT(("mFramesSent : %li\n", mFramesSent)); + PRINT(("mOutput.format.u.raw_audio.frame_rate : %f\n", mOutput.format.u.raw_audio.frame_rate)); + + return buf; +} + + diff --git a/src/kits/media/SoundPlayNode.h b/src/kits/media/SoundPlayNode.h index e00996dbe2..597ef004e3 100644 --- a/src/kits/media/SoundPlayNode.h +++ b/src/kits/media/SoundPlayNode.h @@ -1,91 +1,174 @@ #ifndef _SOUND_PLAY_NODE_ #define _SOUND_PLAY_NODE_ +#include +#include +#include +#include +#include "SoundPlayer.h" + /*********************************************************************** - * AUTHOR: Marcus Overhagen + * AUTHOR: Marcus Overhagen, Jérôme Duval * FILE: SoundPlayNode.h * DESCR: This is the BBufferProducer, used internally by BSoundPlayer * This belongs into a private namespace, but isn't for * compatibility reasons. ***********************************************************************/ -class _SoundPlayNode : public BBufferProducer +class _SoundPlayNode + : public BBufferProducer, public BMediaEventLooper { public: _SoundPlayNode(const char *name, const media_multi_audio_format *format, BSoundPlayer *player); ~_SoundPlayNode(); - - media_multi_audio_format Format() const; -private: +/*************************/ +/* begin from BMediaNode */ +public: +virtual BMediaAddOn* AddOn( + int32 * internal_id) const; /* Who instantiated you -- or NULL for app class */ - virtual status_t FormatSuggestionRequested( - media_type type, - int32 quality, - media_format * format); - virtual status_t FormatProposal( - const media_source & output, - media_format * format); - virtual status_t FormatChangeRequested( - const media_source & source, - const media_destination & destination, - media_format * io_format, - int32 * _deprecated_); - virtual status_t GetNextOutput( /* cookie starts as 0 */ - int32 * cookie, - media_output * out_output); - virtual status_t DisposeOutputCookie( - int32 cookie); - virtual status_t SetBufferGroup( - const media_source & for_source, - BBufferGroup * group); - virtual status_t VideoClippingChanged( - const media_source & for_source, - int16 num_shorts, - int16 * clip_data, - const media_video_display_info & display, - int32 * _deprecated_); - virtual status_t GetLatency( - bigtime_t * out_lantency); - virtual status_t PrepareToConnect( - const media_source & what, - const media_destination & where, - media_format * format, - media_source * out_source, - char * out_name); - virtual void Connect( - status_t error, - const media_source & source, - const media_destination & destination, - const media_format & format, - char * io_name); - virtual void Disconnect( - const media_source & what, - const media_destination & where); - virtual void LateNoticeReceived( - const media_source & what, - bigtime_t how_much, - bigtime_t performance_time); - virtual void EnableOutput( - const media_source & what, - bool enabled, - int32 * _deprecated_); - - virtual BMediaAddOn* AddOn( - int32 * internal_id) const; +protected: + /* These don't return errors; instead, they use the global error condition reporter. */ + /* A node is required to have a queue of at least one pending command (plus TimeWarp) */ + /* and is recommended to allow for at least one pending command of each type. */ + /* Allowing an arbitrary number of outstanding commands might be nice, but apps */ + /* cannot depend on that happening. */ +virtual void Preroll(void); public: - void Start(); - void Stop(); +virtual status_t HandleMessage( + int32 message, + const void * data, + size_t size); + +protected: +virtual void NodeRegistered(void); /* reserved 2 */ +virtual status_t RequestCompleted(const media_request_info &info); +virtual void SetTimeSource(BTimeSource *timeSource); +virtual void SetRunMode(run_mode mode); + +/* end from BMediaNode */ +/***********************/ + +/******************************/ +/* begin from BBufferProducer */ + + virtual status_t FormatSuggestionRequested( media_type type, + int32 quality, + media_format* format); + + virtual status_t FormatProposal( const media_source& output, + media_format* format); + + virtual status_t FormatChangeRequested( const media_source& source, + const media_destination& destination, + media_format* io_format, + int32* _deprecated_); + virtual status_t GetNextOutput( int32* cookie, + media_output* out_output); + virtual status_t DisposeOutputCookie( int32 cookie); + + virtual status_t SetBufferGroup( const media_source& for_source, + BBufferGroup* group); + virtual status_t GetLatency( bigtime_t * out_latency); + + virtual status_t PrepareToConnect( const media_source& what, + const media_destination& where, + media_format* format, + media_source* out_source, + char* out_name); + + virtual void Connect( status_t error, + const media_source& source, + const media_destination& destination, + const media_format& format, + char* io_name); + + virtual void Disconnect( const media_source& what, + const media_destination& where); + + virtual void LateNoticeReceived( const media_source& what, + bigtime_t how_much, + bigtime_t performance_time); + + virtual void EnableOutput( const media_source & what, + bool enabled, + int32* _deprecated_); + virtual void AdditionalBufferRequested( const media_source& source, + media_buffer_id prev_buffer, + bigtime_t prev_time, + const media_seek_tag* prev_tag); + virtual void LatencyChanged( const media_source& source, + const media_destination& destination, + bigtime_t new_latency, + uint32 flags); +/* end from BBufferProducer */ +/****************************/ + +/********************************/ +/* start from BMediaEventLooper */ + + protected: + /* you must override to handle your events! */ + /* you should not call HandleEvent directly */ + virtual void HandleEvent( const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); + +/* end from BMediaEventLooper */ +/******************************/ + +public: + media_multi_audio_format Format() const; + +protected: + +virtual status_t HandleStart( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleSeek( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleWarp( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleStop( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleBuffer( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleDataStatus( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleParameter( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); + + void AllocateBuffers(); + BBuffer* FillNextBuffer(bigtime_t event_time); private: - int fFramesPerBuffer; - thread_id fThreadId; - volatile bool fStopThread; - static int32 threadstart(void *arg); - void PlayThread(); - media_multi_audio_format fFormat; - BSoundPlayer *fPlayer; + BSoundPlayer *mPlayer; + + status_t mInitCheckStatus; + bool mOutputEnabled; + media_output mOutput; + BBufferGroup *mBufferGroup; + media_format mPreferredFormat; + media_format mFormat; + bigtime_t mLatency; + bigtime_t mInternalLatency; + bigtime_t mStartTime; + uint64 mFramesSent; }; #endif diff --git a/src/kits/media/SoundPlayer.cpp b/src/kits/media/SoundPlayer.cpp index 1ca318b3e4..bfd6f27306 100644 --- a/src/kits/media/SoundPlayer.cpp +++ b/src/kits/media/SoundPlayer.cpp @@ -1,10 +1,10 @@ /*********************************************************************** - * AUTHOR: Marcus Overhagen + * AUTHOR: Marcus Overhagen, Jérôme Duval * FILE: SoundPlayer.cpp * DESCR: ***********************************************************************/ #include -#include +#include "SoundPlayer.h" #include #include @@ -78,9 +78,37 @@ BSoundPlayer::BSoundPlayer(const media_node & toNode, BSoundPlayer::~BSoundPlayer() { CALLED(); - if (_m_node) - _m_node->Release(); - delete _m_node; + if (_m_node) { + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n"); + } else { + status_t err; + + // Ordinarily we'd stop *all* of the nodes in the chain at this point. However, + // one of the nodes is the System Mixer, and stopping the Mixer is a Bad Idea (tm). + // So, we just disconnect from it, and release our references to the nodes that + // we're using. We *are* supposed to do that even for global nodes like the Mixer. + Stop(true, false); + + err = roster->Disconnect(m_input.node.node, m_input.source, + m_output.node.node, m_output.destination); + if (err) { + fprintf(stderr, "* Error disconnecting nodes: %ld (%s)\n", err, strerror(err)); + } + + err = roster->ReleaseNode(m_input.node); + if (err) { + fprintf(stderr, "* Error releasing input node: %ld (%s)\n", err, strerror(err)); + } + + err = roster->ReleaseNode(m_output.node); + if (err) { + fprintf(stderr, "* Error releasing output node: %ld (%s)\n", err, strerror(err)); + } + _m_node = NULL; + } + } delete [] _m_buf; } @@ -117,9 +145,25 @@ BSoundPlayer::Start() if (!_m_node) return B_ERROR; + + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n"); + return B_ERROR; + } - _m_node->Start(); - return B_OK; + BTimeSource *timeSource = roster->MakeTimeSourceFor(_m_node->Node()); + + // make sure we give the producer enough time to run buffers through + // the node chain, otherwise it'll start up already late + bigtime_t latency = 0; + status_t err = roster->GetLatencyFor(_m_node->Node(), &latency); + + err = roster->StartNode(_m_node->Node(), timeSource->Now() + latency); + + timeSource->Release(); + + return err; } @@ -131,8 +175,15 @@ BSoundPlayer::Stop(bool block, if (!_m_node) return; + + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n"); + return; + } + + roster->StopNode(_m_node->Node(), 0, true); - _m_node->Stop(); } BSoundPlayer::BufferPlayerFunc @@ -200,10 +251,8 @@ BSoundPlayer::CurrentTime() CALLED(); if (!_m_node) return system_time(); -#if 0 /* we don't have a media roster, or real media nodes yet */ - return _m_node->TimeSource()->Now(); /* either this one is wrong */ -#endif - return system_time(); + + return _m_node->TimeSource()->Now(); } @@ -213,19 +262,29 @@ BSoundPlayer::PerformanceTime() CALLED(); if (!_m_node) return (bigtime_t) B_ERROR; -#if 0 /* we don't have a media roster, or real media nodes yet */ - return _m_node->TimeSource()->Now(); /* or this one is wrong */ -#endif - return system_time(); + + return _m_node->TimeSource()->Now(); } status_t BSoundPlayer::Preroll() { - UNIMPLEMENTED(); + CALLED(); - return B_OK; + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n"); + return B_ERROR; + } + + status_t err = roster->PrerollNode(m_output.node); + + if(err != B_OK) { + fprintf(stderr, "Error while PrerollNode: %ld (%s)\n", err, strerror(err)); + } + + return err; } @@ -342,8 +401,22 @@ BSoundPlayer::GetVolumeInfo(media_node *out_node, bigtime_t BSoundPlayer::Latency() { - BROKEN(); - return 50000; + CALLED(); + + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n"); + return 0; + } + + bigtime_t latency = 0; + status_t err = roster->GetLatencyFor(m_output.node, &latency); + + if(err != B_OK) { + fprintf(stderr, "Error while GetLatencyFor: %ld (%s)\n", err, strerror(err)); + } + + return latency; } @@ -423,10 +496,7 @@ BSoundPlayer::Init( _m_waiting = NULL; _PlayBuffer = PlayBuffer; _Notifier = Notifier; - //_m_lock; _m_volume = 0.0f; - //m_input; - //m_output; _m_mix_buffer = 0; _m_mix_buffer_size = 0; _m_cookie = cookie; @@ -440,11 +510,16 @@ BSoundPlayer::Init( _m_node = 0; -#if 0 /* we don't have a media roster, or real media nodes yet */ - status_t status; - BMediaRoster *roster; - media_node outnode; - roster = BMediaRoster::Roster(); + status_t err; + media_node outputNode; + media_output _output; + media_input _input; + int32 inputCount, outputCount; + media_format tryFormat; + media_multi_audio_format fmt; + media_node timeSource; + + BMediaRoster *roster = BMediaRoster::Roster(); if (!roster) { TRACE("BSoundPlayer::Init: Couldn't get BMediaRoster\n"); return; @@ -453,17 +528,14 @@ BSoundPlayer::Init( //connect our producer node either to the //system mixer or to the supplied out node if (!node) { - status = roster->GetAudioMixer(&outnode); - if (status != B_OK) { + err = roster->GetAudioMixer(&outputNode); + if (err != B_OK) { TRACE("BSoundPlayer::Init: Couldn't GetAudioMixer\n"); - SetInitError(status); - return; + goto the_end; } - node = &outnode; + node = &outputNode; } -#endif - - media_multi_audio_format fmt; + memcpy(&fmt,format,sizeof(fmt)); if (fmt.frame_rate == media_multi_audio_format::wildcard.frame_rate) @@ -485,29 +557,54 @@ BSoundPlayer::Init( _m_bufsize = fmt.buffer_size; _m_buf = new char[_m_bufsize]; _m_node = new _SoundPlayNode(name,&fmt,this); - - -/* - m_input = ; - m_output = ; - -tryFormat = fileAudioOutput.format; -   err = roster->Connect(fileAudioOutput.source, audioInput.destination, -               &tryFormat, &m_output, &m_input); - - - err = roster->GetStartLatencyFor(timeSourceNode, &startTime); -   startTime += b_timesource->PerformanceTimeFor(BTimeSource::RealTime() -               + 1000000 / 50); -    -   err = roster->StartNode(mediaFileNode, startTime); -   err = roster->StartNode(codecNode, startTime); -   err = roster->StartNode(videoNode, startTime); - -*/ + err = roster->RegisterNode(_m_node); + if(err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't RegisterNode\n"); + goto the_end; + } - SetInitError(B_OK); + // set the producer's time source to be the "default" time source, which + // the Mixer uses too. + + roster->GetTimeSource(&timeSource); + roster->SetTimeSourceFor(_m_node->Node().node, timeSource.node); + + if(!input) { + err = roster->GetFreeInputsFor(*node, &_input, 1, + &inputCount, B_MEDIA_RAW_AUDIO); + if(err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't GetFreeInputsFor\n"); + goto the_end; + } + } else { + _input = *input; + } + err = roster->GetFreeOutputsFor(_m_node->Node(), &_output, 1, &outputCount, B_MEDIA_RAW_AUDIO); + if(err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't GetFreeOutputsFor\n"); + goto the_end; + } + + //tryFormat.type = B_MEDIA_RAW_AUDIO; + //tryformat.fileAudioOutput.format; + tryFormat = _output.format; + err = roster->Connect(_output.source, _input.destination, &tryFormat, &m_output, &m_input); + if(err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't Connect\n"); + goto the_end; + } + + // Set an appropriate run mode for the producer + err = roster->SetRunModeNode(_m_node->Node(), BMediaNode::B_INCREASE_LATENCY); + if(err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't SetRunModeNode\n"); + goto the_end; + } + +the_end: + TRACE("BSoundPlayer::Init: %s\n", strerror(err)); + SetInitError(err); } /* virtual */ void