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

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 (*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,

View File

@ -7,6 +7,7 @@
***********************************************************************/
#include <TimeSource.h>
#include <MediaRoster.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
@ -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;
}

View File

@ -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 */

View File

@ -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;
}