* Implement basic support for the BSound parts of BSoundPlayer. Only one sound

at a time is supported and volume as well as start time are not taken into
  account. For reasons I don't feel like investigating right now all the
  BMediaTrack calls in the BTrackReader end up as pure virtual function calls
  though, so it's unusable for now.
* Fix leaking the temporary buffer in BSound.
* Whitespace cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29374 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2009-03-01 23:22:30 +00:00
parent 0b2267c921
commit 9b9aa75aac
3 changed files with 211 additions and 74 deletions

View File

@ -123,6 +123,8 @@ protected:
status_t in_error);
private:
static void _SoundPlayBufferFunc(void *cookie, void *buffer, size_t size,
const media_raw_audio_format &format);
virtual status_t _Reserved_SoundPlayer_0(void *, ...);
virtual status_t _Reserved_SoundPlayer_1(void *, ...);
@ -134,16 +136,19 @@ virtual status_t _Reserved_SoundPlayer_6(void *, ...);
virtual status_t _Reserved_SoundPlayer_7(void *, ...);
_SoundPlayNode * fPlayerNode;
struct _playing_sound {
_playing_sound * next;
off_t cur_offset;
BSound * sound;
_playing_sound *next;
off_t current_offset;
BSound *sound;
play_id id;
int32 delta;
int32 rate;
sem_id wait_sem;
float volume;
};
_playing_sound * _m_sounds;
_playing_sound *fPlayingSounds;
struct _waiting_sound {
_waiting_sound * next;
bigtime_t start_time;
@ -152,7 +157,8 @@ virtual status_t _Reserved_SoundPlayer_7(void *, ...);
int32 rate;
float volume;
};
_waiting_sound * _m_waiting;
_waiting_sound *fWaitingSounds;
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;

View File

@ -207,6 +207,8 @@ BSound::GetDataAt(off_t offset, void *intoBuffer, size_t bufferSize,
indirectSize - bufferOffset);
if (outUsed != NULL)
*outUsed = indirectSize - bufferOffset;
free(buffer);
} else if (outUsed != NULL)
*outUsed = 0;

View File

@ -1,11 +1,12 @@
/***********************************************************************
* AUTHOR: Marcus Overhagen, Jérôme Duval
* FILE: SoundPlayer.cpp
* DESCR:
* DESCR:
***********************************************************************/
#include <TimeSource.h>
#include <MediaRoster.h>
#include <ParameterWeb.h>
#include <Sound.h>
#include <math.h>
#include <string.h>
@ -24,6 +25,9 @@ enum {
};
static BSoundPlayer::play_id sCurrentPlayID = 1;
/*************************************************************
* public BSoundPlayer
*************************************************************/
@ -44,7 +48,7 @@ BSoundPlayer::BSoundPlayer(const char * name,
}
BSoundPlayer::BSoundPlayer(const media_raw_audio_format * format,
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),
void (*Notifier)(void *, sound_player_notification what, ...),
@ -77,9 +81,9 @@ BSoundPlayer::BSoundPlayer(const media_node & toNode,
void * cookie)
{
CALLED();
TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used\n");
if ((toNode.kind & B_BUFFER_CONSUMER) == 0)
debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n");
@ -99,9 +103,9 @@ BSoundPlayer::BSoundPlayer(const media_node & toNode,
*************************************************************/
void
void
BSoundPlayer::Init( const media_node * node,
const media_multi_audio_format * format,
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),
@ -109,21 +113,26 @@ BSoundPlayer::Init( const media_node * node,
void * cookie)
{
CALLED();
_m_sounds = NULL; // unused
_m_waiting = NULL; // unused
fPlayingSounds = NULL;
fWaitingSounds = NULL;
fPlayerNode = NULL;
fPlayBufferFunc = PlayBuffer;
if (fPlayBufferFunc == NULL) {
fPlayBufferFunc = _SoundPlayBufferFunc;
fCookie = this;
} else
fCookie = cookie;
fNotifierFunc = Notifier;
fVolumeDB = 0.0f;
fCookie = cookie;
fFlags = 0;
fInitStatus = B_ERROR;
fParameterWeb = NULL;
fVolumeSlider = NULL;
fLastVolumeUpdate = 0;
status_t err;
status_t err;
media_node timeSource;
media_node inputNode;
media_output _output;
@ -159,7 +168,7 @@ BSoundPlayer::Init( const media_node * node,
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);
@ -172,7 +181,7 @@ BSoundPlayer::Init( const media_node * node,
TRACE("BSoundPlayer::Init: Couldn't SetTimeSourceFor\n");
goto the_end;
}
// find a free media_input
if (!input) {
err = roster->GetFreeInputsFor(inputNode, &_input, 1, &inputCount, B_MEDIA_RAW_AUDIO);
@ -211,7 +220,7 @@ BSoundPlayer::Init( const media_node * node,
// setup our requested format (can still have many wildcards)
tryFormat.type = B_MEDIA_RAW_AUDIO;
tryFormat.u.raw_audio = *format;
#if DEBUG > 0
char buf[100];
string_for_format(tryFormat, buf, sizeof(buf));
@ -226,9 +235,9 @@ BSoundPlayer::Init( const media_node * node,
}
fFlags |= F_NODES_CONNECTED;
get_volume_slider();
TRACE("BSoundPlayer node %ld has timesource %ld\n", fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node);
the_end:
@ -264,18 +273,18 @@ BSoundPlayer::~BSoundPlayer()
// 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(fMediaOutput, fMediaInput);
#if DEBUG >0
#if DEBUG >0
if (err) {
TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: %ld (%s)\n", err, strerror(err));
}
#endif
}
if (fFlags & F_MUST_RELEASE_MIXER) {
// Release the mixer as it was acquired
// through BMediaRoster::GetAudioMixer()
err = roster->ReleaseNode(fMediaInput.node);
#if DEBUG >0
#if DEBUG >0
if (err) {
TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: %ld (%s)\n", err, strerror(err));
}
@ -283,7 +292,7 @@ BSoundPlayer::~BSoundPlayer()
}
cleanup:
// Dispose of the player node
if (fPlayerNode) {
// We do not call BMediaRoster::ReleaseNode(), since
@ -325,7 +334,7 @@ BSoundPlayer::Start()
if ((fFlags & F_NODES_CONNECTED) == 0)
return B_NO_INIT;
if (fFlags & F_IS_STARTED)
return B_OK;
@ -340,11 +349,11 @@ BSoundPlayer::Start()
fPlayerNode->TimeSource()->RealTime());
}
// Add latency and a few ms to the nodes current time to
// 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
status_t err = roster->StartNode(fPlayerNode->Node(), fPlayerNode->TimeSource()->Now() + Latency() + 5000);
if (err != B_OK) {
TRACE("BSoundPlayer::Start: StartNode failed, %ld", err);
@ -353,10 +362,10 @@ BSoundPlayer::Start()
if (fNotifierFunc)
fNotifierFunc(fCookie, B_STARTED, this);
SetHasData(true);
atomic_or(&fFlags, F_IS_STARTED);
return B_OK;
}
@ -381,21 +390,21 @@ BSoundPlayer::Stop(bool block,
TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n");
return;
}
roster->StopNode(fPlayerNode->Node(), 0, true);
atomic_and(&fFlags, ~F_IS_STARTED);
}
if (block) {
// wait until the node is stopped
int maxtrys;
for (maxtrys = 250; fPlayerNode->IsPlaying() && maxtrys != 0; maxtrys--)
snooze(2000);
DEBUG_ONLY(if (maxtrys == 0) TRACE("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(Latency() + 2000);
}
@ -409,7 +418,7 @@ bigtime_t
BSoundPlayer::Latency()
{
CALLED();
if ((fFlags & F_NODES_CONNECTED) == 0)
return 0;
@ -418,7 +427,7 @@ BSoundPlayer::Latency()
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) {
@ -560,70 +569,155 @@ BSoundPlayer::Preroll()
TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n");
return B_ERROR;
}
status_t err = roster->PrerollNode(fMediaOutput.node);
if (err != B_OK) {
TRACE("BSoundPlayer::Preroll: Error while PrerollNode: %ld (%s)\n", err, strerror(err));
return err;
}
return B_OK;
}
BSoundPlayer::play_id
BSoundPlayer::StartPlaying(BSound *sound,
bigtime_t at_time)
BSoundPlayer::StartPlaying(BSound *sound, bigtime_t atTime)
{
UNIMPLEMENTED();
return 1;
return StartPlaying(sound, atTime, 1.0);
}
BSoundPlayer::play_id
BSoundPlayer::StartPlaying(BSound *sound,
bigtime_t at_time,
float with_volume)
BSoundPlayer::StartPlaying(BSound *sound, bigtime_t atTime, float withVolume)
{
UNIMPLEMENTED();
return 1;
CALLED();
// TODO: support the at_time and with_volume parameters
_playing_sound *item = (_playing_sound *)malloc(sizeof(_playing_sound));
if (item == NULL)
return B_NO_MEMORY;
item->current_offset = 0;
item->sound = sound;
item->id = atomic_add(&sCurrentPlayID, 1);
item->delta = 0;
item->rate = 0;
item->volume = withVolume;
if (!fLocker.Lock()) {
free(item);
return B_ERROR;
}
item->next = fPlayingSounds;
fPlayingSounds = item;
fLocker.Unlock();
SetHasData(true);
return item->id;
}
status_t
BSoundPlayer::SetSoundVolume(play_id sound,
float new_volume)
BSoundPlayer::SetSoundVolume(play_id id, float newVolume)
{
UNIMPLEMENTED();
CALLED();
if (!fLocker.Lock())
return B_ERROR;
return B_OK;
_playing_sound *item = fPlayingSounds;
while (item) {
if (item->id == id) {
item->volume = newVolume;
fLocker.Unlock();
return B_OK;
}
item = item->next;
}
fLocker.Unlock();
return B_ENTRY_NOT_FOUND;
}
bool
BSoundPlayer::IsPlaying(play_id id)
{
UNIMPLEMENTED();
CALLED();
if (!fLocker.Lock())
return B_ERROR;
return true;
_playing_sound *item = fPlayingSounds;
while (item) {
if (item->id == id) {
fLocker.Unlock();
return true;
}
item = item->next;
}
fLocker.Unlock();
return false;
}
status_t
BSoundPlayer::StopPlaying(play_id id)
{
UNIMPLEMENTED();
CALLED();
if (!fLocker.Lock())
return B_ERROR;
return B_OK;
_playing_sound **link = &fPlayingSounds;
_playing_sound *item = fPlayingSounds;
while (item) {
if (item->id == id) {
*link = item->next;
sem_id waitSem = item->wait_sem;
free(item);
fLocker.Unlock();
NotifySoundDone(id, true);
if (waitSem >= 0)
release_sem(waitSem);
return B_OK;
}
link = &item->next;
item = item->next;
}
fLocker.Unlock();
return B_ENTRY_NOT_FOUND;
}
status_t
BSoundPlayer::WaitForSound(play_id id)
{
UNIMPLEMENTED();
CALLED();
if (!fLocker.Lock())
return B_ERROR;
return B_OK;
_playing_sound *item = fPlayingSounds;
while (item) {
if (item->id == id) {
sem_id waitSem = item->wait_sem;
if (waitSem < 0)
waitSem = item->wait_sem = create_sem(0, "wait for sound");
fLocker.Unlock();
return acquire_sem(waitSem);
}
item = item->next;
}
fLocker.Unlock();
return B_ENTRY_NOT_FOUND;
}
@ -631,7 +725,6 @@ float
BSoundPlayer::Volume()
{
CALLED();
return pow(10.0, VolumeDB(true) / 20.0);
}
@ -650,17 +743,17 @@ BSoundPlayer::VolumeDB(bool forcePoll)
CALLED();
if (!fVolumeSlider)
return -94.0f; // silence
if (!forcePoll && (system_time() - fLastVolumeUpdate < 500000))
return fVolumeDB;
int32 count = fVolumeSlider->CountChannels();
int32 count = fVolumeSlider->CountChannels();
float values[count];
size_t size = count * sizeof(float);
fVolumeSlider->GetValue(&values, &size, NULL);
fLastVolumeUpdate = system_time();
fVolumeDB = values[0];
return values[0];
}
@ -671,7 +764,7 @@ BSoundPlayer::SetVolumeDB(float volume_dB)
CALLED();
if (!fVolumeSlider)
return;
float min_dB = fVolumeSlider->MinValue();
float max_dB = fVolumeSlider->MaxValue();
if (volume_dB < min_dB)
@ -679,7 +772,7 @@ BSoundPlayer::SetVolumeDB(float volume_dB)
if (volume_dB > max_dB)
volume_dB = max_dB;
int count = fVolumeSlider->CountChannels();
int count = fVolumeSlider->CountChannels();
float values[count];
for (int i = 0; i < count; i++)
values[i] = volume_dB;
@ -699,7 +792,7 @@ BSoundPlayer::GetVolumeInfo(media_node *out_node,
CALLED();
if (!fVolumeSlider)
return B_NO_INIT;
if (out_node)
*out_node = fMediaInput.node;
if (out_parameter_id)
@ -730,6 +823,42 @@ BSoundPlayer::SetInitError(status_t in_error)
* private BSoundPlayer
*************************************************************/
void
BSoundPlayer::_SoundPlayBufferFunc(void *cookie, void *buffer, size_t size,
const media_raw_audio_format &format)
{
// TODO: support more than one sound and make use of the format parameter
BSoundPlayer *player = (BSoundPlayer *)cookie;
if (!player->fLocker.Lock()) {
memset(buffer, 0, size);
return;
}
_playing_sound *sound = player->fPlayingSounds;
if (sound == NULL) {
player->SetHasData(false);
player->fLocker.Unlock();
memset(buffer, 0, size);
return;
}
size_t used = 0;
if (!sound->sound->GetDataAt(sound->current_offset, buffer, size, &used)) {
// will take care of removing the item and notifying others
player->StopPlaying(sound->id);
player->fLocker.Unlock();
memset(buffer, 0, size);
return;
}
sound->current_offset += used;
player->fLocker.Unlock();
if (used < size)
memset((uint8 *)buffer + used, 0, size - used);
}
status_t BSoundPlayer::_Reserved_SoundPlayer_0(void *, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_1(void *, ...) { return B_ERROR; }
status_t BSoundPlayer::_Reserved_SoundPlayer_2(void *, ...) { return B_ERROR; }
@ -741,10 +870,10 @@ status_t BSoundPlayer::_Reserved_SoundPlayer_7(void *, ...) { return B_ERROR; }
void
BSoundPlayer::NotifySoundDone(play_id sound,
bool got_to_play)
BSoundPlayer::NotifySoundDone(play_id id, bool gotToPlay)
{
UNIMPLEMENTED();
CALLED();
Notify(B_SOUND_DONE, id, gotToPlay);
}
@ -754,13 +883,13 @@ BSoundPlayer::get_volume_slider()
CALLED();
ASSERT(fVolumeSlider == NULL);
BMediaRoster *roster = BMediaRoster::CurrentRoster();
if (!roster) {
TRACE("BSoundPlayer::get_volume_slider failed to get BMediaRoster");
return;
}
if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) {
TRACE("BSoundPlayer::get_volume_slider couldn't get parameter web");
return;
@ -776,10 +905,10 @@ BSoundPlayer::get_volume_slider()
if (strcmp(parameter->Kind(), B_GAIN) != 0)
continue;
fVolumeSlider = (BContinuousParameter *)parameter;
break;
break;
}
#if DEBUG >0
#if DEBUG >0
if (!fVolumeSlider) {
TRACE("BSoundPlayer::get_volume_slider couldn't find volume control");
}