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
This commit is contained in:
beveloper
2004-10-24 00:16:27 +00:00
parent d3f88ee1ed
commit df42a7b539
4 changed files with 437 additions and 361 deletions
+4 -9
View File
@@ -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 (*fPlayBufferFunc)(void * cookie, void * buffer, size_t size, const media_raw_audio_format & format);
void (*fNotifierFunc)(void * cookie, sound_player_notification what, ...); void (*fNotifierFunc)(void * cookie, sound_player_notification what, ...);
BLocker fLocker; BLocker fLocker;
float fVolume; float fVolumeDB;
media_input fMediaInput; media_input fMediaInput; // the system mixer
media_output fMediaOutput; media_output fMediaOutput; // the player node
float * _m_mix_buffer;
size_t _m_mix_buffer_size;
void * fCookie; void * fCookie;
void * _m_buf;
size_t _m_bufsize;
int32 fFlags; int32 fFlags;
status_t fInitStatus; // new in R4.1 status_t fInitStatus; // new in R4.1
bigtime_t _m_perfTime;
BContinuousParameter * fVolumeSlider; BContinuousParameter * fVolumeSlider;
bigtime_t fLastVolumeUpdate; bigtime_t fLastVolumeUpdate;
BParameterWeb *fParameterWeb; BParameterWeb *fParameterWeb;
uint32 _m_reserved[9]; uint32 _m_reserved[15];
void NotifySoundDone( void NotifySoundDone(
play_id sound, play_id sound,
+74 -58
View File
@@ -7,6 +7,7 @@
***********************************************************************/ ***********************************************************************/
#include <TimeSource.h> #include <TimeSource.h>
#include <MediaRoster.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@@ -25,7 +26,7 @@
#define SEND_NEW_BUFFER_EVENT (BTimedEventQueue::B_USER_EVENT + 1) #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), BMediaNode(name),
BBufferProducer(B_MEDIA_RAW_AUDIO), BBufferProducer(B_MEDIA_RAW_AUDIO),
BMediaEventLooper(), BMediaEventLooper(),
@@ -37,28 +38,8 @@ _SoundPlayNode::_SoundPlayNode(const char *name, const media_multi_audio_format
mTooEarlyCount(0) mTooEarlyCount(0)
{ {
CALLED(); CALLED();
mFormat.type = B_MEDIA_RAW_AUDIO; mOutput.format.type = B_MEDIA_RAW_AUDIO;
mFormat.u.raw_audio = *format; mOutput.format.u.raw_audio = media_multi_audio_format::wildcard;
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;
}
} }
@@ -75,16 +56,16 @@ _SoundPlayNode::IsPlaying()
} }
bigtime_t 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 media_multi_audio_format
_SoundPlayNode::Format() const _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); 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.destination = media_destination::null;
mOutput.source.port = ControlPort(); mOutput.source.port = ControlPort();
mOutput.source.id = 0; mOutput.source.id = 0;
@@ -168,7 +150,8 @@ _SoundPlayNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, me
return B_MEDIA_BAD_FORMAT; return B_MEDIA_BAD_FORMAT;
// this is the format we'll be returning (our preferred 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; return B_OK;
} }
@@ -186,16 +169,21 @@ _SoundPlayNode::FormatProposal(const media_source& output, media_format* format)
return B_MEDIA_BAD_SOURCE; return B_MEDIA_BAD_SOURCE;
} }
// we only support floating-point raw audio, so we always return that, but we // if wildcard, change it to raw audio
// supply an error code depending on whether we found the proposal acceptable. if (format->type == B_MEDIA_UNKNOWN_TYPE)
media_type requestedType = format->type; format->type = B_MEDIA_RAW_AUDIO;
*format = mFormat;
if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != 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"); TRACE("_SoundPlayNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT; 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 status_t
@@ -291,8 +279,7 @@ _SoundPlayNode::PrepareToConnect(const media_source& what, const media_destinati
CALLED(); CALLED();
// is this our output? // is this our output?
if (what != mOutput.source) if (what != mOutput.source) {
{
TRACE("_SoundPlayNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n"); TRACE("_SoundPlayNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE; return B_MEDIA_BAD_SOURCE;
} }
@@ -304,24 +291,53 @@ _SoundPlayNode::PrepareToConnect(const media_source& what, const media_destinati
// the format may not yet be fully specialized (the consumer might have // 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. // error if we don't support the requested format.
if (format->type != B_MEDIA_RAW_AUDIO)
{ char buf[100];
TRACE("\tnon-raw-audio format?!\n"); 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; 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 // the haiku mixer might have a hint
if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) // for us, so check for it
{ #define FORMAT_USER_DATA_TYPE 0x7294a8f3
format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest #define FORMAT_USER_DATA_MAGIC_1 0xc84173bd
TRACE("\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size); #define FORMAT_USER_DATA_MAGIC_2 0x4af62b7d
} uint32 channel_count = 0;
else float frame_rate = 0;
{ if (format->user_data_type == FORMAT_USER_DATA_TYPE
TRACE("\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size); && *(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 // Now reserve the connection, and return information about it
mOutput.destination = where; mOutput.destination = where;
@@ -337,8 +353,7 @@ _SoundPlayNode::Connect(status_t error, const media_source& source, const media_
CALLED(); CALLED();
// is this our output? // is this our output?
if (source != mOutput.source) if (source != mOutput.source) {
{
TRACE("_SoundPlayNode::Connect returning\n"); TRACE("_SoundPlayNode::Connect returning\n");
return; 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 // 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 // error code. When that happens we simply unreserve the connection and do
// nothing else. // nothing else.
if (error) if (error) {
{
mOutput.destination = media_destination::null; 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; return;
} }
@@ -400,7 +415,8 @@ _SoundPlayNode::Disconnect(const media_source& what, const media_destination& wh
if ((where == mOutput.destination) && (what == mOutput.source)) if ((where == mOutput.destination) && (what == mOutput.source))
{ {
mOutput.destination = media_destination::null; 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; delete mBufferGroup;
mBufferGroup = NULL; mBufferGroup = NULL;
} }
+2 -2
View File
@@ -19,11 +19,11 @@ class _SoundPlayNode
: public BBufferProducer, public BMediaEventLooper : public BBufferProducer, public BMediaEventLooper
{ {
public: public:
_SoundPlayNode(const char *name, const media_multi_audio_format *format, BSoundPlayer *player); _SoundPlayNode(const char *name, BSoundPlayer *player);
~_SoundPlayNode(); ~_SoundPlayNode();
bool IsPlaying(); bool IsPlaying();
bigtime_t Latency(); bigtime_t CurrentTime();
/*************************/ /*************************/
/* begin from BMediaNode */ /* begin from BMediaNode */
+327 -262
View File
@@ -13,50 +13,47 @@
#include "SoundPlayNode.h" #include "SoundPlayNode.h"
#include "SoundPlayer.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 // Flags used internally in BSoundPlayer
enum { enum {
F_HAS_DATA (1 << 0), F_NODES_CONNECTED = (1 << 0),
F_NODES_CONNECTED (1 << 1), 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 * public BSoundPlayer
*************************************************************/ *************************************************************/
BSoundPlayer::BSoundPlayer(const char * name, BSoundPlayer::BSoundPlayer(const char * name,
void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
void (*Notifier)(void *, sound_player_notification what, ...), void (*Notifier)(void *, sound_player_notification what, ...),
void * cookie) void * cookie)
{ {
CALLED(); CALLED();
TRACE("BSoundPlayer::BSoundPlayer: default constructor used");
media_multi_audio_format fmt = media_multi_audio_format::wildcard; 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.channel_count = 2;
fmt.format = media_raw_audio_format::B_AUDIO_FLOAT; // fmt.format = media_raw_audio_format::B_AUDIO_FLOAT;
fmt.byte_order = B_MEDIA_HOST_ENDIAN; // fmt.byte_order = B_MEDIA_HOST_ENDIAN;
//fmt.buffer_size = 4096; // fmt.buffer_size = 4096;
Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie); Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie);
} }
BSoundPlayer::BSoundPlayer(const media_raw_audio_format * format, BSoundPlayer::BSoundPlayer(const media_raw_audio_format * format,
const char * name, const char * name,
void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format), 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) void * cookie)
{ {
CALLED(); CALLED();
TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used");
media_multi_audio_format fmt = media_multi_audio_format::wildcard; 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); Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie);
} }
BSoundPlayer::BSoundPlayer(const media_node & toNode, BSoundPlayer::BSoundPlayer(const media_node & toNode,
const media_multi_audio_format * format, const media_multi_audio_format * format,
const char * name, const char * name,
@@ -78,55 +79,199 @@ BSoundPlayer::BSoundPlayer(const media_node & toNode,
void * cookie) void * cookie)
{ {
CALLED(); CALLED();
TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used");
if (toNode.kind & B_BUFFER_CONSUMER == 0) if (toNode.kind & B_BUFFER_CONSUMER == 0)
debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n"); debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n");
Init(&toNode, format, name, input, PlayBuffer, Notifier, cookie); 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 */ /* virtual */
BSoundPlayer::~BSoundPlayer() BSoundPlayer::~BSoundPlayer()
{ {
CALLED(); CALLED();
F_NODES_CONNECTED if (fFlags & F_IS_STARTED) {
Stop(true, false); // block, but don't flush
}
status_t err;
if (fPlayerNode) {
BMediaRoster *roster = BMediaRoster::Roster(); BMediaRoster *roster = BMediaRoster::Roster();
if (!roster) { if (!roster) {
TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n"); TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n");
} else { goto cleanup;
status_t err; }
// Ordinarily we'd stop *all* of the nodes in the chain at this point. However, if (fFlags & F_NODES_CONNECTED) {
// one of the nodes is the System Mixer, and stopping the Mixer is a Bad Idea (tm). // 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 // 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. // 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, err = roster->Disconnect(fMediaInput.node.node, fMediaInput.source,
fMediaOutput.node.node, fMediaOutput.destination); fMediaOutput.node.node, fMediaOutput.destination);
if (err) { if (err) {
fprintf(stderr, "* Error disconnecting nodes: %ld (%s)\n", err, strerror(err)); TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: %ld (%s)\n", err, strerror(err));
}
} }
fVolumeSlider = NULL;
if (fFlags & F_MUST_RELEASE_MIXER) {
// Release the mixer as it was acquired
// through BMediaRoster::GetAudioMixer()
err = roster->ReleaseNode(fMediaInput.node); err = roster->ReleaseNode(fMediaInput.node);
if (err) { if (err) {
fprintf(stderr, "* Error releasing input node: %ld (%s)\n", err, strerror(err)); TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: %ld (%s)\n", err, strerror(err));
}
} }
err = roster->ReleaseNode(fMediaOutput.node); cleanup:
if (err) {
fprintf(stderr, "* Error releasing output node: %ld (%s)\n", err, strerror(err)); // 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;
} }
fPlayerNode = NULL;
}
}
delete [] _m_buf;
delete fParameterWeb; delete fParameterWeb;
// do not delete fVolumeSlider, it belonged to the parameter web
} }
@@ -143,6 +288,12 @@ BSoundPlayer::Format() const
{ {
CALLED(); 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; media_raw_audio_format temp = media_raw_audio_format::wildcard;
if (fPlayerNode) { if (fPlayerNode) {
@@ -152,6 +303,7 @@ BSoundPlayer::Format() const
} }
return temp; return temp;
#endif
} }
@@ -160,8 +312,11 @@ BSoundPlayer::Start()
{ {
CALLED(); CALLED();
if (!fPlayerNode) if ((fFlags & F_NODES_CONNECTED) == 0)
return B_ERROR; return B_NO_INIT;
if (fFlags & F_IS_STARTED)
return B_OK;
BMediaRoster *roster = BMediaRoster::Roster(); BMediaRoster *roster = BMediaRoster::Roster();
if (!roster) { if (!roster) {
@@ -169,14 +324,20 @@ BSoundPlayer::Start()
return B_ERROR; return B_ERROR;
} }
// make sure we give the producer enough time to run buffers through // Add latency and a few ms to the nodes current time to
// the node chain, otherwise it'll start up already late // make sure that we give the producer enough time to run
bigtime_t latency = 0; // buffers through the node chain, otherwise it'll start
status_t err = roster->GetLatencyFor(fPlayerNode->Node(), &latency); // up already late
err = roster->StartNode(fPlayerNode->Node(), fPlayerNode->TimeSource()->Now() + latency + 5000);
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; return err;
}
atomic_or(&fFlags, F_IS_STARTED);
return B_OK;
} }
@@ -186,12 +347,14 @@ BSoundPlayer::Stop(bool block,
{ {
CALLED(); CALLED();
if (!fPlayerNode) TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);
if ((fFlags & F_NODES_CONNECTED) == 0)
return; return;
// XXX flush is ignored // XXX flush is ignored
TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush); if (fFlags & F_IS_STARTED) {
BMediaRoster *roster = BMediaRoster::Roster(); BMediaRoster *roster = BMediaRoster::Roster();
if (!roster) { if (!roster) {
@@ -201,6 +364,9 @@ BSoundPlayer::Stop(bool block,
roster->StopNode(fPlayerNode->Node(), 0, true); roster->StopNode(fPlayerNode->Node(), 0, true);
atomic_and(&fFlags, ~F_IS_STARTED);
}
if (block) { if (block) {
// wait until the node is stopped // wait until the node is stopped
int maxtrys; int maxtrys;
@@ -210,10 +376,56 @@ BSoundPlayer::Stop(bool block,
DEBUG_ONLY(if (maxtrys == 0) printf("BSoundPlayer::Stop: waiting for node stop failed\n")); 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 // 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::BufferPlayerFunc
BSoundPlayer::BufferPlayer() const BSoundPlayer::BufferPlayer() const
{ {
@@ -221,7 +433,9 @@ BSoundPlayer::BufferPlayer() const
return fPlayBufferFunc; 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(); CALLED();
fLocker.Lock(); fLocker.Lock();
@@ -229,6 +443,7 @@ void BSoundPlayer::SetBufferPlayer(void (*PlayBuffer)(void *, void * buffer, siz
fLocker.Unlock(); fLocker.Unlock();
} }
BSoundPlayer::EventNotifierFunc BSoundPlayer::EventNotifierFunc
BSoundPlayer::EventNotifier() const BSoundPlayer::EventNotifier() const
{ {
@@ -236,6 +451,7 @@ BSoundPlayer::EventNotifier() const
return fNotifierFunc; return fNotifierFunc;
} }
void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notification what, ...)) void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notification what, ...))
{ {
CALLED(); CALLED();
@@ -244,6 +460,7 @@ void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notificatio
fLocker.Unlock(); fLocker.Unlock();
} }
void * void *
BSoundPlayer::Cookie() const BSoundPlayer::Cookie() const
{ {
@@ -251,6 +468,7 @@ BSoundPlayer::Cookie() const
return fCookie; return fCookie;
} }
void void
BSoundPlayer::SetCookie(void *cookie) BSoundPlayer::SetCookie(void *cookie)
{ {
@@ -260,7 +478,9 @@ BSoundPlayer::SetCookie(void *cookie)
fLocker.Unlock(); fLocker.Unlock();
} }
void BSoundPlayer::SetCallbacks(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
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 (*Notifier)(void *, sound_player_notification what, ...),
void * cookie) void * cookie)
{ {
@@ -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 bigtime_t
BSoundPlayer::CurrentTime() BSoundPlayer::CurrentTime()
{ {
CALLED(); if ((fFlags & F_NODES_CONNECTED) == 0)
if (!fPlayerNode) return 0;
return system_time(); // XXX wrong
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 bigtime_t
BSoundPlayer::PerformanceTime() BSoundPlayer::PerformanceTime()
{ {
CALLED(); if ((fFlags & F_NODES_CONNECTED) == 0)
if (!fPlayerNode)
return (bigtime_t) B_ERROR; return (bigtime_t) B_ERROR;
return fPlayerNode->TimeSource()->Now(); return fPlayerNode->TimeSource()->Now();
@@ -300,6 +527,9 @@ BSoundPlayer::Preroll()
{ {
CALLED(); CALLED();
if ((fFlags & F_NODES_CONNECTED) == 0)
return B_NO_INIT;
BMediaRoster *roster = BMediaRoster::Roster(); BMediaRoster *roster = BMediaRoster::Roster();
if (!roster) { if (!roster) {
TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n"); TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n");
@@ -307,12 +537,12 @@ BSoundPlayer::Preroll()
} }
status_t err = roster->PrerollNode(fMediaOutput.node); status_t err = roster->PrerollNode(fMediaOutput.node);
if (err != B_OK) { 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(); CALLED();
if (!fVolumeSlider) if (!fVolumeSlider)
return 0.0f; return -94.0f; // silence
if (!forcePoll && (system_time() - fLastVolumeUpdate < 500000)) if (!forcePoll && (system_time() - fLastVolumeUpdate < 500000))
return fVolume; return fVolumeDB;
int32 count = fVolumeSlider->CountChannels(); int32 count = fVolumeSlider->CountChannels();
float values[count]; float values[count];
size_t size = count * sizeof(float); size_t size = count * sizeof(float);
fVolumeSlider->GetValue(&values, &size, NULL); fVolumeSlider->GetValue(&values, &size, NULL);
fLastVolumeUpdate = system_time(); fLastVolumeUpdate = system_time();
fVolume = values[0]; fVolumeDB = values[0];
return values[0]; return values[0];
} }
@@ -430,7 +660,7 @@ BSoundPlayer::SetVolumeDB(float volume_dB)
values[i] = volume_dB; values[i] = volume_dB;
fVolumeSlider->SetValue(values, sizeof(float) * count, 0); fVolumeSlider->SetValue(values, sizeof(float) * count, 0);
fVolume = volume_dB; fVolumeDB = volume_dB;
fLastVolumeUpdate = system_time(); 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 * protected BSoundPlayer
*************************************************************/ *************************************************************/
//final
void void
BSoundPlayer::SetInitError(status_t in_error) BSoundPlayer::SetInitError(status_t in_error)
{ {
@@ -551,10 +739,10 @@ BSoundPlayer::get_volume_slider()
int count = fParameterWeb->CountParameters(); int count = fParameterWeb->CountParameters();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
BParameter *parameter = web->ParameterAt(i); BParameter *parameter = fParameterWeb->ParameterAt(i);
if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER) if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
continue; continue;
if (parameter->ID() >> 16) != fMediaInput.destination.id) if ((parameter->ID() >> 16) != fMediaInput.destination.id)
continue; continue;
if (strcmp(parameter->Kind(), B_GAIN) != 0) if (strcmp(parameter->Kind(), B_GAIN) != 0)
continue; 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 /* virtual */ void
BSoundPlayer::Notify(sound_player_notification what, BSoundPlayer::Notify(sound_player_notification what, ...)
...)
{ {
CALLED(); CALLED();
if (fLocker.Lock()) { if (fLocker.Lock()) {
@@ -719,9 +765,7 @@ BSoundPlayer::Notify(sound_player_notification what,
/* virtual */ void /* virtual */ void
BSoundPlayer::PlayBuffer(void *buffer, BSoundPlayer::PlayBuffer(void *buffer, size_t size, const media_raw_audio_format &format)
size_t size,
const media_raw_audio_format &format)
{ {
if (fLocker.Lock()) { if (fLocker.Lock()) {
if (fPlayBufferFunc) if (fPlayBufferFunc)
@@ -729,3 +773,24 @@ BSoundPlayer::PlayBuffer(void *buffer,
fLocker.Unlock(); 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;
}