961 lines
20 KiB
C++
961 lines
20 KiB
C++
/*
|
|
* Copyright 2002-2009, Haiku.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Marcus Overhagen
|
|
* Jérôme Duval
|
|
*/
|
|
|
|
|
|
#include <SoundPlayer.h>
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include <Autolock.h>
|
|
#include <MediaRoster.h>
|
|
#include <ParameterWeb.h>
|
|
#include <Sound.h>
|
|
#include <TimeSource.h>
|
|
|
|
#include "SoundPlayNode.h"
|
|
|
|
#include "MediaDebug.h"
|
|
|
|
|
|
// Flags used internally in BSoundPlayer
|
|
enum {
|
|
F_NODES_CONNECTED = (1 << 0),
|
|
F_HAS_DATA = (1 << 1),
|
|
F_IS_STARTED = (1 << 2),
|
|
F_MUST_RELEASE_MIXER = (1 << 3),
|
|
};
|
|
|
|
|
|
static BSoundPlayer::play_id sCurrentPlayID = 1;
|
|
|
|
|
|
BSoundPlayer::BSoundPlayer(const char* name, BufferPlayerFunc playerFunction,
|
|
EventNotifierFunc eventNotifierFunction, void* cookie)
|
|
{
|
|
CALLED();
|
|
|
|
TRACE("BSoundPlayer::BSoundPlayer: default constructor used\n");
|
|
|
|
media_multi_audio_format format = media_multi_audio_format::wildcard;
|
|
|
|
_Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction,
|
|
cookie);
|
|
}
|
|
|
|
|
|
BSoundPlayer::BSoundPlayer(const media_raw_audio_format* _format,
|
|
const char* name, BufferPlayerFunc playerFunction,
|
|
EventNotifierFunc eventNotifierFunction, void* cookie)
|
|
{
|
|
CALLED();
|
|
|
|
TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used\n");
|
|
|
|
media_multi_audio_format format = media_multi_audio_format::wildcard;
|
|
*(media_raw_audio_format*)&format = *_format;
|
|
|
|
#if DEBUG > 0
|
|
char buf[100];
|
|
media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = format;
|
|
string_for_format(tmp, buf, sizeof(buf));
|
|
TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
|
|
#endif
|
|
|
|
_Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction,
|
|
cookie);
|
|
}
|
|
|
|
|
|
BSoundPlayer::BSoundPlayer(const media_node& toNode,
|
|
const media_multi_audio_format* format, const char* name,
|
|
const media_input* input, BufferPlayerFunc playerFunction,
|
|
EventNotifierFunc eventNotifierFunction, 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");
|
|
|
|
#if DEBUG > 0
|
|
char buf[100];
|
|
media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = *format;
|
|
string_for_format(tmp, buf, sizeof(buf));
|
|
TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
|
|
#endif
|
|
|
|
_Init(&toNode, format, name, input, playerFunction, eventNotifierFunction,
|
|
cookie);
|
|
}
|
|
|
|
|
|
BSoundPlayer::~BSoundPlayer()
|
|
{
|
|
CALLED();
|
|
|
|
if ((fFlags & F_IS_STARTED) != 0) {
|
|
// block, but don't flush
|
|
Stop(true, false);
|
|
}
|
|
|
|
status_t err;
|
|
BMediaRoster* roster = BMediaRoster::Roster();
|
|
if (roster == NULL) {
|
|
TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((fFlags & F_NODES_CONNECTED) != 0) {
|
|
// 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(fMediaOutput, fMediaInput);
|
|
if (err != B_OK) {
|
|
TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: "
|
|
"%" B_PRId32 " (%s)\n", err, strerror(err));
|
|
}
|
|
}
|
|
|
|
if ((fFlags & F_MUST_RELEASE_MIXER) != 0) {
|
|
// Release the mixer as it was acquired
|
|
// through BMediaRoster::GetAudioMixer()
|
|
err = roster->ReleaseNode(fMediaInput.node);
|
|
if (err != B_OK) {
|
|
TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: "
|
|
"%" B_PRId32 " (%s)\n", err, strerror(err));
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
// Dispose of the player node
|
|
|
|
// 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.
|
|
|
|
// The node is deleted by the Release() when ref count reach 0.
|
|
// Since we are the sole owners, and no one acquired it
|
|
// this should be the case. The Quit() synchronization
|
|
// is handled by the DeleteHook inheritance.
|
|
// NOTE: this might be crucial when using a BMediaEventLooper.
|
|
if (fPlayerNode != NULL && fPlayerNode->Release() != NULL) {
|
|
TRACE("BSoundPlayer::~BSoundPlayer: Error the producer node "
|
|
"appears to be acquired by someone else than us!");
|
|
}
|
|
|
|
// do not delete fVolumeSlider, it belongs to the parameter web
|
|
delete fParameterWeb;
|
|
}
|
|
|
|
|
|
status_t
|
|
BSoundPlayer::InitCheck()
|
|
{
|
|
CALLED();
|
|
return fInitStatus;
|
|
}
|
|
|
|
|
|
media_raw_audio_format
|
|
BSoundPlayer::Format() const
|
|
{
|
|
CALLED();
|
|
|
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
|
return media_raw_audio_format::wildcard;
|
|
|
|
return fPlayerNode->Format();
|
|
}
|
|
|
|
|
|
status_t
|
|
BSoundPlayer::Start()
|
|
{
|
|
CALLED();
|
|
|
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
|
return B_NO_INIT;
|
|
|
|
if ((fFlags & F_IS_STARTED) != 0)
|
|
return B_OK;
|
|
|
|
BMediaRoster* roster = BMediaRoster::Roster();
|
|
if (!roster) {
|
|
TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
if (!fPlayerNode->TimeSource()->IsRunning()) {
|
|
roster->StartTimeSource(fPlayerNode->TimeSource()->Node(),
|
|
fPlayerNode->TimeSource()->RealTime());
|
|
}
|
|
|
|
// 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, %" B_PRId32, err);
|
|
return err;
|
|
}
|
|
|
|
if (fNotifierFunc != NULL)
|
|
fNotifierFunc(fCookie, B_STARTED, this);
|
|
|
|
SetHasData(true);
|
|
atomic_or(&fFlags, F_IS_STARTED);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::Stop(bool block, bool flush)
|
|
{
|
|
CALLED();
|
|
|
|
TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);
|
|
|
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
|
return;
|
|
|
|
// TODO: flush is ignored
|
|
|
|
if ((fFlags & F_IS_STARTED) != 0) {
|
|
BMediaRoster* roster = BMediaRoster::Roster();
|
|
if (roster == NULL) {
|
|
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 tries;
|
|
for (tries = 250; fPlayerNode->IsPlaying() && tries != 0; tries--)
|
|
snooze(2000);
|
|
|
|
DEBUG_ONLY(if (tries == 0)
|
|
TRACE("BSoundPlayer::Stop: waiting for node stop failed\n"));
|
|
|
|
// Wait until all buffers on the way to the physical output have been
|
|
// played
|
|
snooze(Latency() + 2000);
|
|
}
|
|
|
|
if (fNotifierFunc)
|
|
fNotifierFunc(fCookie, B_STOPPED, this);
|
|
|
|
}
|
|
|
|
|
|
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 %" B_PRId32
|
|
" (%s)\n", err, strerror(err));
|
|
return 0;
|
|
}
|
|
|
|
TRACE("BSoundPlayer::Latency: latency is %" B_PRId64 "\n", latency);
|
|
|
|
return latency;
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetHasData(bool hasData)
|
|
{
|
|
CALLED();
|
|
if (hasData)
|
|
atomic_or(&fFlags, F_HAS_DATA);
|
|
else
|
|
atomic_and(&fFlags, ~F_HAS_DATA);
|
|
}
|
|
|
|
|
|
bool
|
|
BSoundPlayer::HasData()
|
|
{
|
|
CALLED();
|
|
return (atomic_get(&fFlags) & F_HAS_DATA) != 0;
|
|
}
|
|
|
|
|
|
BSoundPlayer::BufferPlayerFunc
|
|
BSoundPlayer::BufferPlayer() const
|
|
{
|
|
CALLED();
|
|
return fPlayBufferFunc;
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction)
|
|
{
|
|
CALLED();
|
|
BAutolock _(fLocker);
|
|
|
|
fPlayBufferFunc = playerFunction;
|
|
}
|
|
|
|
|
|
BSoundPlayer::EventNotifierFunc
|
|
BSoundPlayer::EventNotifier() const
|
|
{
|
|
CALLED();
|
|
return fNotifierFunc;
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction)
|
|
{
|
|
CALLED();
|
|
BAutolock _(fLocker);
|
|
|
|
fNotifierFunc = eventNotifierFunction;
|
|
}
|
|
|
|
|
|
void*
|
|
BSoundPlayer::Cookie() const
|
|
{
|
|
CALLED();
|
|
return fCookie;
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetCookie(void *cookie)
|
|
{
|
|
CALLED();
|
|
BAutolock _(fLocker);
|
|
|
|
fCookie = cookie;
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetCallbacks(BufferPlayerFunc playerFunction,
|
|
EventNotifierFunc eventNotifierFunction, void* cookie)
|
|
{
|
|
CALLED();
|
|
BAutolock _(fLocker);
|
|
|
|
SetBufferPlayer(playerFunction);
|
|
SetNotifier(eventNotifierFunction);
|
|
SetCookie(cookie);
|
|
}
|
|
|
|
|
|
/*! 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()
|
|
{
|
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
|
return 0;
|
|
|
|
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()
|
|
{
|
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
|
return (bigtime_t) B_ERROR;
|
|
|
|
return fPlayerNode->TimeSource()->Now();
|
|
}
|
|
|
|
|
|
status_t
|
|
BSoundPlayer::Preroll()
|
|
{
|
|
CALLED();
|
|
|
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
|
return B_NO_INIT;
|
|
|
|
BMediaRoster* roster = BMediaRoster::Roster();
|
|
if (roster == NULL) {
|
|
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: %"
|
|
B_PRId32 " (%s)\n", err, strerror(err));
|
|
return err;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
BSoundPlayer::play_id
|
|
BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime)
|
|
{
|
|
return StartPlaying(sound, atTime, 1.0);
|
|
}
|
|
|
|
|
|
BSoundPlayer::play_id
|
|
BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime, float withVolume)
|
|
{
|
|
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;
|
|
}
|
|
|
|
sound->AcquireRef();
|
|
item->next = fPlayingSounds;
|
|
fPlayingSounds = item;
|
|
fLocker.Unlock();
|
|
|
|
SetHasData(true);
|
|
return item->id;
|
|
}
|
|
|
|
|
|
status_t
|
|
BSoundPlayer::SetSoundVolume(play_id id, float newVolume)
|
|
{
|
|
CALLED();
|
|
if (!fLocker.Lock())
|
|
return B_ERROR;
|
|
|
|
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)
|
|
{
|
|
CALLED();
|
|
if (!fLocker.Lock())
|
|
return B_ERROR;
|
|
|
|
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)
|
|
{
|
|
CALLED();
|
|
if (!fLocker.Lock())
|
|
return B_ERROR;
|
|
|
|
playing_sound** link = &fPlayingSounds;
|
|
playing_sound* item = fPlayingSounds;
|
|
|
|
while (item != NULL) {
|
|
if (item->id == id) {
|
|
*link = item->next;
|
|
sem_id waitSem = item->wait_sem;
|
|
item->sound->ReleaseRef();
|
|
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)
|
|
{
|
|
CALLED();
|
|
if (!fLocker.Lock())
|
|
return B_ERROR;
|
|
|
|
playing_sound* item = fPlayingSounds;
|
|
while (item != NULL) {
|
|
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;
|
|
}
|
|
|
|
|
|
float
|
|
BSoundPlayer::Volume()
|
|
{
|
|
CALLED();
|
|
return pow(10.0, VolumeDB(true) / 20.0);
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetVolume(float newVolume)
|
|
{
|
|
CALLED();
|
|
SetVolumeDB(20.0 * log10(newVolume));
|
|
}
|
|
|
|
|
|
float
|
|
BSoundPlayer::VolumeDB(bool forcePoll)
|
|
{
|
|
CALLED();
|
|
if (!fVolumeSlider)
|
|
return -94.0f; // silence
|
|
|
|
if (!forcePoll && system_time() - fLastVolumeUpdate < 500000)
|
|
return fVolumeDB;
|
|
|
|
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];
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetVolumeDB(float volumeDB)
|
|
{
|
|
CALLED();
|
|
if (!fVolumeSlider)
|
|
return;
|
|
|
|
float minDB = fVolumeSlider->MinValue();
|
|
float maxDB = fVolumeSlider->MaxValue();
|
|
if (volumeDB < minDB)
|
|
volumeDB = minDB;
|
|
if (volumeDB > maxDB)
|
|
volumeDB = maxDB;
|
|
|
|
int count = fVolumeSlider->CountChannels();
|
|
float values[count];
|
|
for (int i = 0; i < count; i++)
|
|
values[i] = volumeDB;
|
|
fVolumeSlider->SetValue(values, sizeof(float) * count, 0);
|
|
|
|
fVolumeDB = volumeDB;
|
|
fLastVolumeUpdate = system_time();
|
|
}
|
|
|
|
|
|
status_t
|
|
BSoundPlayer::GetVolumeInfo(media_node* _node, int32* _parameterID,
|
|
float* _minDB, float* _maxDB)
|
|
{
|
|
CALLED();
|
|
if (fVolumeSlider == NULL)
|
|
return B_NO_INIT;
|
|
|
|
if (_node != NULL)
|
|
*_node = fMediaInput.node;
|
|
if (_parameterID != NULL)
|
|
*_parameterID = fVolumeSlider->ID();
|
|
if (_minDB != NULL)
|
|
*_minDB = fVolumeSlider->MinValue();
|
|
if (_maxDB != NULL)
|
|
*_maxDB = fVolumeSlider->MaxValue();
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark - protected BSoundPlayer
|
|
|
|
|
|
void
|
|
BSoundPlayer::SetInitError(status_t error)
|
|
{
|
|
CALLED();
|
|
fInitStatus = error;
|
|
}
|
|
|
|
|
|
// #pragma mark - 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; }
|
|
status_t BSoundPlayer::_Reserved_SoundPlayer_3(void*, ...) { return B_ERROR; }
|
|
status_t BSoundPlayer::_Reserved_SoundPlayer_4(void*, ...) { return B_ERROR; }
|
|
status_t BSoundPlayer::_Reserved_SoundPlayer_5(void*, ...) { return B_ERROR; }
|
|
status_t BSoundPlayer::_Reserved_SoundPlayer_6(void*, ...) { return B_ERROR; }
|
|
status_t BSoundPlayer::_Reserved_SoundPlayer_7(void*, ...) { return B_ERROR; }
|
|
|
|
|
|
void
|
|
BSoundPlayer::_Init(const media_node* node,
|
|
const media_multi_audio_format* format, const char* name,
|
|
const media_input* input, BufferPlayerFunc playerFunction,
|
|
EventNotifierFunc eventNotifierFunction, void* cookie)
|
|
{
|
|
CALLED();
|
|
fPlayingSounds = NULL;
|
|
fWaitingSounds = NULL;
|
|
|
|
fPlayerNode = NULL;
|
|
if (playerFunction == NULL) {
|
|
fPlayBufferFunc = _SoundPlayBufferFunc;
|
|
fCookie = this;
|
|
} else {
|
|
fPlayBufferFunc = playerFunction;
|
|
fCookie = cookie;
|
|
}
|
|
|
|
fNotifierFunc = eventNotifierFunction;
|
|
fVolumeDB = 0.0f;
|
|
fFlags = 0;
|
|
fInitStatus = B_ERROR;
|
|
fParameterWeb = NULL;
|
|
fVolumeSlider = NULL;
|
|
fLastVolumeUpdate = 0;
|
|
|
|
BMediaRoster* roster = BMediaRoster::Roster();
|
|
if (roster == NULL) {
|
|
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
|
|
media_node inputNode;
|
|
if (node) {
|
|
inputNode = *node;
|
|
} else {
|
|
fInitStatus = roster->GetAudioMixer(&inputNode);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't GetAudioMixer\n");
|
|
return;
|
|
}
|
|
fFlags |= F_MUST_RELEASE_MIXER;
|
|
}
|
|
|
|
media_output _output;
|
|
media_input _input;
|
|
int32 inputCount;
|
|
int32 outputCount;
|
|
media_format tryFormat;
|
|
|
|
// Create the player node and register it
|
|
fPlayerNode = new BPrivate::SoundPlayNode(name, this);
|
|
fInitStatus = roster->RegisterNode(fPlayerNode);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't RegisterNode: %s\n",
|
|
strerror(fInitStatus));
|
|
return;
|
|
}
|
|
|
|
// set the producer's time source to be the "default" time source,
|
|
// which the system audio mixer uses too.
|
|
media_node timeSource;
|
|
fInitStatus = roster->GetTimeSource(&timeSource);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't GetTimeSource: %s\n",
|
|
strerror(fInitStatus));
|
|
return;
|
|
}
|
|
fInitStatus = roster->SetTimeSourceFor(fPlayerNode->Node().node,
|
|
timeSource.node);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't SetTimeSourceFor: %s\n",
|
|
strerror(fInitStatus));
|
|
return;
|
|
}
|
|
|
|
// find a free media_input
|
|
if (!input) {
|
|
fInitStatus = roster->GetFreeInputsFor(inputNode, &_input, 1,
|
|
&inputCount, B_MEDIA_RAW_AUDIO);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't GetFreeInputsFor: %s\n",
|
|
strerror(fInitStatus));
|
|
return;
|
|
}
|
|
if (inputCount < 1) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't find a free input\n");
|
|
fInitStatus = B_ERROR;
|
|
return;
|
|
}
|
|
} else {
|
|
_input = *input;
|
|
}
|
|
|
|
// find a free media_output
|
|
fInitStatus = roster->GetFreeOutputsFor(fPlayerNode->Node(), &_output, 1,
|
|
&outputCount, B_MEDIA_RAW_AUDIO);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't GetFreeOutputsFor: %s\n",
|
|
strerror(fInitStatus));
|
|
return;
|
|
}
|
|
if (outputCount < 1) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't find a free output\n");
|
|
fInitStatus = B_ERROR;
|
|
return;
|
|
}
|
|
|
|
// Set an appropriate run mode for the producer
|
|
fInitStatus = roster->SetRunModeNode(fPlayerNode->Node(),
|
|
BMediaNode::B_INCREASE_LATENCY);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't SetRunModeNode: %s\n",
|
|
strerror(fInitStatus));
|
|
return;
|
|
}
|
|
|
|
// 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));
|
|
TRACE("BSoundPlayer::_Init: trying to connect with format %s\n", buf);
|
|
#endif
|
|
|
|
// and connect the nodes
|
|
fInitStatus = roster->Connect(_output.source, _input.destination,
|
|
&tryFormat, &fMediaOutput, &fMediaInput);
|
|
if (fInitStatus != B_OK) {
|
|
TRACE("BSoundPlayer::_Init: Couldn't Connect: %s\n",
|
|
strerror(fInitStatus));
|
|
return;
|
|
}
|
|
|
|
fFlags |= F_NODES_CONNECTED;
|
|
|
|
_GetVolumeSlider();
|
|
|
|
TRACE("BSoundPlayer node %" B_PRId32 " has timesource %" B_PRId32 "\n",
|
|
fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node);
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay)
|
|
{
|
|
CALLED();
|
|
Notify(B_SOUND_DONE, id, gotToPlay);
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::_GetVolumeSlider()
|
|
{
|
|
CALLED();
|
|
|
|
ASSERT(fVolumeSlider == NULL);
|
|
|
|
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
|
if (!roster) {
|
|
TRACE("BSoundPlayer::_GetVolumeSlider failed to get BMediaRoster");
|
|
return;
|
|
}
|
|
|
|
if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) {
|
|
TRACE("BSoundPlayer::_GetVolumeSlider couldn't get parameter web");
|
|
return;
|
|
}
|
|
|
|
int count = fParameterWeb->CountParameters();
|
|
for (int i = 0; i < count; i++) {
|
|
BParameter *parameter = fParameterWeb->ParameterAt(i);
|
|
if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
|
|
continue;
|
|
if ((parameter->ID() >> 16) != fMediaInput.destination.id)
|
|
continue;
|
|
if (strcmp(parameter->Kind(), B_GAIN) != 0)
|
|
continue;
|
|
fVolumeSlider = (BContinuousParameter *)parameter;
|
|
break;
|
|
}
|
|
|
|
#if DEBUG >0
|
|
if (!fVolumeSlider) {
|
|
TRACE("BSoundPlayer::_GetVolumeSlider couldn't find volume control");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::Notify(sound_player_notification what, ...)
|
|
{
|
|
CALLED();
|
|
if (fLocker.Lock()) {
|
|
if (fNotifierFunc)
|
|
(*fNotifierFunc)(fCookie, what);
|
|
fLocker.Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
BSoundPlayer::PlayBuffer(void* buffer, size_t size,
|
|
const media_raw_audio_format& format)
|
|
{
|
|
if (fLocker.Lock()) {
|
|
if (fPlayBufferFunc)
|
|
(*fPlayBufferFunc)(fCookie, buffer, size, format);
|
|
fLocker.Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark - public sound_error
|
|
|
|
|
|
sound_error::sound_error(const char* string)
|
|
{
|
|
m_str_const = string;
|
|
}
|
|
|
|
|
|
const char*
|
|
sound_error::what() const throw()
|
|
{
|
|
return m_str_const;
|
|
}
|
|
|