* When you even already have the typedefs, why wouldn't you want to use them?
* Some cleanup, no functional change. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34474 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -28,47 +28,40 @@ public:
|
||||
|
||||
|
||||
class BSoundPlayer {
|
||||
friend class _SoundPlayNode;
|
||||
public:
|
||||
enum sound_player_notification {
|
||||
B_STARTED = 1,
|
||||
B_STOPPED,
|
||||
B_SOUND_DONE
|
||||
};
|
||||
enum sound_player_notification {
|
||||
B_STARTED = 1,
|
||||
B_STOPPED,
|
||||
B_SOUND_DONE
|
||||
};
|
||||
|
||||
typedef void (*BufferPlayerFunc)(void*, void* buffer, size_t size,
|
||||
const media_raw_audio_format& format);
|
||||
typedef void (*EventNotifierFunc)(void*, sound_player_notification what,
|
||||
...);
|
||||
|
||||
public:
|
||||
BSoundPlayer(const char* name = NULL,
|
||||
void (*PlayBuffer)(void*, void* buffer,
|
||||
size_t size,
|
||||
const media_raw_audio_format& format)
|
||||
= NULL,
|
||||
void (*Notifier)(void*,
|
||||
sound_player_notification what, ...)
|
||||
= NULL,
|
||||
BufferPlayerFunc playerFunction = NULL,
|
||||
EventNotifierFunc
|
||||
eventNotifierFunction = NULL,
|
||||
void* cookie = NULL);
|
||||
BSoundPlayer(
|
||||
const media_raw_audio_format* format,
|
||||
const char* name = NULL,
|
||||
void (*PlayBuffer)(void*, void* buffer,
|
||||
size_t size,
|
||||
const media_raw_audio_format& format)
|
||||
= NULL,
|
||||
void (*Notifier)(void*,
|
||||
sound_player_notification what, ...)
|
||||
= NULL,
|
||||
BufferPlayerFunc playerFunction = NULL,
|
||||
EventNotifierFunc
|
||||
eventNotifierFunction = NULL,
|
||||
void* cookie = NULL);
|
||||
BSoundPlayer(
|
||||
const media_node& toNode,
|
||||
const media_multi_audio_format* format
|
||||
= NULL,
|
||||
const media_multi_audio_format*
|
||||
format = NULL,
|
||||
const char* name = NULL,
|
||||
const media_input* input = NULL,
|
||||
void (*PlayBuffer)(void*, void* buffer,
|
||||
size_t size,
|
||||
const media_raw_audio_format& format)
|
||||
= NULL,
|
||||
void (*Notifier)(void*,
|
||||
sound_player_notification what, ...)
|
||||
= NULL,
|
||||
BufferPlayerFunc playerFunction = NULL,
|
||||
EventNotifierFunc
|
||||
eventNotifierFunction = NULL,
|
||||
void* cookie = NULL);
|
||||
virtual ~BSoundPlayer();
|
||||
|
||||
@@ -78,27 +71,20 @@ public:
|
||||
status_t Start();
|
||||
void Stop(bool block = true, bool flush = true);
|
||||
|
||||
typedef void (*BufferPlayerFunc)(void*, void*, size_t,
|
||||
const media_raw_audio_format&);
|
||||
|
||||
BufferPlayerFunc BufferPlayer() const;
|
||||
void SetBufferPlayer(void (*PlayBuffer)(void*,
|
||||
void* buffer, size_t size,
|
||||
const media_raw_audio_format& format));
|
||||
typedef void (*EventNotifierFunc)(void*, sound_player_notification what,
|
||||
...);
|
||||
|
||||
EventNotifierFunc EventNotifier() const;
|
||||
void SetNotifier(void (*Notifier)(void*,
|
||||
sound_player_notification what, ...));
|
||||
void SetNotifier(
|
||||
EventNotifierFunc eventNotifierFunction);
|
||||
void* Cookie() const;
|
||||
void SetCookie(void* cookie);
|
||||
void SetCallbacks(void (*PlayBuffer)(void*,
|
||||
void* buffer, size_t size,
|
||||
const media_raw_audio_format& format)
|
||||
= NULL,
|
||||
void (*Notifier)(void*,
|
||||
sound_player_notification what, ...)
|
||||
= NULL,
|
||||
void SetCallbacks(
|
||||
BufferPlayerFunc playerFunction = NULL,
|
||||
EventNotifierFunc
|
||||
eventNotifierFunction = NULL,
|
||||
void* cookie = NULL);
|
||||
|
||||
typedef int32 play_id;
|
||||
@@ -137,7 +123,7 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
void SetInitError(status_t inError);
|
||||
void SetInitError(status_t error);
|
||||
|
||||
private:
|
||||
static void _SoundPlayBufferFunc(void* cookie,
|
||||
@@ -154,37 +140,50 @@ private:
|
||||
virtual status_t _Reserved_SoundPlayer_6(void*, ...);
|
||||
virtual status_t _Reserved_SoundPlayer_7(void*, ...);
|
||||
|
||||
void _Init(const media_node* node,
|
||||
const media_multi_audio_format* format,
|
||||
const char* name, const media_input* input,
|
||||
BufferPlayerFunc playerFunction,
|
||||
EventNotifierFunc eventNotifierFunction,
|
||||
void* cookie);
|
||||
void _GetVolumeSlider();
|
||||
|
||||
void _NotifySoundDone(play_id sound, bool gotToPlay);
|
||||
|
||||
virtual void _Notify(sound_player_notification what, ...);
|
||||
virtual void _PlayBuffer(void* buffer, size_t size,
|
||||
const media_raw_audio_format& format);
|
||||
|
||||
private:
|
||||
friend class _SoundPlayNode;
|
||||
|
||||
struct _playing_sound {
|
||||
_playing_sound* next;
|
||||
off_t current_offset;
|
||||
BSound* sound;
|
||||
play_id id;
|
||||
int32 delta;
|
||||
int32 rate;
|
||||
sem_id wait_sem;
|
||||
float volume;
|
||||
};
|
||||
|
||||
struct _waiting_sound {
|
||||
_waiting_sound* next;
|
||||
bigtime_t start_time;
|
||||
BSound* sound;
|
||||
play_id id;
|
||||
int32 rate;
|
||||
float volume;
|
||||
};
|
||||
|
||||
_SoundPlayNode* fPlayerNode;
|
||||
|
||||
struct _playing_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* fPlayingSounds;
|
||||
|
||||
struct _waiting_sound {
|
||||
_waiting_sound* next;
|
||||
bigtime_t start_time;
|
||||
BSound* sound;
|
||||
play_id id;
|
||||
int32 rate;
|
||||
float volume;
|
||||
};
|
||||
|
||||
_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,
|
||||
...);
|
||||
BufferPlayerFunc fPlayBufferFunc;
|
||||
EventNotifierFunc fNotifierFunc;
|
||||
|
||||
BLocker fLocker;
|
||||
float fVolumeDB;
|
||||
@@ -201,31 +200,7 @@ private:
|
||||
bigtime_t fLastVolumeUpdate;
|
||||
BParameterWeb* fParameterWeb;
|
||||
|
||||
uint32 _m_reserved[15];
|
||||
|
||||
private:
|
||||
void NotifySoundDone(play_id sound, bool gotToPlay);
|
||||
|
||||
void get_volume_slider();
|
||||
void 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);
|
||||
// For B_STARTED and B_STOPPED, the argument is BSoundPlayer* (this).
|
||||
// For B_SOUND_DONE, the arguments are play_id and true/false for
|
||||
// whether it got to play data at all.
|
||||
virtual void Notify(sound_player_notification what,
|
||||
...);
|
||||
// Get data into the buffer to play -- this version will use the
|
||||
// queued BSound system.
|
||||
virtual void PlayBuffer(void* buffer, size_t size,
|
||||
const media_raw_audio_format& format);
|
||||
uint32 _reserved[15];
|
||||
};
|
||||
|
||||
#endif // _SOUND_PLAYER_H
|
||||
|
||||
+307
-235
@@ -1,29 +1,42 @@
|
||||
/***********************************************************************
|
||||
* AUTHOR: Marcus Overhagen, Jérôme Duval
|
||||
* FILE: SoundPlayNode.cpp
|
||||
* DESCR: This is the BBufferProducer, used internally by BSoundPlayer
|
||||
* This belongs into a private namespace, but isn't for
|
||||
* compatibility reasons.
|
||||
***********************************************************************/
|
||||
/*
|
||||
* Copyright 2002-2009, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Marcus Overhagen
|
||||
* Jérôme Duval
|
||||
*/
|
||||
|
||||
|
||||
/*! This is the BBufferProducer, used internally by BSoundPlayer
|
||||
This belongs into a private namespace, but isn't for
|
||||
compatibility reasons.
|
||||
*/
|
||||
|
||||
|
||||
#include "SoundPlayNode.h"
|
||||
|
||||
#include <TimeSource.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "SoundPlayNode.h"
|
||||
|
||||
#include <TimeSource.h>
|
||||
#include <MediaRoster.h>
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#define SEND_NEW_BUFFER_EVENT (BTimedEventQueue::B_USER_EVENT + 1)
|
||||
|
||||
_SoundPlayNode::_SoundPlayNode(const char *name, BSoundPlayer *player) :
|
||||
|
||||
_SoundPlayNode::_SoundPlayNode(const char* name, BSoundPlayer* player)
|
||||
:
|
||||
BMediaNode(name),
|
||||
BBufferProducer(B_MEDIA_RAW_AUDIO),
|
||||
BMediaEventLooper(),
|
||||
mPlayer(player),
|
||||
mInitCheckStatus(B_OK),
|
||||
mOutputEnabled(true),
|
||||
mBufferGroup(NULL),
|
||||
mBufferGroup(NULL),
|
||||
mFramesSent(0),
|
||||
mTooEarlyCount(0)
|
||||
{
|
||||
@@ -39,60 +52,71 @@ _SoundPlayNode::~_SoundPlayNode()
|
||||
Quit();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
_SoundPlayNode::IsPlaying()
|
||||
{
|
||||
return RunState() == B_STARTED;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
_SoundPlayNode::CurrentTime()
|
||||
{
|
||||
int frame_rate = (int)mOutput.format.u.raw_audio.frame_rate;
|
||||
return frame_rate == 0 ? 0 : bigtime_t((1000000LL * mFramesSent) / frame_rate);
|
||||
int frameRate = (int)mOutput.format.u.raw_audio.frame_rate;
|
||||
return frameRate == 0 ? 0
|
||||
: bigtime_t((1000000LL * mFramesSent) / frameRate);
|
||||
}
|
||||
|
||||
media_multi_audio_format
|
||||
|
||||
media_multi_audio_format
|
||||
_SoundPlayNode::Format() const
|
||||
{
|
||||
return mOutput.format.u.raw_audio;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// implementation of BMediaNode
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
BMediaAddOn * _SoundPlayNode::AddOn(int32 * internal_id) const
|
||||
// #pragma mark - implementation of BMediaNode
|
||||
|
||||
|
||||
BMediaAddOn*
|
||||
_SoundPlayNode::AddOn(int32* _internalID) const
|
||||
{
|
||||
CALLED();
|
||||
// BeBook says this only gets called if we were in an add-on.
|
||||
// This only gets called if we are in an add-on.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _SoundPlayNode::Preroll(void)
|
||||
|
||||
void
|
||||
_SoundPlayNode::Preroll()
|
||||
{
|
||||
CALLED();
|
||||
// XXX:Performance opportunity
|
||||
// TODO: Performance opportunity
|
||||
BMediaNode::Preroll();
|
||||
}
|
||||
|
||||
status_t _SoundPlayNode::HandleMessage(int32 message, const void * data, size_t size)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleMessage(int32 message, const void* data, size_t size)
|
||||
{
|
||||
CALLED();
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
void _SoundPlayNode::NodeRegistered(void)
|
||||
|
||||
void
|
||||
_SoundPlayNode::NodeRegistered()
|
||||
{
|
||||
CALLED();
|
||||
|
||||
|
||||
if (mInitCheckStatus != B_OK) {
|
||||
ReportError(B_NODE_IN_DISTRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SetPriority(B_URGENT_PRIORITY);
|
||||
|
||||
|
||||
mOutput.format.type = B_MEDIA_RAW_AUDIO;
|
||||
mOutput.format.u.raw_audio = media_multi_audio_format::wildcard;
|
||||
mOutput.destination = media_destination::null;
|
||||
@@ -100,22 +124,27 @@ void _SoundPlayNode::NodeRegistered(void)
|
||||
mOutput.source.id = 0;
|
||||
mOutput.node = Node();
|
||||
strcpy(mOutput.name, Name());
|
||||
|
||||
Run();
|
||||
|
||||
Run();
|
||||
}
|
||||
|
||||
status_t _SoundPlayNode::RequestCompleted(const media_request_info &info)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::RequestCompleted(const media_request_info& info)
|
||||
{
|
||||
CALLED();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void _SoundPlayNode::SetTimeSource(BTimeSource *timeSource)
|
||||
|
||||
void
|
||||
_SoundPlayNode::SetTimeSource(BTimeSource* timeSource)
|
||||
{
|
||||
CALLED();
|
||||
BMediaNode::SetTimeSource(timeSource);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_SoundPlayNode::SetRunMode(run_mode mode)
|
||||
{
|
||||
@@ -123,16 +152,18 @@ _SoundPlayNode::SetRunMode(run_mode mode)
|
||||
BMediaNode::SetRunMode(mode);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// implementation for BBufferProducer
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
|
||||
// #pragma mark - implementation for BBufferProducer
|
||||
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::FormatSuggestionRequested(media_type type, int32 /*quality*/,
|
||||
media_format* format)
|
||||
{
|
||||
// FormatSuggestionRequested() is not necessarily part of the format negotiation
|
||||
// process; it's simply an interrogation -- the caller wants to see what the node's
|
||||
// preferred data format is, given a suggestion by the caller.
|
||||
// FormatSuggestionRequested() is not necessarily part of the format
|
||||
// negotiation process; it's simply an interrogation -- the caller wants
|
||||
// to see what the node's preferred data format is, given a suggestion by
|
||||
// the caller.
|
||||
CALLED();
|
||||
|
||||
// a wildcard type is okay; but we only support raw audio
|
||||
@@ -146,11 +177,13 @@ _SoundPlayNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, me
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::FormatProposal(const media_source& output, media_format* format)
|
||||
{
|
||||
// FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand
|
||||
// out a suggested format, with wildcards for any variations we support.
|
||||
// FormatProposal() is the first stage in the BMediaRoster::Connect()
|
||||
// process. We hand out a suggested format, with wildcards for any
|
||||
// variations we support.
|
||||
CALLED();
|
||||
|
||||
// is this a proposal for our one output?
|
||||
@@ -168,7 +201,7 @@ _SoundPlayNode::FormatProposal(const media_source& output, media_format* format)
|
||||
TRACE("_SoundPlayNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG >0
|
||||
char buf[100];
|
||||
string_for_format(*format, buf, sizeof(buf));
|
||||
@@ -178,8 +211,11 @@ _SoundPlayNode::FormatProposal(const media_source& output, media_format* format)
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::FormatChangeRequested(const media_source& source,
|
||||
const media_destination& destination, media_format* _format,
|
||||
int32* /* deprecated */)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
@@ -187,13 +223,14 @@ _SoundPlayNode::FormatChangeRequested(const media_source& source, const media_de
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::GetNextOutput(int32* cookie, media_output* out_output)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::GetNextOutput(int32* cookie, media_output* _output)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
if (*cookie == 0) {
|
||||
*out_output = mOutput;
|
||||
*_output = mOutput;
|
||||
*cookie += 1;
|
||||
return B_OK;
|
||||
} else {
|
||||
@@ -201,7 +238,8 @@ _SoundPlayNode::GetNextOutput(int32* cookie, media_output* out_output)
|
||||
}
|
||||
}
|
||||
|
||||
status_t
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::DisposeOutputCookie(int32 cookie)
|
||||
{
|
||||
CALLED();
|
||||
@@ -209,13 +247,15 @@ _SoundPlayNode::DisposeOutputCookie(int32 cookie)
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::SetBufferGroup(const media_source& forSource,
|
||||
BBufferGroup* newGroup)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
// is this our output?
|
||||
if (for_source != mOutput.source) {
|
||||
if (forSource != mOutput.source) {
|
||||
TRACE("_SoundPlayNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n");
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
}
|
||||
@@ -224,20 +264,18 @@ _SoundPlayNode::SetBufferGroup(const media_source& for_source, BBufferGroup* new
|
||||
if (newGroup == mBufferGroup)
|
||||
return B_OK;
|
||||
|
||||
// Ahh, someone wants us to use a different buffer group. At this point we delete
|
||||
// the one we are using and use the specified one instead. If the specified group is
|
||||
// NULL, we need to recreate one ourselves, and use *that*. Note that if we're
|
||||
// caching a BBuffer that we requested earlier, we have to Recycle() that buffer
|
||||
// *before* deleting the buffer group, otherwise we'll deadlock waiting for that
|
||||
// buffer to be recycled!
|
||||
delete mBufferGroup; // waits for all buffers to recycle
|
||||
if (newGroup != NULL)
|
||||
{
|
||||
// Ahh, someone wants us to use a different buffer group. At this point we
|
||||
// delete the one we are using and use the specified one instead.
|
||||
// If the specified group is NULL, we need to recreate one ourselves, and
|
||||
// use *that*. Note that if we're caching a BBuffer that we requested
|
||||
// earlier, we have to Recycle() that buffer *before* deleting the buffer
|
||||
// group, otherwise we'll deadlock waiting for that buffer to be recycled!
|
||||
delete mBufferGroup;
|
||||
// waits for all buffers to recycle
|
||||
if (newGroup != NULL) {
|
||||
// we were given a valid group; just use that one from now on
|
||||
mBufferGroup = newGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// we were passed a NULL group pointer; that means we construct
|
||||
// our own buffer group to use from now on
|
||||
size_t size = mOutput.format.u.raw_audio.buffer_size;
|
||||
@@ -250,29 +288,35 @@ _SoundPlayNode::SetBufferGroup(const media_source& for_source, BBufferGroup* new
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::GetLatency(bigtime_t* out_latency)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::GetLatency(bigtime_t* _latency)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
|
||||
// report our *total* latency: internal plus downstream plus scheduling
|
||||
*out_latency = EventLatency() + SchedulingLatency();
|
||||
*_latency = EventLatency() + SchedulingLatency();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::PrepareToConnect(const media_source& what,
|
||||
const media_destination& where, media_format* format,
|
||||
media_source* _source, char* _name)
|
||||
{
|
||||
// PrepareToConnect() is the second stage of format negotiations that happens
|
||||
// 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()
|
||||
// *must* fully specialize the format before returning!
|
||||
// PrepareToConnect() is the second stage of format negotiations that
|
||||
// happens 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() *must* fully specialize the format before
|
||||
// returning!
|
||||
CALLED();
|
||||
|
||||
// is this our output?
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -291,8 +335,10 @@ _SoundPlayNode::PrepareToConnect(const media_source& what, const media_destinati
|
||||
#endif
|
||||
|
||||
// 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");
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -304,27 +350,30 @@ _SoundPlayNode::PrepareToConnect(const media_source& what, const media_destinati
|
||||
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) {
|
||||
&& *(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];
|
||||
TRACE("_SoundPlayNode::PrepareToConnect: found mixer info: channel_count %ld, frame_rate %.1f\n", channel_count, frame_rate);
|
||||
TRACE("_SoundPlayNode::PrepareToConnect: found mixer info: "
|
||||
"channel_count %ld, frame_rate %.1f\n", channel_count, frame_rate);
|
||||
}
|
||||
|
||||
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.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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#if DEBUG > 0
|
||||
string_for_format(*format, buf, sizeof(buf));
|
||||
@@ -334,25 +383,28 @@ _SoundPlayNode::PrepareToConnect(const media_source& what, const media_destinati
|
||||
// Now reserve the connection, and return information about it
|
||||
mOutput.destination = where;
|
||||
mOutput.format = *format;
|
||||
*out_source = mOutput.source;
|
||||
strcpy(out_name, Name());
|
||||
*_source = mOutput.source;
|
||||
strcpy(_name, Name());
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void
|
||||
_SoundPlayNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
|
||||
|
||||
void
|
||||
_SoundPlayNode::Connect(status_t error, const media_source& source,
|
||||
const media_destination& destination, const media_format& format,
|
||||
char* name)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
|
||||
// is this our output?
|
||||
if (source != mOutput.source) {
|
||||
TRACE("_SoundPlayNode::Connect returning\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 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) {
|
||||
mOutput.destination = media_destination::null;
|
||||
mOutput.format.type = B_MEDIA_RAW_AUDIO;
|
||||
@@ -360,11 +412,11 @@ _SoundPlayNode::Connect(status_t error, const media_source& source, const media_
|
||||
return;
|
||||
}
|
||||
|
||||
// Okay, the connection has been confirmed. Record the destination and format
|
||||
// that we agreed on, and report our connection name again.
|
||||
// Okay, the connection has been confirmed. Record the destination and
|
||||
// format that we agreed on, and report our connection name again.
|
||||
mOutput.destination = destination;
|
||||
mOutput.format = format;
|
||||
strcpy(io_name, Name());
|
||||
strcpy(name, Name());
|
||||
|
||||
// Now that we're connected, we can determine our downstream latency.
|
||||
// Do so, then make sure we get our events early enough.
|
||||
@@ -374,83 +426,83 @@ _SoundPlayNode::Connect(status_t error, const media_source& source, const media_
|
||||
|
||||
// reset our buffer duration, etc. to avoid later calculations
|
||||
bigtime_t duration = ((mOutput.format.u.raw_audio.buffer_size * 1000000LL)
|
||||
/ ((mOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) * mOutput.format.u.raw_audio.channel_count))
|
||||
/ ((mOutput.format.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
|
||||
* mOutput.format.u.raw_audio.channel_count))
|
||||
/ (int32)mOutput.format.u.raw_audio.frame_rate;
|
||||
SetBufferDuration(duration);
|
||||
TRACE("_SoundPlayNode::Connect: buffer duration is %Ld\n", duration);
|
||||
|
||||
mInternalLatency = (3 * BufferDuration()) / 4;
|
||||
TRACE("_SoundPlayNode::Connect: using %Ld as internal latency\n", mInternalLatency);
|
||||
TRACE("_SoundPlayNode::Connect: using %Ld as internal latency\n",
|
||||
mInternalLatency);
|
||||
SetEventLatency(mLatency + mInternalLatency);
|
||||
|
||||
// Set up the buffer group for our connection, as long as nobody handed us a
|
||||
// buffer group (via SetBufferGroup()) prior to this. That can happen, for example,
|
||||
// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
|
||||
// method.
|
||||
if (!mBufferGroup)
|
||||
// Set up the buffer group for our connection, as long as nobody handed us
|
||||
// a buffer group (via SetBufferGroup()) prior to this.
|
||||
// That can happen, for example, if the consumer calls SetOutputBuffersFor()
|
||||
// on us from within its Connected() method.
|
||||
if (!mBufferGroup)
|
||||
AllocateBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
_SoundPlayNode::Disconnect(const media_source& what, const media_destination& where)
|
||||
|
||||
void
|
||||
_SoundPlayNode::Disconnect(const media_source& what,
|
||||
const media_destination& where)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
|
||||
// is this our output?
|
||||
if (what != mOutput.source)
|
||||
{
|
||||
if (what != mOutput.source) {
|
||||
TRACE("_SoundPlayNode::Disconnect returning\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure that our connection is the one being disconnected
|
||||
if ((where == mOutput.destination) && (what == mOutput.source))
|
||||
{
|
||||
if (where == mOutput.destination && what == mOutput.source) {
|
||||
mOutput.destination = media_destination::null;
|
||||
mOutput.format.type = B_MEDIA_RAW_AUDIO;
|
||||
mOutput.format.u.raw_audio = media_multi_audio_format::wildcard;
|
||||
delete mBufferGroup;
|
||||
mBufferGroup = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
fprintf(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n",
|
||||
what.id, where.id, mOutput.source.id, mOutput.destination.id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
|
||||
|
||||
void
|
||||
_SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
|
||||
bigtime_t performanceTime)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
TRACE("_SoundPlayNode::LateNoticeReceived, %Ld too late at %Ld\n", how_much, performance_time);
|
||||
|
||||
|
||||
TRACE("_SoundPlayNode::LateNoticeReceived, %Ld too late at %Ld\n", howMuch,
|
||||
performanceTime);
|
||||
|
||||
// is this our output?
|
||||
if (what != mOutput.source)
|
||||
{
|
||||
if (what != mOutput.source) {
|
||||
TRACE("_SoundPlayNode::LateNoticeReceived returning\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (RunMode() != B_DROP_DATA)
|
||||
{
|
||||
if (RunMode() != B_DROP_DATA) {
|
||||
// We're late, and our run mode dictates that we try to produce buffers
|
||||
// earlier in order to catch up. This argues that the downstream nodes are
|
||||
// not properly reporting their latency, but there's not much we can do about
|
||||
// that at the moment, so we try to start producing buffers earlier to
|
||||
// compensate.
|
||||
|
||||
mInternalLatency += how_much;
|
||||
|
||||
mInternalLatency += howMuch;
|
||||
|
||||
if (mInternalLatency > 30000) // avoid getting a too high latency
|
||||
mInternalLatency = 30000;
|
||||
|
||||
SetEventLatency(mLatency + mInternalLatency);
|
||||
TRACE("_SoundPlayNode::LateNoticeReceived: increasing latency to %Ld\n", mLatency + mInternalLatency);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// The other run modes dictate various strategies for sacrificing data quality
|
||||
// in the interests of timely data delivery. The way *we* do this is to skip
|
||||
// a buffer, which catches us up in time by one buffer duration.
|
||||
@@ -465,61 +517,67 @@ _SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_SoundPlayNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
|
||||
|
||||
void
|
||||
_SoundPlayNode::EnableOutput(const media_source& what, bool enabled,
|
||||
int32* /* deprecated */)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
// If I had more than one output, I'd have to walk my list of output records to see
|
||||
// which one matched the given source, and then enable/disable that one. But this
|
||||
// node only has one output, so I just make sure the given source matches, then set
|
||||
// the enable state accordingly.
|
||||
// If I had more than one output, I'd have to walk my list of output
|
||||
// records to see which one matched the given source, and then
|
||||
// enable/disable that one.
|
||||
// But this node only has one output, so I just make sure the given source
|
||||
// matches, then set the enable state accordingly.
|
||||
|
||||
// is this our output?
|
||||
if (what != mOutput.source)
|
||||
{
|
||||
if (what != mOutput.source) {
|
||||
fprintf(stderr, "_SoundPlayNode::EnableOutput returning\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mOutputEnabled = enabled;
|
||||
}
|
||||
|
||||
void
|
||||
_SoundPlayNode::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
|
||||
|
||||
void
|
||||
_SoundPlayNode::AdditionalBufferRequested(const media_source& source,
|
||||
media_buffer_id previousBuffer, bigtime_t previousTime,
|
||||
const media_seek_tag* previousTag)
|
||||
{
|
||||
CALLED();
|
||||
// we don't support offline mode
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
_SoundPlayNode::LatencyChanged(const media_source& source, const media_destination& destination, bigtime_t new_latency, uint32 flags)
|
||||
|
||||
void
|
||||
_SoundPlayNode::LatencyChanged(const media_source& source,
|
||||
const media_destination& destination, bigtime_t newLatency, uint32 flags)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
TRACE("_SoundPlayNode::LatencyChanged: new_latency %Ld\n", new_latency);
|
||||
|
||||
|
||||
TRACE("_SoundPlayNode::LatencyChanged: new_latency %Ld\n", newLatency);
|
||||
|
||||
// something downstream changed latency, so we need to start producing
|
||||
// buffers earlier (or later) than we were previously. Make sure that the
|
||||
// connection that changed is ours, and adjust to the new downstream
|
||||
// latency if so.
|
||||
if ((source == mOutput.source) && (destination == mOutput.destination))
|
||||
{
|
||||
mLatency = new_latency;
|
||||
if (source == mOutput.source && destination == mOutput.destination) {
|
||||
mLatency = newLatency;
|
||||
SetEventLatency(mLatency + mInternalLatency);
|
||||
} else {
|
||||
TRACE("_SoundPlayNode::LatencyChanged: ignored\n");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// implementation for BMediaEventLooper
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void _SoundPlayNode::HandleEvent(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
// #pragma mark - implementation for BMediaEventLooper
|
||||
|
||||
|
||||
void
|
||||
_SoundPlayNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
switch (event->type) {
|
||||
@@ -539,9 +597,8 @@ void _SoundPlayNode::HandleEvent(
|
||||
// we don't get any buffers
|
||||
break;
|
||||
case SEND_NEW_BUFFER_EVENT:
|
||||
if (RunState() == BMediaEventLooper::B_STARTED) {
|
||||
if (RunState() == BMediaEventLooper::B_STARTED)
|
||||
SendNewBuffer(event, lateness, realTimeEvent);
|
||||
}
|
||||
break;
|
||||
case BTimedEventQueue::B_DATA_STATUS:
|
||||
HandleDataStatus(event,lateness,realTimeEvent);
|
||||
@@ -550,35 +607,41 @@ void _SoundPlayNode::HandleEvent(
|
||||
HandleParameter(event,lateness,realTimeEvent);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr," unknown event type: %li\n",event->type);
|
||||
fprintf(stderr," unknown event type: %li\n", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// protected:
|
||||
|
||||
// #pragma mark - protected methods
|
||||
|
||||
|
||||
// how should we handle late buffers? drop them?
|
||||
// notify the producer?
|
||||
status_t
|
||||
_SoundPlayNode::SendNewBuffer(const media_timed_event *event, bigtime_t lateness, bool realTimeEvent)
|
||||
status_t
|
||||
_SoundPlayNode::SendNewBuffer(const media_timed_event* event,
|
||||
bigtime_t lateness, bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
// printf("latency = %12Ld, event = %12Ld, sched = %5Ld, arrive at %12Ld, now %12Ld, current lateness %12Ld\n", EventLatency() + SchedulingLatency(), EventLatency(), SchedulingLatency(), event->event_time, TimeSource()->Now(), lateness);
|
||||
|
||||
// make sure we're both started *and* connected before delivering a buffer
|
||||
if ((RunState() != BMediaEventLooper::B_STARTED) || (mOutput.destination == media_destination::null))
|
||||
if (RunState() != BMediaEventLooper::B_STARTED
|
||||
|| mOutput.destination == media_destination::null)
|
||||
return B_OK;
|
||||
|
||||
// The event->event_time is the time at which the buffer we are preparing here should
|
||||
// arrive at it's destination. The MediaEventLooper should have scheduled us early enough
|
||||
// (based on EventLatency() and the SchedulingLatency()) to make this possible.
|
||||
|
||||
// The event->event_time is the time at which the buffer we are preparing
|
||||
// here should arrive at it's destination. The MediaEventLooper should have
|
||||
// scheduled us early enough (based on EventLatency() and the
|
||||
// SchedulingLatency()) to make this possible.
|
||||
// lateness is independent of EventLatency()!
|
||||
|
||||
if (lateness > (BufferDuration() / 3) ) {
|
||||
printf("_SoundPlayNode::SendNewBuffer, event scheduled much too late, lateness is %Ld\n", lateness);
|
||||
printf("_SoundPlayNode::SendNewBuffer, event scheduled much too late, "
|
||||
"lateness is %Ld\n", lateness);
|
||||
}
|
||||
|
||||
// skip buffer creation if output not enabled
|
||||
// skip buffer creation if output not enabled
|
||||
if (mOutputEnabled) {
|
||||
|
||||
// Get the next buffer of data
|
||||
@@ -607,7 +670,8 @@ _SoundPlayNode::SendNewBuffer(const media_timed_event *event, bigtime_t lateness
|
||||
if (B_OK != SendBuffer(buffer, mOutput.destination)) {
|
||||
// we need to recycle the buffer
|
||||
// if the call to SendBuffer() fails
|
||||
printf("_SoundPlayNode::SendNewBuffer: Buffer sending failed\n");
|
||||
printf("_SoundPlayNode::SendNewBuffer: Buffer sending "
|
||||
"failed\n");
|
||||
buffer->Recycle();
|
||||
}
|
||||
}
|
||||
@@ -615,27 +679,31 @@ _SoundPlayNode::SendNewBuffer(const media_timed_event *event, bigtime_t lateness
|
||||
|
||||
// track how much media we've delivered so far
|
||||
size_t nFrames = mOutput.format.u.raw_audio.buffer_size
|
||||
/ ((mOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
|
||||
/ ((mOutput.format.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
|
||||
* mOutput.format.u.raw_audio.channel_count);
|
||||
mFramesSent += nFrames;
|
||||
|
||||
// The buffer is on its way; now schedule the next one to go
|
||||
// nextEvent is the time at which the buffer should arrive at it's destination
|
||||
bigtime_t nextEvent = mStartTime + bigtime_t((1000000LL * mFramesSent) / (int32)mOutput.format.u.raw_audio.frame_rate);
|
||||
// nextEvent is the time at which the buffer should arrive at it's
|
||||
// destination
|
||||
bigtime_t nextEvent = mStartTime + bigtime_t((1000000LL * mFramesSent)
|
||||
/ (int32)mOutput.format.u.raw_audio.frame_rate);
|
||||
media_timed_event nextBufferEvent(nextEvent, SEND_NEW_BUFFER_EVENT);
|
||||
EventQueue()->AddEvent(nextBufferEvent);
|
||||
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleDataStatus(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleDataStatus(const media_timed_event* event,
|
||||
bigtime_t lateness, bool realTimeEvent)
|
||||
{
|
||||
TRACE("_SoundPlayNode::HandleDataStatus status: %li, lateness: %Li\n", event->data, lateness);
|
||||
switch(event->data) {
|
||||
TRACE("_SoundPlayNode::HandleDataStatus status: %li, lateness: %Li\n",
|
||||
event->data, lateness);
|
||||
|
||||
switch (event->data) {
|
||||
case B_DATA_NOT_AVAILABLE:
|
||||
break;
|
||||
case B_DATA_AVAILABLE:
|
||||
@@ -648,25 +716,24 @@ _SoundPlayNode::HandleDataStatus(
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleStart(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
// don't do anything if we're already running
|
||||
if (RunState() != B_STARTED)
|
||||
{
|
||||
// We want to start sending buffers now, so we set up the buffer-sending bookkeeping
|
||||
// and fire off the first "produce a buffer" event.
|
||||
|
||||
mFramesSent = 0;
|
||||
mStartTime = event->event_time;
|
||||
media_timed_event firstBufferEvent(event->event_time, SEND_NEW_BUFFER_EVENT);
|
||||
if (RunState() != B_STARTED) {
|
||||
// We want to start sending buffers now, so we set up the buffer-sending
|
||||
// bookkeeping and fire off the first "produce a buffer" event.
|
||||
|
||||
// Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
|
||||
// the event queue, like this:
|
||||
mFramesSent = 0;
|
||||
mStartTime = event->event_time;
|
||||
media_timed_event firstBufferEvent(event->event_time,
|
||||
SEND_NEW_BUFFER_EVENT);
|
||||
|
||||
// Alternatively, we could call HandleEvent() directly with this event,
|
||||
// to avoid a trip through the event queue, like this:
|
||||
//
|
||||
// this->HandleEvent(&firstBufferEvent, 0, false);
|
||||
//
|
||||
@@ -675,51 +742,50 @@ _SoundPlayNode::HandleStart(
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleSeek(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
TRACE("_SoundPlayNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n", event->event_time, event->data, event->bigdata);
|
||||
TRACE("_SoundPlayNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n",
|
||||
event->event_time, event->data, event->bigdata);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleWarp(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleWarp(const media_timed_event* event, bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleStop(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
// flush the queue so downstreamers don't get any more
|
||||
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, SEND_NEW_BUFFER_EVENT);
|
||||
|
||||
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
|
||||
SEND_NEW_BUFFER_EVENT);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleParameter(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
|
||||
status_t
|
||||
_SoundPlayNode::HandleParameter(const media_timed_event* event,
|
||||
bigtime_t lateness, bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
void
|
||||
_SoundPlayNode::AllocateBuffers()
|
||||
{
|
||||
CALLED();
|
||||
@@ -728,41 +794,47 @@ _SoundPlayNode::AllocateBuffers()
|
||||
size_t size = mOutput.format.u.raw_audio.buffer_size;
|
||||
int32 count = int32(mLatency / BufferDuration() + 1 + 1);
|
||||
|
||||
TRACE("_SoundPlayNode::AllocateBuffers: latency = %Ld, buffer duration = %Ld, count %ld\n", mLatency, BufferDuration(), count);
|
||||
|
||||
TRACE("_SoundPlayNode::AllocateBuffers: latency = %Ld, buffer duration "
|
||||
"= %Ld, count %ld\n", mLatency, BufferDuration(), count);
|
||||
|
||||
if (count < 3)
|
||||
count = 3;
|
||||
|
||||
TRACE("_SoundPlayNode::AllocateBuffers: creating group of %ld buffers, size = %lu\n", count, size);
|
||||
|
||||
|
||||
TRACE("_SoundPlayNode::AllocateBuffers: creating group of %ld buffers, "
|
||||
"size = %lu\n", count, size);
|
||||
|
||||
mBufferGroup = new BBufferGroup(size, count);
|
||||
if (mBufferGroup->InitCheck() != B_OK) {
|
||||
ERROR("_SoundPlayNode::AllocateBuffers: BufferGroup::InitCheck() failed\n");
|
||||
}
|
||||
ERROR("_SoundPlayNode::AllocateBuffers: BufferGroup::InitCheck() "
|
||||
"failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BBuffer*
|
||||
_SoundPlayNode::FillNextBuffer(bigtime_t event_time)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
// get a buffer from our buffer group
|
||||
BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration() / 2);
|
||||
BBuffer* buf = mBufferGroup->RequestBuffer(
|
||||
mOutput.format.u.raw_audio.buffer_size, BufferDuration() / 2);
|
||||
|
||||
// if we fail to get a buffer (for example, if the request times out), we skip this
|
||||
// buffer and go on to the next, to avoid locking up the control thread
|
||||
// If we fail to get a buffer (for example, if the request times out), we
|
||||
// skip this buffer and go on to the next, to avoid locking up the control
|
||||
// thread
|
||||
if (!buf) {
|
||||
ERROR("_SoundPlayNode::FillNextBuffer: RequestBuffer failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
if (mPlayer->HasData()) {
|
||||
mPlayer->PlayBuffer(buf->Data(),
|
||||
mPlayer->_PlayBuffer(buf->Data(),
|
||||
mOutput.format.u.raw_audio.buffer_size, mOutput.format.u.raw_audio);
|
||||
} else {
|
||||
memset(buf->Data(), 0, mOutput.format.u.raw_audio.buffer_size);
|
||||
}
|
||||
|
||||
|
||||
// fill in the buffer header
|
||||
media_header* hdr = buf->Header();
|
||||
hdr->type = B_MEDIA_RAW_AUDIO;
|
||||
|
||||
+328
-332
@@ -1,18 +1,27 @@
|
||||
/***********************************************************************
|
||||
* AUTHOR: Marcus Overhagen, Jérôme Duval
|
||||
* FILE: SoundPlayer.cpp
|
||||
* DESCR:
|
||||
***********************************************************************/
|
||||
#include <TimeSource.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <ParameterWeb.h>
|
||||
#include <Sound.h>
|
||||
/*
|
||||
* 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 "debug.h"
|
||||
#include <Autolock.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <ParameterWeb.h>
|
||||
#include <Sound.h>
|
||||
#include <TimeSource.h>
|
||||
|
||||
#include "SoundPlayNode.h"
|
||||
#include "SoundPlayer.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#define atomic_read(a) atomic_or(a, 0)
|
||||
|
||||
@@ -28,57 +37,47 @@ enum {
|
||||
static BSoundPlayer::play_id sCurrentPlayID = 1;
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* 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)
|
||||
BSoundPlayer::BSoundPlayer(const char* name, BufferPlayerFunc playerFunction,
|
||||
EventNotifierFunc eventNotifierFunction, void* cookie)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
TRACE("BSoundPlayer::BSoundPlayer: default constructor used\n");
|
||||
|
||||
media_multi_audio_format fmt = media_multi_audio_format::wildcard;
|
||||
media_multi_audio_format format = media_multi_audio_format::wildcard;
|
||||
|
||||
Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie);
|
||||
_Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction,
|
||||
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),
|
||||
void (*Notifier)(void *, sound_player_notification what, ...),
|
||||
void * 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 fmt = media_multi_audio_format::wildcard;
|
||||
*(media_raw_audio_format *)&fmt = *format;
|
||||
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 = fmt;
|
||||
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, &fmt, name, NULL, PlayBuffer, Notifier, cookie);
|
||||
_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,
|
||||
void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
|
||||
void (*Notifier)(void *, sound_player_notification what, ...),
|
||||
void * 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();
|
||||
|
||||
@@ -94,216 +93,66 @@ BSoundPlayer::BSoundPlayer(const media_node & toNode,
|
||||
TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
|
||||
#endif
|
||||
|
||||
Init(&toNode, format, name, input, PlayBuffer, Notifier, cookie);
|
||||
_Init(&toNode, format, name, input, playerFunction, eventNotifierFunction,
|
||||
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();
|
||||
fPlayingSounds = NULL;
|
||||
fWaitingSounds = NULL;
|
||||
|
||||
fPlayerNode = NULL;
|
||||
fPlayBufferFunc = PlayBuffer;
|
||||
if (fPlayBufferFunc == NULL) {
|
||||
fPlayBufferFunc = _SoundPlayBufferFunc;
|
||||
fCookie = this;
|
||||
} else
|
||||
fCookie = cookie;
|
||||
|
||||
fNotifierFunc = Notifier;
|
||||
fVolumeDB = 0.0f;
|
||||
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;
|
||||
}
|
||||
|
||||
// find a free media_input
|
||||
if (!input) {
|
||||
err = roster->GetFreeInputsFor(inputNode, &_input, 1, &inputCount, B_MEDIA_RAW_AUDIO);
|
||||
if (err != B_OK) {
|
||||
TRACE("BSoundPlayer::Init: Couldn't GetFreeInputsFor\n");
|
||||
goto the_end;
|
||||
}
|
||||
if (inputCount < 1) {
|
||||
TRACE("BSoundPlayer::Init: Couldn't find a free input\n");
|
||||
err = B_ERROR;
|
||||
goto the_end;
|
||||
}
|
||||
} else {
|
||||
_input = *input;
|
||||
}
|
||||
|
||||
// find a free media_output
|
||||
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;
|
||||
}
|
||||
if (outputCount < 1) {
|
||||
TRACE("BSoundPlayer::Init: Couldn't find a free output\n");
|
||||
err = B_ERROR;
|
||||
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;
|
||||
|
||||
#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
|
||||
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();
|
||||
|
||||
TRACE("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();
|
||||
|
||||
if (fFlags & F_IS_STARTED) {
|
||||
Stop(true, false); // block, but don't flush
|
||||
if ((fFlags & F_IS_STARTED) != 0) {
|
||||
// block, but don't flush
|
||||
Stop(true, false);
|
||||
}
|
||||
|
||||
status_t err;
|
||||
BMediaRoster *roster = BMediaRoster::Roster();
|
||||
if (!roster) {
|
||||
BMediaRoster* roster = BMediaRoster::Roster();
|
||||
if (roster == NULL) {
|
||||
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.
|
||||
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 DEBUG >0
|
||||
if (err) {
|
||||
TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: %ld (%s)\n", err, strerror(err));
|
||||
#if DEBUG > 0
|
||||
if (err != B_OK) {
|
||||
TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: "
|
||||
"%ld (%s)\n", err, strerror(err));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (fFlags & F_MUST_RELEASE_MIXER) {
|
||||
if ((fFlags & F_MUST_RELEASE_MIXER) != 0) {
|
||||
// Release the mixer as it was acquired
|
||||
// through BMediaRoster::GetAudioMixer()
|
||||
err = roster->ReleaseNode(fMediaInput.node);
|
||||
#if DEBUG >0
|
||||
if (err) {
|
||||
TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: %ld (%s)\n", err, strerror(err));
|
||||
#if DEBUG > 0
|
||||
if (err != B_OK) {
|
||||
TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: "
|
||||
"%ld (%s)\n", err, strerror(err));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// do not delete fVolumeSlider, it belongs to the parameter web
|
||||
delete fParameterWeb;
|
||||
// do not delete fVolumeSlider, it belonged to the parameter web
|
||||
}
|
||||
|
||||
|
||||
@@ -335,10 +184,10 @@ BSoundPlayer::Start()
|
||||
if ((fFlags & F_NODES_CONNECTED) == 0)
|
||||
return B_NO_INIT;
|
||||
|
||||
if (fFlags & F_IS_STARTED)
|
||||
if ((fFlags & F_IS_STARTED) != 0)
|
||||
return B_OK;
|
||||
|
||||
BMediaRoster *roster = BMediaRoster::Roster();
|
||||
BMediaRoster* roster = BMediaRoster::Roster();
|
||||
if (!roster) {
|
||||
TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
|
||||
return B_ERROR;
|
||||
@@ -354,13 +203,14 @@ BSoundPlayer::Start()
|
||||
// buffers through the node chain, otherwise it'll start
|
||||
// up already late
|
||||
|
||||
status_t err = roster->StartNode(fPlayerNode->Node(), fPlayerNode->TimeSource()->Now() + Latency() + 5000);
|
||||
status_t err = roster->StartNode(fPlayerNode->Node(),
|
||||
fPlayerNode->TimeSource()->Now() + Latency() + 5000);
|
||||
if (err != B_OK) {
|
||||
TRACE("BSoundPlayer::Start: StartNode failed, %ld", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (fNotifierFunc)
|
||||
if (fNotifierFunc != NULL)
|
||||
fNotifierFunc(fCookie, B_STARTED, this);
|
||||
|
||||
SetHasData(true);
|
||||
@@ -371,8 +221,7 @@ BSoundPlayer::Start()
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::Stop(bool block,
|
||||
bool flush)
|
||||
BSoundPlayer::Stop(bool block, bool flush)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
@@ -381,12 +230,11 @@ BSoundPlayer::Stop(bool block,
|
||||
if ((fFlags & F_NODES_CONNECTED) == 0)
|
||||
return;
|
||||
|
||||
// XXX flush is ignored
|
||||
// TODO: flush is ignored
|
||||
|
||||
if (fFlags & F_IS_STARTED) {
|
||||
|
||||
BMediaRoster *roster = BMediaRoster::Roster();
|
||||
if (!roster) {
|
||||
if ((fFlags & F_IS_STARTED) != 0) {
|
||||
BMediaRoster* roster = BMediaRoster::Roster();
|
||||
if (roster == NULL) {
|
||||
TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n");
|
||||
return;
|
||||
}
|
||||
@@ -398,13 +246,15 @@ BSoundPlayer::Stop(bool block,
|
||||
|
||||
if (block) {
|
||||
// wait until the node is stopped
|
||||
int maxtrys;
|
||||
for (maxtrys = 250; fPlayerNode->IsPlaying() && maxtrys != 0; maxtrys--)
|
||||
int tries;
|
||||
for (tries = 250; fPlayerNode->IsPlaying() && tries != 0; tries--)
|
||||
snooze(2000);
|
||||
|
||||
DEBUG_ONLY(if (maxtrys == 0) TRACE("BSoundPlayer::Stop: waiting for node stop failed\n"));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -431,7 +281,8 @@ BSoundPlayer::Latency()
|
||||
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));
|
||||
TRACE("BSoundPlayer::Latency: GetLatencyFor failed %ld (%s)\n", err,
|
||||
strerror(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -452,7 +303,7 @@ BSoundPlayer::SetHasData(bool has_data)
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ bool
|
||||
bool
|
||||
BSoundPlayer::HasData()
|
||||
{
|
||||
CALLED();
|
||||
@@ -469,12 +320,12 @@ BSoundPlayer::BufferPlayer() const
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::SetBufferPlayer(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format))
|
||||
BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction)
|
||||
{
|
||||
CALLED();
|
||||
fLocker.Lock();
|
||||
fPlayBufferFunc = PlayBuffer;
|
||||
fLocker.Unlock();
|
||||
BAutolock _(fLocker);
|
||||
|
||||
fPlayBufferFunc = playerFunction;
|
||||
}
|
||||
|
||||
|
||||
@@ -486,16 +337,17 @@ BSoundPlayer::EventNotifier() const
|
||||
}
|
||||
|
||||
|
||||
void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notification what, ...))
|
||||
void
|
||||
BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction)
|
||||
{
|
||||
CALLED();
|
||||
fLocker.Lock();
|
||||
fNotifierFunc = Notifier;
|
||||
fLocker.Unlock();
|
||||
BAutolock _(fLocker);
|
||||
|
||||
fNotifierFunc = eventNotifierFunction;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
void*
|
||||
BSoundPlayer::Cookie() const
|
||||
{
|
||||
CALLED();
|
||||
@@ -507,31 +359,30 @@ void
|
||||
BSoundPlayer::SetCookie(void *cookie)
|
||||
{
|
||||
CALLED();
|
||||
fLocker.Lock();
|
||||
BAutolock _(fLocker);
|
||||
|
||||
fCookie = 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)
|
||||
BSoundPlayer::SetCallbacks(BufferPlayerFunc playerFunction,
|
||||
EventNotifierFunc eventNotifierFunction, void* cookie)
|
||||
{
|
||||
CALLED();
|
||||
fLocker.Lock();
|
||||
SetBufferPlayer(PlayBuffer);
|
||||
SetNotifier(Notifier);
|
||||
BAutolock _(fLocker);
|
||||
|
||||
SetBufferPlayer(playerFunction);
|
||||
SetNotifier(eventNotifierFunction);
|
||||
SetCookie(cookie);
|
||||
fLocker.Unlock();
|
||||
}
|
||||
|
||||
|
||||
/* 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 "
|
||||
*/
|
||||
/*! 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()
|
||||
{
|
||||
@@ -542,10 +393,10 @@ BSoundPlayer::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.
|
||||
*/
|
||||
/*! 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()
|
||||
{
|
||||
@@ -564,15 +415,16 @@ BSoundPlayer::Preroll()
|
||||
if ((fFlags & F_NODES_CONNECTED) == 0)
|
||||
return B_NO_INIT;
|
||||
|
||||
BMediaRoster *roster = BMediaRoster::Roster();
|
||||
if (!roster) {
|
||||
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: %ld (%s)\n", err, strerror(err));
|
||||
TRACE("BSoundPlayer::Preroll: Error while PrerollNode: %ld (%s)\n",
|
||||
err, strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -581,14 +433,14 @@ BSoundPlayer::Preroll()
|
||||
|
||||
|
||||
BSoundPlayer::play_id
|
||||
BSoundPlayer::StartPlaying(BSound *sound, bigtime_t atTime)
|
||||
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)
|
||||
BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime, float withVolume)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
@@ -671,9 +523,10 @@ BSoundPlayer::StopPlaying(play_id id)
|
||||
if (!fLocker.Lock())
|
||||
return B_ERROR;
|
||||
|
||||
_playing_sound **link = &fPlayingSounds;
|
||||
_playing_sound *item = fPlayingSounds;
|
||||
while (item) {
|
||||
_playing_sound** link = &fPlayingSounds;
|
||||
_playing_sound* item = fPlayingSounds;
|
||||
|
||||
while (item != NULL) {
|
||||
if (item->id == id) {
|
||||
*link = item->next;
|
||||
sem_id waitSem = item->wait_sem;
|
||||
@@ -681,7 +534,7 @@ BSoundPlayer::StopPlaying(play_id id)
|
||||
free(item);
|
||||
fLocker.Unlock();
|
||||
|
||||
NotifySoundDone(id, true);
|
||||
_NotifySoundDone(id, true);
|
||||
if (waitSem >= 0)
|
||||
release_sem(waitSem);
|
||||
|
||||
@@ -704,8 +557,8 @@ BSoundPlayer::WaitForSound(play_id id)
|
||||
if (!fLocker.Lock())
|
||||
return B_ERROR;
|
||||
|
||||
_playing_sound *item = fPlayingSounds;
|
||||
while (item) {
|
||||
_playing_sound* item = fPlayingSounds;
|
||||
while (item != NULL) {
|
||||
if (item->id == id) {
|
||||
sem_id waitSem = item->wait_sem;
|
||||
if (waitSem < 0)
|
||||
@@ -732,10 +585,10 @@ BSoundPlayer::Volume()
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::SetVolume(float new_volume)
|
||||
BSoundPlayer::SetVolume(float newVolume)
|
||||
{
|
||||
CALLED();
|
||||
SetVolumeDB(20.0 * log10(new_volume));
|
||||
SetVolumeDB(20.0 * log10(newVolume));
|
||||
}
|
||||
|
||||
|
||||
@@ -746,7 +599,7 @@ BSoundPlayer::VolumeDB(bool forcePoll)
|
||||
if (!fVolumeSlider)
|
||||
return -94.0f; // silence
|
||||
|
||||
if (!forcePoll && (system_time() - fLastVolumeUpdate < 500000))
|
||||
if (!forcePoll && system_time() - fLastVolumeUpdate < 500000)
|
||||
return fVolumeDB;
|
||||
|
||||
int32 count = fVolumeSlider->CountChannels();
|
||||
@@ -761,69 +614,64 @@ BSoundPlayer::VolumeDB(bool forcePoll)
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::SetVolumeDB(float volume_dB)
|
||||
BSoundPlayer::SetVolumeDB(float volumeDB)
|
||||
{
|
||||
CALLED();
|
||||
if (!fVolumeSlider)
|
||||
return;
|
||||
|
||||
float min_dB = fVolumeSlider->MinValue();
|
||||
float max_dB = fVolumeSlider->MaxValue();
|
||||
if (volume_dB < min_dB)
|
||||
volume_dB = min_dB;
|
||||
if (volume_dB > max_dB)
|
||||
volume_dB = max_dB;
|
||||
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] = volume_dB;
|
||||
values[i] = volumeDB;
|
||||
fVolumeSlider->SetValue(values, sizeof(float) * count, 0);
|
||||
|
||||
fVolumeDB = volume_dB;
|
||||
fVolumeDB = volumeDB;
|
||||
fLastVolumeUpdate = system_time();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BSoundPlayer::GetVolumeInfo(media_node *out_node,
|
||||
int32 *out_parameter_id,
|
||||
float *out_min_dB,
|
||||
float *out_max_dB)
|
||||
BSoundPlayer::GetVolumeInfo(media_node* _node, int32* _parameterID,
|
||||
float* _minDB, float* _maxDB)
|
||||
{
|
||||
CALLED();
|
||||
if (!fVolumeSlider)
|
||||
if (fVolumeSlider == NULL)
|
||||
return B_NO_INIT;
|
||||
|
||||
if (out_node)
|
||||
*out_node = fMediaInput.node;
|
||||
if (out_parameter_id)
|
||||
*out_parameter_id = fVolumeSlider->ID();
|
||||
if (out_min_dB)
|
||||
*out_min_dB = fVolumeSlider->MinValue();
|
||||
if (out_max_dB)
|
||||
*out_max_dB = fVolumeSlider->MaxValue();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* protected BSoundPlayer
|
||||
*************************************************************/
|
||||
// #pragma mark - protected BSoundPlayer
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::SetInitError(status_t in_error)
|
||||
BSoundPlayer::SetInitError(status_t error)
|
||||
{
|
||||
CALLED();
|
||||
fInitStatus = in_error;
|
||||
fInitStatus = error;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* private BSoundPlayer
|
||||
*************************************************************/
|
||||
// #pragma mark - private BSoundPlayer
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::_SoundPlayBufferFunc(void *cookie, void *buffer, size_t size,
|
||||
@@ -861,26 +709,175 @@ BSoundPlayer::_SoundPlayBufferFunc(void *cookie, void *buffer, size_t size,
|
||||
}
|
||||
|
||||
|
||||
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; }
|
||||
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::NotifySoundDone(play_id id, bool gotToPlay)
|
||||
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();
|
||||
Notify(B_SOUND_DONE, id, gotToPlay);
|
||||
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 _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 %ld has timesource %ld\n",
|
||||
fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::get_volume_slider()
|
||||
BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay)
|
||||
{
|
||||
CALLED();
|
||||
_Notify(B_SOUND_DONE, id, gotToPlay);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BSoundPlayer::_GetVolumeSlider()
|
||||
{
|
||||
CALLED();
|
||||
|
||||
@@ -888,12 +885,12 @@ BSoundPlayer::get_volume_slider()
|
||||
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (!roster) {
|
||||
TRACE("BSoundPlayer::get_volume_slider failed to get BMediaRoster");
|
||||
TRACE("BSoundPlayer::_GetVolumeSlider failed to get BMediaRoster");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) {
|
||||
TRACE("BSoundPlayer::get_volume_slider couldn't get parameter web");
|
||||
TRACE("BSoundPlayer::_GetVolumeSlider couldn't get parameter web");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -912,14 +909,14 @@ BSoundPlayer::get_volume_slider()
|
||||
|
||||
#if DEBUG >0
|
||||
if (!fVolumeSlider) {
|
||||
TRACE("BSoundPlayer::get_volume_slider couldn't find volume control");
|
||||
TRACE("BSoundPlayer::_GetVolumeSlider couldn't find volume control");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ void
|
||||
BSoundPlayer::Notify(sound_player_notification what, ...)
|
||||
void
|
||||
BSoundPlayer::_Notify(sound_player_notification what, ...)
|
||||
{
|
||||
CALLED();
|
||||
if (fLocker.Lock()) {
|
||||
@@ -930,8 +927,9 @@ BSoundPlayer::Notify(sound_player_notification what, ...)
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ void
|
||||
BSoundPlayer::PlayBuffer(void *buffer, size_t size, const media_raw_audio_format &format)
|
||||
void
|
||||
BSoundPlayer::_PlayBuffer(void* buffer, size_t size,
|
||||
const media_raw_audio_format& format)
|
||||
{
|
||||
if (fLocker.Lock()) {
|
||||
if (fPlayBufferFunc)
|
||||
@@ -941,19 +939,17 @@ BSoundPlayer::PlayBuffer(void *buffer, size_t size, const media_raw_audio_format
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* public sound_error
|
||||
*************************************************************/
|
||||
// #pragma mark - public sound_error
|
||||
|
||||
|
||||
sound_error::sound_error(const char *str)
|
||||
sound_error::sound_error(const char* string)
|
||||
{
|
||||
m_str_const = str;
|
||||
m_str_const = string;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
sound_error::what() const throw ()
|
||||
const char*
|
||||
sound_error::what() const throw()
|
||||
{
|
||||
return m_str_const;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user