From df42a7b539617ec8e5c9c8c4f18e6f3a71f81301 Mon Sep 17 00:00:00 2001 From: beveloper Date: Sun, 24 Oct 2004 00:16:27 +0000 Subject: [PATCH] Massive BSoundPlayer rewrite. Format negotiation and node registering/unregistering fixed. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9470 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/media/SoundPlayer.h | 13 +- src/kits/media/SoundPlayNode.cpp | 138 ++++--- src/kits/media/SoundPlayNode.h | 4 +- src/kits/media/SoundPlayer.cpp | 643 +++++++++++++++++-------------- 4 files changed, 437 insertions(+), 361 deletions(-) diff --git a/headers/os/media/SoundPlayer.h b/headers/os/media/SoundPlayer.h index 7d916774e2..087bc68d45 100644 --- a/headers/os/media/SoundPlayer.h +++ b/headers/os/media/SoundPlayer.h @@ -152,22 +152,17 @@ virtual status_t _Reserved_SoundPlayer_7(void *, ...); void (*fPlayBufferFunc)(void * cookie, void * buffer, size_t size, const media_raw_audio_format & format); void (*fNotifierFunc)(void * cookie, sound_player_notification what, ...); BLocker fLocker; - float fVolume; - media_input fMediaInput; - media_output fMediaOutput; - float * _m_mix_buffer; - size_t _m_mix_buffer_size; + float fVolumeDB; + media_input fMediaInput; // the system mixer + media_output fMediaOutput; // the player node void * fCookie; - void * _m_buf; - size_t _m_bufsize; int32 fFlags; status_t fInitStatus; // new in R4.1 - bigtime_t _m_perfTime; BContinuousParameter * fVolumeSlider; bigtime_t fLastVolumeUpdate; BParameterWeb *fParameterWeb; - uint32 _m_reserved[9]; + uint32 _m_reserved[15]; void NotifySoundDone( play_id sound, diff --git a/src/kits/media/SoundPlayNode.cpp b/src/kits/media/SoundPlayNode.cpp index 3505be125e..58385772db 100644 --- a/src/kits/media/SoundPlayNode.cpp +++ b/src/kits/media/SoundPlayNode.cpp @@ -7,6 +7,7 @@ ***********************************************************************/ #include +#include #include #include #include @@ -25,7 +26,7 @@ #define SEND_NEW_BUFFER_EVENT (BTimedEventQueue::B_USER_EVENT + 1) -_SoundPlayNode::_SoundPlayNode(const char *name, const media_multi_audio_format *format, BSoundPlayer *player) : +_SoundPlayNode::_SoundPlayNode(const char *name, BSoundPlayer *player) : BMediaNode(name), BBufferProducer(B_MEDIA_RAW_AUDIO), BMediaEventLooper(), @@ -37,28 +38,8 @@ _SoundPlayNode::_SoundPlayNode(const char *name, const media_multi_audio_format mTooEarlyCount(0) { CALLED(); - mFormat.type = B_MEDIA_RAW_AUDIO; - mFormat.u.raw_audio = *format; - - DPRINTF("Format Info:\n"); - DPRINTF(" frame_rate: %.1f (%ld)\n", mFormat.u.raw_audio.frame_rate, (int32)mFormat.u.raw_audio.frame_rate); - DPRINTF(" channel_count: %ld\n",mFormat.u.raw_audio.channel_count); - DPRINTF(" byte_order: %ld (",mFormat.u.raw_audio.byte_order); - switch (mFormat.u.raw_audio.byte_order) { - case B_MEDIA_BIG_ENDIAN: DPRINTF("B_MEDIA_BIG_ENDIAN)\n"); break; - case B_MEDIA_LITTLE_ENDIAN: DPRINTF("B_MEDIA_LITTLE_ENDIAN)\n"); break; - default: DPRINTF("unknown)\n"); break; - } - DPRINTF(" buffer_size: %ld\n",mFormat.u.raw_audio.buffer_size); - DPRINTF(" format: %ld (",mFormat.u.raw_audio.format); - switch (mFormat.u.raw_audio.format) { - case media_raw_audio_format::B_AUDIO_FLOAT: DPRINTF("B_AUDIO_FLOAT)\n"); break; - case media_raw_audio_format::B_AUDIO_SHORT: DPRINTF("B_AUDIO_SHORT)\n"); break; - case media_raw_audio_format::B_AUDIO_INT: DPRINTF("B_AUDIO_INT)\n"); break; - case media_raw_audio_format::B_AUDIO_CHAR: DPRINTF("B_AUDIO_CHAR)\n"); break; - case media_raw_audio_format::B_AUDIO_UCHAR: DPRINTF("B_AUDIO_UCHAR)\n"); break; - default: DPRINTF("unknown)\n"); break; - } + mOutput.format.type = B_MEDIA_RAW_AUDIO; + mOutput.format.u.raw_audio = media_multi_audio_format::wildcard; } @@ -75,16 +56,16 @@ _SoundPlayNode::IsPlaying() } bigtime_t -_SoundPlayNode::Latency() +_SoundPlayNode::CurrentTime() { - return EventLatency(); + int frame_rate = (int)mOutput.format.u.raw_audio.frame_rate; + return frame_rate == 0 ? 0 : bigtime_t((1000000LL * mFramesSent) / frame_rate); } - media_multi_audio_format _SoundPlayNode::Format() const { - return mFormat.u.raw_audio; + return mOutput.format.u.raw_audio; } // -------------------------------------------------------- // @@ -122,7 +103,8 @@ void _SoundPlayNode::NodeRegistered(void) SetPriority(B_URGENT_PRIORITY); - mOutput.format = mFormat; + mOutput.format.type = B_MEDIA_RAW_AUDIO; + mOutput.format.u.raw_audio = media_multi_audio_format::wildcard; mOutput.destination = media_destination::null; mOutput.source.port = ControlPort(); mOutput.source.id = 0; @@ -168,7 +150,8 @@ _SoundPlayNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, me return B_MEDIA_BAD_FORMAT; // this is the format we'll be returning (our preferred format) - *format = mFormat; + format->type = B_MEDIA_RAW_AUDIO; + format->u.raw_audio = media_multi_audio_format::wildcard; return B_OK; } @@ -186,16 +169,21 @@ _SoundPlayNode::FormatProposal(const media_source& output, media_format* format) 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 = mFormat; - if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) { + // if wildcard, change it to raw audio + if (format->type == B_MEDIA_UNKNOWN_TYPE) + format->type = B_MEDIA_RAW_AUDIO; + + // if not raw audio, we can't support it + if (format->type != B_MEDIA_RAW_AUDIO) { TRACE("_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 + + char buf[100]; + string_for_format(*format, buf, sizeof(buf)); + printf("_SoundPlayNode::FormatProposal: format %s\n", buf); + + return B_OK; } status_t @@ -284,15 +272,14 @@ 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() + // 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() + // 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) - { + if (what != mOutput.source) { TRACE("_SoundPlayNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n"); return B_MEDIA_BAD_SOURCE; } @@ -302,26 +289,55 @@ _SoundPlayNode::PrepareToConnect(const media_source& what, const media_destinati 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 + // 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) - { - TRACE("\tnon-raw-audio format?!\n"); + + char buf[100]; + string_for_format(*format, buf, sizeof(buf)); + printf("_SoundPlayNode::PrepareToConnect: input format %s\n", buf); + + // if not raw audio, we can't support it + if (format->type != B_MEDIA_UNKNOWN_TYPE && format->type != B_MEDIA_RAW_AUDIO) { + TRACE("_SoundPlayNode::PrepareToConnect: non raw format, returning B_MEDIA_BAD_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 - TRACE("\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size); - } - else - { - TRACE("\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size); + // the haiku mixer might have a hint + // for us, so check for it + #define FORMAT_USER_DATA_TYPE 0x7294a8f3 + #define FORMAT_USER_DATA_MAGIC_1 0xc84173bd + #define FORMAT_USER_DATA_MAGIC_2 0x4af62b7d + uint32 channel_count = 0; + float frame_rate = 0; + if (format->user_data_type == FORMAT_USER_DATA_TYPE + && *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1 + && *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) { + channel_count = *(uint32 *)&format->user_data[4]; + frame_rate = *(float *)&format->user_data[20]; + printf("_SoundPlayNode::PrepareToConnect: found mixer info: channel_count %ld, frame_rate %.1f\n", channel_count, frame_rate); } + if (channel_count <= 0) + channel_count = 2; + if (frame_rate <= 0) + frame_rate = 44100; + + media_format default_format; + default_format.type = B_MEDIA_RAW_AUDIO; + default_format.u.raw_audio.frame_rate = frame_rate > 0 ? frame_rate : 44100; + default_format.u.raw_audio.channel_count = channel_count > 0 ? channel_count : 2; + default_format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + default_format.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; + default_format.u.raw_audio.buffer_size = 0; + format->SpecializeTo(&default_format); + + if (format->u.raw_audio.buffer_size == 0) + format->u.raw_audio.buffer_size = BMediaRoster::Roster()->AudioBufferSizeFor( + format->u.raw_audio.channel_count, + format->u.raw_audio.format, + format->u.raw_audio.frame_rate); + + string_for_format(*format, buf, sizeof(buf)); + printf("_SoundPlayNode::PrepareToConnect: output format %s\n", buf); // Now reserve the connection, and return information about it mOutput.destination = where; @@ -337,8 +353,7 @@ _SoundPlayNode::Connect(status_t error, const media_source& source, const media_ CALLED(); // is this our output? - if (source != mOutput.source) - { + if (source != mOutput.source) { TRACE("_SoundPlayNode::Connect returning\n"); return; } @@ -346,10 +361,10 @@ _SoundPlayNode::Connect(status_t error, const media_source& source, const media_ // 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) - { + if (error) { mOutput.destination = media_destination::null; - mOutput.format = mFormat; + mOutput.format.type = B_MEDIA_RAW_AUDIO; + mOutput.format.u.raw_audio = media_multi_audio_format::wildcard; return; } @@ -400,7 +415,8 @@ _SoundPlayNode::Disconnect(const media_source& what, const media_destination& wh if ((where == mOutput.destination) && (what == mOutput.source)) { mOutput.destination = media_destination::null; - mOutput.format = mFormat; + mOutput.format.type = B_MEDIA_RAW_AUDIO; + mOutput.format.u.raw_audio = media_multi_audio_format::wildcard; delete mBufferGroup; mBufferGroup = NULL; } diff --git a/src/kits/media/SoundPlayNode.h b/src/kits/media/SoundPlayNode.h index 1cff65cecb..ab2ed94b26 100644 --- a/src/kits/media/SoundPlayNode.h +++ b/src/kits/media/SoundPlayNode.h @@ -19,11 +19,11 @@ class _SoundPlayNode : public BBufferProducer, public BMediaEventLooper { public: - _SoundPlayNode(const char *name, const media_multi_audio_format *format, BSoundPlayer *player); + _SoundPlayNode(const char *name, BSoundPlayer *player); ~_SoundPlayNode(); bool IsPlaying(); - bigtime_t Latency(); + bigtime_t CurrentTime(); /*************************/ /* begin from BMediaNode */ diff --git a/src/kits/media/SoundPlayer.cpp b/src/kits/media/SoundPlayer.cpp index 88fffc2905..6d8d11cdd8 100644 --- a/src/kits/media/SoundPlayer.cpp +++ b/src/kits/media/SoundPlayer.cpp @@ -13,50 +13,47 @@ #include "SoundPlayNode.h" #include "SoundPlayer.h" +#define TRACE_SOUND_PLAYER +#ifdef TRACE_SOUND_PLAYER + #undef TRACE + #define TRACE printf +#endif + +#define atomic_read(a) atomic_or(a, 0) + // Flags used internally in BSoundPlayer enum { - F_HAS_DATA (1 << 0), - F_NODES_CONNECTED (1 << 1), + F_NODES_CONNECTED = (1 << 0), + F_HAS_DATA = (1 << 1), + F_IS_STARTED = (1 << 2), + F_MUST_RELEASE_MIXER = (1 << 3), }; -/************************************************************* - * public sound_error - *************************************************************/ - -//final -sound_error::sound_error(const char *str) -{ - CALLED(); - m_str_const = str; -} - -//final -const char * -sound_error::what() const -{ - CALLED(); - return m_str_const; -} /************************************************************* * public BSoundPlayer *************************************************************/ + BSoundPlayer::BSoundPlayer(const char * name, void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), void (*Notifier)(void *, sound_player_notification what, ...), void * cookie) { CALLED(); + + TRACE("BSoundPlayer::BSoundPlayer: default constructor used"); + media_multi_audio_format fmt = media_multi_audio_format::wildcard; - fmt.frame_rate = 44100.0f; +// fmt.frame_rate = 44100.0f; // fmt.channel_count = 2; - fmt.format = media_raw_audio_format::B_AUDIO_FLOAT; - fmt.byte_order = B_MEDIA_HOST_ENDIAN; - //fmt.buffer_size = 4096; +// fmt.format = media_raw_audio_format::B_AUDIO_FLOAT; +// fmt.byte_order = B_MEDIA_HOST_ENDIAN; +// fmt.buffer_size = 4096; Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie); } + BSoundPlayer::BSoundPlayer(const media_raw_audio_format * format, const char * name, void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), @@ -64,11 +61,15 @@ BSoundPlayer::BSoundPlayer(const media_raw_audio_format * format, void * cookie) { CALLED(); + + TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used"); + media_multi_audio_format fmt = media_multi_audio_format::wildcard; - fmt = *format; + *(media_raw_audio_format *)&fmt = *format; Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie); } + BSoundPlayer::BSoundPlayer(const media_node & toNode, const media_multi_audio_format * format, const char * name, @@ -78,55 +79,199 @@ BSoundPlayer::BSoundPlayer(const media_node & toNode, void * cookie) { CALLED(); + + TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used"); + if (toNode.kind & B_BUFFER_CONSUMER == 0) debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n"); + Init(&toNode, format, name, input, PlayBuffer, Notifier, cookie); } + +/************************************************************* + * private BSoundPlayer + *************************************************************/ + + +void +BSoundPlayer::Init( const media_node * node, + const media_multi_audio_format * format, + const char * name, + const media_input * input, + void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), + void (*Notifier)(void *, sound_player_notification what, ...), + void * cookie) +{ + CALLED(); + _m_sounds = NULL; // unused + _m_waiting = NULL; // unused + + fPlayerNode = NULL; + fPlayBufferFunc = PlayBuffer; + fNotifierFunc = Notifier; + fVolumeDB = 0.0f; + fCookie = cookie; + fFlags = 0; + fInitStatus = B_ERROR; + fParameterWeb = NULL; + fVolumeSlider = NULL; + fLastVolumeUpdate = 0; + + status_t err; + media_node timeSource; + media_node inputNode; + media_output _output; + media_input _input; + int32 inputCount; + int32 outputCount; + media_format tryFormat; + + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::Init: Couldn't get BMediaRoster\n"); + return; + } + + // The inputNode that our player node will be + // connected with is either supplied by the user + // or the system audio mixer + if (node) { + inputNode = *node; + } else { + err = roster->GetAudioMixer(&inputNode); + if (err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't GetAudioMixer\n"); + goto the_end; + } + fFlags |= F_MUST_RELEASE_MIXER; + } + + // Create the player node and register it + fPlayerNode = new _SoundPlayNode(name, this); + err = roster->RegisterNode(fPlayerNode); + if (err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't RegisterNode\n"); + goto the_end; + } + + // set the producer's time source to be the "default" time source, + // which the system audio mixer uses too. + err = roster->GetTimeSource(&timeSource); + if (err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't GetTimeSource\n"); + goto the_end; + } + err = roster->SetTimeSourceFor(fPlayerNode->Node().node, timeSource.node); + if (err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't SetTimeSourceFor\n"); + goto the_end; + } + + if (!input) { + err = roster->GetFreeInputsFor(inputNode, &_input, 1, &inputCount, B_MEDIA_RAW_AUDIO); + if (err != B_OK || inputCount < 1) { + TRACE("BSoundPlayer::Init: Couldn't GetFreeInputsFor\n"); + goto the_end; + } + } else { + _input = *input; + } + err = roster->GetFreeOutputsFor(fPlayerNode->Node(), &_output, 1, &outputCount, B_MEDIA_RAW_AUDIO); + if (err != B_OK || outputCount < 1) { + TRACE("BSoundPlayer::Init: Couldn't GetFreeOutputsFor\n"); + goto the_end; + } + + // Set an appropriate run mode for the producer + err = roster->SetRunModeNode(fPlayerNode->Node(), BMediaNode::B_INCREASE_LATENCY); + if (err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't SetRunModeNode\n"); + goto the_end; + } + + // setup our requested format (can still have many wildcards) + tryFormat.type = B_MEDIA_RAW_AUDIO; + tryFormat.u.raw_audio = *format; + + char buf[100]; + string_for_format(tryFormat, buf, sizeof(buf)); + TRACE("BSoundPlayer::Init: trying to connect with format %s\n", buf); + + // and connect the nodes + err = roster->Connect(_output.source, _input.destination, &tryFormat, &fMediaOutput, &fMediaInput); + if (err != B_OK) { + TRACE("BSoundPlayer::Init: Couldn't Connect\n"); + goto the_end; + } + + fFlags |= F_NODES_CONNECTED; + + get_volume_slider(); + + printf("BSoundPlayer node %ld has timesource %ld\n", fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node); + +the_end: + TRACE("BSoundPlayer::Init: %s\n", strerror(err)); + SetInitError(err); +} + + +/************************************************************* + * public BSoundPlayer + *************************************************************/ + + /* virtual */ BSoundPlayer::~BSoundPlayer() { CALLED(); - - F_NODES_CONNECTED - - - - if (fPlayerNode) { - 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(fMediaInput.node.node, fMediaInput.source, - fMediaOutput.node.node, fMediaOutput.destination); - if (err) { - fprintf(stderr, "* Error disconnecting nodes: %ld (%s)\n", err, strerror(err)); - } - fVolumeSlider = NULL; - - err = roster->ReleaseNode(fMediaInput.node); - if (err) { - fprintf(stderr, "* Error releasing input node: %ld (%s)\n", err, strerror(err)); - } - - err = roster->ReleaseNode(fMediaOutput.node); - if (err) { - fprintf(stderr, "* Error releasing output node: %ld (%s)\n", err, strerror(err)); - } - fPlayerNode = NULL; + if (fFlags & F_IS_STARTED) { + Stop(true, false); // block, but don't flush + } + + status_t err; + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n"); + goto cleanup; + } + + if (fFlags & F_NODES_CONNECTED) { + // Ordinarily we'd stop *all* of the nodes in the chain before disconnecting. However, + // our node is already stopped, and we can't stop the System Mixer. + // 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. + err = roster->Disconnect(fMediaInput.node.node, fMediaInput.source, + fMediaOutput.node.node, fMediaOutput.destination); + if (err) { + TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: %ld (%s)\n", err, strerror(err)); } } - delete [] _m_buf; + if (fFlags & F_MUST_RELEASE_MIXER) { + // Release the mixer as it was acquired + // through BMediaRoster::GetAudioMixer() + err = roster->ReleaseNode(fMediaInput.node); + if (err) { + TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: %ld (%s)\n", err, strerror(err)); + } + } + +cleanup: + + // Dispose of the player node + if (fPlayerNode) { + // We do not call BMediaRoster::ReleaseNode(), since + // the player was created by using "new". We could + // call BMediaRoster::UnregisterNode(), but this is + // supposed to be done by BMediaNode destructor automatically + delete fPlayerNode; + } + delete fParameterWeb; + // do not delete fVolumeSlider, it belonged to the parameter web } @@ -143,6 +288,12 @@ BSoundPlayer::Format() const { CALLED(); + if ((fFlags & F_NODES_CONNECTED) == 0) + return media_raw_audio_format::wildcard; + + return fPlayerNode->Format(); + +#if 0 media_raw_audio_format temp = media_raw_audio_format::wildcard; if (fPlayerNode) { @@ -152,6 +303,7 @@ BSoundPlayer::Format() const } return temp; +#endif } @@ -159,24 +311,33 @@ status_t BSoundPlayer::Start() { CALLED(); + + if ((fFlags & F_NODES_CONNECTED) == 0) + return B_NO_INIT; - if (!fPlayerNode) - return B_ERROR; + if (fFlags & F_IS_STARTED) + return B_OK; BMediaRoster *roster = BMediaRoster::Roster(); if (!roster) { TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n"); return B_ERROR; } - - // 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(fPlayerNode->Node(), &latency); - err = roster->StartNode(fPlayerNode->Node(), fPlayerNode->TimeSource()->Now() + latency + 5000); + // Add latency and a few ms to the nodes current time to + // make sure that we give the producer enough time to run + // buffers through the node chain, otherwise it'll start + // up already late - return err; + status_t err = roster->StartNode(fPlayerNode->Node(), fPlayerNode->TimeSource()->Now() + Latency() + 5000); + if (err != B_OK) { + TRACE("BSoundPlayer::Start: StartNode failed, %ld", err); + return err; + } + + atomic_or(&fFlags, F_IS_STARTED); + + return B_OK; } @@ -186,21 +347,26 @@ BSoundPlayer::Stop(bool block, { CALLED(); - if (!fPlayerNode) - return; - - // XXX flush is ignored - TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush); - - BMediaRoster *roster = BMediaRoster::Roster(); - if (!roster) { - TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n"); + + if ((fFlags & F_NODES_CONNECTED) == 0) return; + + // XXX flush is ignored + + if (fFlags & F_IS_STARTED) { + + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n"); + return; + } + + roster->StopNode(fPlayerNode->Node(), 0, true); + + atomic_and(&fFlags, ~F_IS_STARTED); } - roster->StopNode(fPlayerNode->Node(), 0, true); - if (block) { // wait until the node is stopped int maxtrys; @@ -210,10 +376,56 @@ BSoundPlayer::Stop(bool block, DEBUG_ONLY(if (maxtrys == 0) printf("BSoundPlayer::Stop: waiting for node stop failed\n")); // wait until all buffers on the way to the physical output have been played - snooze(fPlayerNode->Latency() + 2000); + snooze(Latency() + 2000); } } + +bigtime_t +BSoundPlayer::Latency() +{ + CALLED(); + + if ((fFlags & F_NODES_CONNECTED) == 0) + return 0; + + BMediaRoster *roster = BMediaRoster::Roster(); + if (!roster) { + TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n"); + return 0; + } + + bigtime_t latency; + status_t err = roster->GetLatencyFor(fMediaOutput.node, &latency); + if (err != B_OK) { + TRACE("BSoundPlayer::Latency: GetLatencyFor failed %ld (%s)\n", err, strerror(err)); + return 0; + } + + return latency; +} + + +void +BSoundPlayer::SetHasData(bool has_data) +{ + CALLED(); + if (has_data) + atomic_or(&fFlags, F_HAS_DATA); + else + atomic_and(&fFlags, ~F_HAS_DATA); +} + + +/* virtual */ bool +BSoundPlayer::HasData() +{ + CALLED(); + return (atomic_read(&fFlags) & F_HAS_DATA) != 0; +} + + + BSoundPlayer::BufferPlayerFunc BSoundPlayer::BufferPlayer() const { @@ -221,7 +433,9 @@ BSoundPlayer::BufferPlayer() const return fPlayBufferFunc; } -void BSoundPlayer::SetBufferPlayer(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format)) + +void +BSoundPlayer::SetBufferPlayer(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format)) { CALLED(); fLocker.Lock(); @@ -229,6 +443,7 @@ void BSoundPlayer::SetBufferPlayer(void (*PlayBuffer)(void *, void * buffer, siz fLocker.Unlock(); } + BSoundPlayer::EventNotifierFunc BSoundPlayer::EventNotifier() const { @@ -236,6 +451,7 @@ BSoundPlayer::EventNotifier() const return fNotifierFunc; } + void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notification what, ...)) { CALLED(); @@ -244,6 +460,7 @@ void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notificatio fLocker.Unlock(); } + void * BSoundPlayer::Cookie() const { @@ -251,6 +468,7 @@ BSoundPlayer::Cookie() const return fCookie; } + void BSoundPlayer::SetCookie(void *cookie) { @@ -260,9 +478,11 @@ BSoundPlayer::SetCookie(void *cookie) fLocker.Unlock(); } -void BSoundPlayer::SetCallbacks(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), - void (*Notifier)(void *, sound_player_notification what, ...), - void * cookie) + +void +BSoundPlayer::SetCallbacks(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), + void (*Notifier)(void *, sound_player_notification what, ...), + void * cookie) { CALLED(); fLocker.Lock(); @@ -273,22 +493,29 @@ void BSoundPlayer::SetCallbacks(void (*PlayBuffer)(void *, void * buffer, size_t } +/* The BeBook is inaccurate about the meaning of this function. + * The probably best interpretation is to return the time that + * has elapsed since playing was started, whichs seems to match + * " CurrentTime() returns the current media time " + */ bigtime_t BSoundPlayer::CurrentTime() { - CALLED(); - if (!fPlayerNode) - return system_time(); // XXX wrong + if ((fFlags & F_NODES_CONNECTED) == 0) + return 0; - return fPlayerNode->TimeSource()->Now(); // XXX wrong + return fPlayerNode->CurrentTime(); } +/* Returns the current performance time of the sound player node + * being used by the BSoundPlayer. Will return B_ERROR if the + * BSoundPlayer object hasn't been properly initialized. + */ bigtime_t BSoundPlayer::PerformanceTime() { - CALLED(); - if (!fPlayerNode) + if ((fFlags & F_NODES_CONNECTED) == 0) return (bigtime_t) B_ERROR; return fPlayerNode->TimeSource()->Now(); @@ -300,6 +527,9 @@ BSoundPlayer::Preroll() { CALLED(); + if ((fFlags & F_NODES_CONNECTED) == 0) + return B_NO_INIT; + BMediaRoster *roster = BMediaRoster::Roster(); if (!roster) { TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n"); @@ -307,12 +537,12 @@ BSoundPlayer::Preroll() } status_t err = roster->PrerollNode(fMediaOutput.node); - if (err != B_OK) { - fprintf(stderr, "Error while PrerollNode: %ld (%s)\n", err, strerror(err)); + TRACE("BSoundPlayer::Preroll: Error while PrerollNode: %ld (%s)\n", err, strerror(err)); + return err; } - return err; + return B_OK; } @@ -394,17 +624,17 @@ BSoundPlayer::VolumeDB(bool forcePoll) { CALLED(); if (!fVolumeSlider) - return 0.0f; + return -94.0f; // silence if (!forcePoll && (system_time() - fLastVolumeUpdate < 500000)) - return fVolume; + return fVolumeDB; int32 count = fVolumeSlider->CountChannels(); float values[count]; size_t size = count * sizeof(float); fVolumeSlider->GetValue(&values, &size, NULL); fLastVolumeUpdate = system_time(); - fVolume = values[0]; + fVolumeDB = values[0]; return values[0]; } @@ -430,7 +660,7 @@ BSoundPlayer::SetVolumeDB(float volume_dB) values[i] = volume_dB; fVolumeSlider->SetValue(values, sizeof(float) * count, 0); - fVolume = volume_dB; + fVolumeDB = volume_dB; fLastVolumeUpdate = system_time(); } @@ -457,54 +687,12 @@ BSoundPlayer::GetVolumeInfo(media_node *out_node, } -bigtime_t -BSoundPlayer::Latency() -{ - CALLED(); - if (fInitStatus != B_OK) - return B_NO_INIT; - - BMediaRoster *roster = BMediaRoster::Roster(); - if (!roster) { - TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n"); - return 0; - } - - bigtime_t latency; - status_t err = roster->GetLatencyFor(fMediaOutput.node, &latency); - if (err != B_OK) { - TRACE("BSoundPlayer::Latency: GetLatencyFor failed %ld (%s)\n", err, strerror(err)); - return 0; - } - - return latency; -} - - -/* virtual */ bool -BSoundPlayer::HasData() -{ - CALLED(); - return (atomic_read(&fFlags) & F_HAS_DATA) != 0; -} - - -void -BSoundPlayer::SetHasData(bool has_data) -{ - CALLED(); - if (has_data) - atomic_or(&fFlags, F_HAS_DATA); - else - atomic_and(&fFlags, ~F_HAS_DATA); -} - /************************************************************* * protected BSoundPlayer *************************************************************/ -//final + void BSoundPlayer::SetInitError(status_t in_error) { @@ -551,10 +739,10 @@ BSoundPlayer::get_volume_slider() int count = fParameterWeb->CountParameters(); for (int i = 0; i < count; i++) { - BParameter *parameter = web->ParameterAt(i); + BParameter *parameter = fParameterWeb->ParameterAt(i); if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER) continue; - if (parameter->ID() >> 16) != fMediaInput.destination.id) + if ((parameter->ID() >> 16) != fMediaInput.destination.id) continue; if (strcmp(parameter->Kind(), B_GAIN) != 0) continue; @@ -563,151 +751,9 @@ BSoundPlayer::get_volume_slider() } } -void -BSoundPlayer::Init( - const media_node * node, - const media_multi_audio_format * format, - const char * name, - const media_input * input, - void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), - void (*Notifier)(void *, sound_player_notification what, ...), - void * cookie) -{ - CALLED(); - fPlayerNode = NULL; - _m_sounds = NULL; - _m_waiting = NULL; - fPlayBufferFunc = PlayBuffer; - fNotifierFunc = Notifier; - fVolume = 0.0f; - _m_mix_buffer = 0; - _m_mix_buffer_size = 0; - fCookie = cookie; - _m_buf = NULL; - _m_bufsize = 0; - fFlags = 0; - fInitStatus = B_ERROR; - _m_perfTime = 0; - fVolumeSlider = NULL; - fParameterWeb = NULL; - fLastVolumeUpdate = 0; - - fPlayerNode = 0; - - status_t err; - media_node mixerNode; - 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; - } - - //connect our producer node either to the - //system mixer or to the supplied out node - if (!node) { - err = roster->GetAudioMixer(&mixerNode); - if (err != B_OK) { - TRACE("BSoundPlayer::Init: Couldn't GetAudioMixer\n"); - goto the_end; - } - node = &mixerNode; - } - - memcpy(&fmt,format,sizeof(fmt)); - - if (fmt.frame_rate == media_multi_audio_format::wildcard.frame_rate) - fmt.frame_rate = 44100.0f; - if (fmt.channel_count == media_multi_audio_format::wildcard.channel_count) - fmt.channel_count = 2; - if (fmt.format == media_multi_audio_format::wildcard.format) - fmt.format = media_raw_audio_format::B_AUDIO_FLOAT; - if (fmt.byte_order == media_multi_audio_format::wildcard.byte_order) - fmt.byte_order = B_MEDIA_HOST_ENDIAN; - if (fmt.buffer_size == media_multi_audio_format::wildcard.buffer_size) - fmt.buffer_size = 4096; - - if (fmt.channel_count != 1 && fmt.channel_count != 2) - ERROR("BSoundPlayer: not a 1 or 2 channel audio format\n"); - if (fmt.frame_rate <= 0.0f) - ERROR("BSoundPlayer: framerate must be > 0\n"); - - _m_bufsize = fmt.buffer_size; - _m_buf = new char[_m_bufsize]; - fPlayerNode = new _SoundPlayNode(name,&fmt,this); - - err = roster->RegisterNode(fPlayerNode); - if (err != B_OK) { - TRACE("BSoundPlayer::Init: Couldn't RegisterNode\n"); - goto the_end; - } - - // set the producer's time source to be the "default" time source, which - // the Mixer uses too. - - err = roster->GetTimeSource(&timeSource); - if (err != B_OK) { - TRACE("BSoundPlayer::Init: Couldn't GetTimeSource\n"); - goto the_end; - } - err = roster->SetTimeSourceFor(fPlayerNode->Node().node, timeSource.node); - if (err != B_OK) { - TRACE("BSoundPlayer::Init: Couldn't SetTimeSourceFor\n"); - goto the_end; - } - - 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(fPlayerNode->Node(), &_output, 1, &outputCount, B_MEDIA_RAW_AUDIO); - if (err != B_OK) { - TRACE("BSoundPlayer::Init: Couldn't GetFreeOutputsFor\n"); - goto the_end; - } - - // Set an appropriate run mode for the producer - err = roster->SetRunModeNode(fPlayerNode->Node(), BMediaNode::B_INCREASE_LATENCY); - if (err != B_OK) { - TRACE("BSoundPlayer::Init: Couldn't SetRunModeNode\n"); - goto the_end; - } - - //tryFormat.type = B_MEDIA_RAW_AUDIO; - //tryformat.fileAudioOutput.format; - tryFormat = _output.format; - err = roster->Connect(_output.source, _input.destination, &tryFormat, &fMediaOutput, &fMediaInput); - if (err != B_OK) { - TRACE("BSoundPlayer::Init: Couldn't Connect\n"); - goto the_end; - } - - get_volume_slider(); - - - printf("BSoundPlayer node %ld has timesource %ld\n", fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node); - -the_end: - TRACE("BSoundPlayer::Init: %s\n", strerror(err)); - SetInitError(err); -} - /* virtual */ void -BSoundPlayer::Notify(sound_player_notification what, - ...) +BSoundPlayer::Notify(sound_player_notification what, ...) { CALLED(); if (fLocker.Lock()) { @@ -719,9 +765,7 @@ BSoundPlayer::Notify(sound_player_notification what, /* virtual */ void -BSoundPlayer::PlayBuffer(void *buffer, - size_t size, - const media_raw_audio_format &format) +BSoundPlayer::PlayBuffer(void *buffer, size_t size, const media_raw_audio_format &format) { if (fLocker.Lock()) { if (fPlayBufferFunc) @@ -729,3 +773,24 @@ BSoundPlayer::PlayBuffer(void *buffer, fLocker.Unlock(); } } + + +/************************************************************* + * public sound_error + *************************************************************/ + + +sound_error::sound_error(const char *str) +{ + CALLED(); + m_str_const = str; +} + + +const char * +sound_error::what() const +{ + CALLED(); + return m_str_const; +} +