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:
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
_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
|
// 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
|
// 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!
|
// *must* fully specialize the format before returning!
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -302,26 +289,55 @@ _SoundPlayNode::PrepareToConnect(const media_source& what, const media_destinati
|
|||||||
return B_MEDIA_ALREADY_CONNECTED;
|
return B_MEDIA_ALREADY_CONNECTED;
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
+354
-289
@@ -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 (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,
|
if (fFlags & F_IS_STARTED) {
|
||||||
// one of the nodes is the System Mixer, and stopping the Mixer is a Bad Idea (tm).
|
Stop(true, false); // block, but don't flush
|
||||||
// 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);
|
status_t err;
|
||||||
|
BMediaRoster *roster = BMediaRoster::Roster();
|
||||||
err = roster->Disconnect(fMediaInput.node.node, fMediaInput.source,
|
if (!roster) {
|
||||||
fMediaOutput.node.node, fMediaOutput.destination);
|
TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n");
|
||||||
if (err) {
|
goto cleanup;
|
||||||
fprintf(stderr, "* Error disconnecting nodes: %ld (%s)\n", err, strerror(err));
|
}
|
||||||
}
|
|
||||||
fVolumeSlider = NULL;
|
if (fFlags & F_NODES_CONNECTED) {
|
||||||
|
// Ordinarily we'd stop *all* of the nodes in the chain before disconnecting. However,
|
||||||
err = roster->ReleaseNode(fMediaInput.node);
|
// our node is already stopped, and we can't stop the System Mixer.
|
||||||
if (err) {
|
// So, we just disconnect from it, and release our references to the nodes that
|
||||||
fprintf(stderr, "* Error releasing input node: %ld (%s)\n", err, strerror(err));
|
// 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);
|
||||||
err = roster->ReleaseNode(fMediaOutput.node);
|
if (err) {
|
||||||
if (err) {
|
TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: %ld (%s)\n", err, strerror(err));
|
||||||
fprintf(stderr, "* Error releasing output node: %ld (%s)\n", err, strerror(err));
|
|
||||||
}
|
|
||||||
fPlayerNode = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -159,24 +311,33 @@ status_t
|
|||||||
BSoundPlayer::Start()
|
BSoundPlayer::Start()
|
||||||
{
|
{
|
||||||
CALLED();
|
CALLED();
|
||||||
|
|
||||||
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
||||||
|
return B_NO_INIT;
|
||||||
|
|
||||||
if (!fPlayerNode)
|
if (fFlags & F_IS_STARTED)
|
||||||
return B_ERROR;
|
return B_OK;
|
||||||
|
|
||||||
BMediaRoster *roster = BMediaRoster::Roster();
|
BMediaRoster *roster = BMediaRoster::Roster();
|
||||||
if (!roster) {
|
if (!roster) {
|
||||||
TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
|
TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
|
||||||
return B_ERROR;
|
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();
|
CALLED();
|
||||||
|
|
||||||
if (!fPlayerNode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// XXX flush is ignored
|
|
||||||
|
|
||||||
TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);
|
TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);
|
||||||
|
|
||||||
BMediaRoster *roster = BMediaRoster::Roster();
|
if ((fFlags & F_NODES_CONNECTED) == 0)
|
||||||
if (!roster) {
|
|
||||||
TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n");
|
|
||||||
return;
|
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) {
|
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,9 +478,11 @@ 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 (*Notifier)(void *, sound_player_notification what, ...),
|
void
|
||||||
void * cookie)
|
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();
|
CALLED();
|
||||||
fLocker.Lock();
|
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
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user