📄 soundconsumer.cxx
字号:
/*******************************************************************************
/
/ File: SoundConsumer.cpp
/
/ Description: Record sound from some sound-producing Node.
/
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
/
*******************************************************************************/
#include <stdio.h>
#include <OS.h>
#include <scheduler.h>
#include <Buffer.h>
#include <TimeSource.h>
#include "SoundPrivate.h"
#include "SoundConsumer.h"
template<class C> class array_delete {
C * & m_ptr;
public:
// auto_ptr<> uses delete, not delete[], so we have to write our own.
// I like hanging on to a reference, because if we manually delete the
// array and set the pointer to NULL (or otherwise change the pointer)
// it will still work. Others like the more elaborate implementation
// of auto_ptr<>. Your Mileage May Vary.
array_delete(C * & ptr) : m_ptr(ptr) {}
~array_delete() { delete[] m_ptr; }
};
// If we don't mind the format changing to another format while
// running, we can define this to 1. Look for the symbol down in the source.
#define ACCEPT_ANY_FORMAT_CHANGE 0
// Compiling with NDEBUG means "release" -- it also turns off assert() and
// other such debugging aids. By contrast, DEBUG turns on extra debugging
// in some programs.
#if !NDEBUG
#define FPRINTF fprintf
#else
#define FPRINTF (void)
#endif
// Comment out the FPRINTF part of these lines to reduce verbiage.
// Enabling MESSAGE will kill performance on slower machines, because it
// prints for each message received (including each buffer).
#define NODE //FPRINTF
#define MESSAGE //FPRINTF
SoundConsumer::SoundConsumer(
const char * name,
SoundProcessFunc recordFunc,
SoundNotifyFunc notifyFunc,
void * cookie) :
BMediaNode(name ? name : "SoundConsumer"),
BBufferConsumer(B_MEDIA_RAW_AUDIO)
{
NODE(stderr, "SoundConsumer::SoundConsumer(%p, %p, %p, %p)\n",
name, recordFunc, notifyFunc, cookie);
if (!name) name = "SoundConsumer";
// Set up the hook functions.
m_recordHook = recordFunc;
m_notifyHook = notifyFunc;
m_cookie = cookie;
// Create the port that we publish as our Control Port.
char pname[32];
sprintf(pname, "%.20s Control", name);
m_port = create_port(10, pname);
// Initialize our single media_input. Make sure it knows
// the Control Port associated with the destination, and
// the index of the destination (since we only have one,
// that's trivial).
m_input.destination.port = m_port;
m_input.destination.id = 1;
sprintf(m_input.name, "%.20s Input", name);
// Set up the timing variables that we'll be using.
m_trTimeout = 0LL;
m_tpSeekAt = 0;
m_tmSeekTo = 0;
m_delta = 0;
m_seeking = false;
// Create, and run, the thread that we use to service
// the Control Port.
sprintf(pname, "%.20s Service", name);
m_thread = spawn_thread(ThreadEntry, pname, 110, this);
resume_thread(m_thread);
}
SoundConsumer::~SoundConsumer()
{
NODE(stderr, "SoundConsumer::~SoundConsumer()\n");
// Signal to our thread that it's time to go home.
write_port(m_port, MSG_QUIT_NOW, 0, 0);
status_t s;
while (wait_for_thread(m_thread, &s) == B_INTERRUPTED)
NODE(stderr, "wait_for_thread() B_INTERRUPTED\n");
delete_port(m_port);
}
status_t
SoundConsumer::SetHooks(
SoundProcessFunc recordFunc,
SoundNotifyFunc notifyFunc,
void * cookie)
{
// SetHooks needs to be synchronized with the service thread, else we may
// call the wrong hook function with the wrong cookie, which would be bad.
// Rather than do locking, which is expensive, we streamline the process
// by sending our service thread a request to change the hooks, and waiting
// for the acknowledge.
status_t err = B_OK;
set_hooks_q cmd;
cmd.process = recordFunc;
cmd.notify = notifyFunc;
cmd.cookie = cookie;
// If we're not in the service thread, we need to round-trip a message.
if (find_thread(0) != m_thread) {
cmd.reply = create_port(1, "SetHooks reply");
// Send the private message to our service thread.
err = write_port(ControlPort(), MSG_CHANGE_HOOKS, &cmd, sizeof(cmd));
if (err >= 0) {
int32 code;
// Wait for acknowledge from the service thread.
err = read_port_etc(cmd.reply, &code, 0, 0, B_TIMEOUT, 6000000LL);
if (err > 0) err = 0;
NODE(stderr, "SoundConsumer::SetHooks read reply: %#010lx\n", err);
}
// Clean up.
delete_port(cmd.reply);
}
else {
// Within the service thread, it's OK to just go ahead and do the change.
DoHookChange(&cmd);
}
return err;
}
////////////////////////////////////////////////////////////////////////////////
//
// BMediaNode-derived methods
//
////////////////////////////////////////////////////////////////////////////////
port_id SoundConsumer::ControlPort() const
{
return m_port;
}
BMediaAddOn* SoundConsumer::AddOn(
int32 * internal_id) const
{
// This object is instantiated inside an application.
// Therefore, it has no add-on.
if (internal_id) *internal_id = 0;
return 0;
}
void SoundConsumer::Start(
bigtime_t performance_time)
{
// Since we are a consumer and just blindly accept buffers that are
// thrown at us, we don't need to do anything special in Start()/Stop().
// If we were (also) a producer, we'd have to be more elaborate.
// The only thing we do is immediately perform any queued Seek based on
// the start time, which is the right thing to do (seeing as we were
// Seek()-ed when we weren't started).
if (m_seeking) {
m_delta = performance_time - m_tmSeekTo;
m_seeking = false;
}
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_WILL_START, performance_time);
}
else {
Notify(B_WILL_START, performance_time);
}
}
void SoundConsumer::Stop(
bigtime_t performance_time,
bool immediate)
{
// Since we are a consumer and just blindly accept buffers that are
// thrown at us, we don't need to do anything special in Start()/Stop().
// If we were (also) a producer, we'd have to be more elaborate.
// Note that this is not strictly in conformance with The Rules,
// but since this is not an add-on Node for use with any application;
// it's a Node over which we have complete control, we can live with
// treating buffers received before the start time or after the stop
// time as any other buffer.
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_WILL_STOP, performance_time, immediate);
}
else {
Notify(B_WILL_STOP, performance_time, immediate);
}
}
void SoundConsumer::Seek(
bigtime_t media_time,
bigtime_t performance_time)
{
// Seek() on a consumer just serves to offset the time stamp
// of received buffers passed to our Record hook function.
// In the hook function, you may wish to save those time stamps
// to disk or otherwise store them. You may also want to
// synchronize this node's media time with an upstream
// producer's media time to make this offset meaningful.
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_WILL_SEEK, performance_time, media_time);
}
else {
Notify(B_WILL_SEEK, performance_time, media_time);
}
m_tpSeekAt = performance_time;
m_tmSeekTo = media_time;
m_seeking = true;
}
void SoundConsumer::SetRunMode(
run_mode mode)
{
if (mode == BMediaNode::B_OFFLINE) {
// BMediaNode::B_OFFLINE means we don't need to run in
// real time. So, we shouldn't run as a real time
// thread.
int32 new_prio = suggest_thread_priority(B_OFFLINE_PROCESSING);
set_thread_priority(m_thread, new_prio);
}
else {
// We're running in real time, so we'd better have
// a big enough thread priority to handle it!
// Here's where those magic scheduler values
// come from:
//
// * In the worst case, we process one buffer per
// reschedule (we get rescheduled when we go to
// look for a message on our Control Port), so
// in order to keep up with the incoming buffers,
// the duration of one buffer becomes our
// scheduling period. If we don't know anything
// about the buffers, we pick a reasonable
// default.
// * We're a simple consumer, so we don't have to
// be too picky about the jitter. Half a period
// of jitter means that we'd have to get two
// consecutive worst-case reschedules before
// we'd fall behind.
// * The amount of time we spend processing is
// our ProcessingLatency().
bigtime_t period = 10000;
if (buffer_duration(m_input.format.u.raw_audio) > 0) {
period = buffer_duration(m_input.format.u.raw_audio);
}
// assuming we're running for 500 us or less per buffer
int32 new_prio = suggest_thread_priority(B_AUDIO_RECORDING,
period, period/2, ProcessingLatency());
set_thread_priority(m_thread, new_prio);
}
}
void SoundConsumer::TimeWarp(
bigtime_t at_real_time,
bigtime_t to_performance_time)
{
// Since buffers will come pre-time-stamped, we only need to look
// at them, so we can ignore the time warp as a consumer.
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_WILL_TIMEWARP, at_real_time, to_performance_time);
}
else {
Notify(B_WILL_TIMEWARP, at_real_time, to_performance_time);
}
}
void SoundConsumer::Preroll()
{
// There is nothing for us to do in Preroll()
}
void SoundConsumer::SetTimeSource(
BTimeSource * /* time_source */)
{
// We don't need to do anything special to take note of the
// fact that the time source changed, because we get our timing
// information from the buffers we receive.
}
status_t SoundConsumer::HandleMessage(
int32 message,
const void * data,
size_t size)
{
// Check with each of our superclasses to see if they
// understand the message. If none of them do, call
// BMediaNode::HandleBadMessage().
if ((BBufferConsumer::HandleMessage(message, data, size) < 0)
&& (BMediaNode::HandleMessage(message, data, size) < 0))
{
HandleBadMessage(message, data, size);
return B_ERROR;
}
return B_OK;
}
////////////////////////////////////////////////////////////////////////////////
//
// BBufferConsumer-derived methods
//
////////////////////////////////////////////////////////////////////////////////
status_t SoundConsumer::AcceptFormat(
const media_destination & dest,
media_format * format)
{
// We only accept formats aimed at our single input.
if (dest != m_input.destination) {
return B_MEDIA_BAD_DESTINATION;
}
// If no format is specified, we say we want raw audio.
if (format->type <= 0) {
format->type = B_MEDIA_RAW_AUDIO;
format->u.raw_audio = media_raw_audio_format::wildcard;
}
// If a non-raw-audio format is specified, we tell the world what
// we want, and that the specified format was unacceptable to us.
else if (format->type != B_MEDIA_RAW_AUDIO) {
format->type = B_MEDIA_RAW_AUDIO;
format->u.raw_audio = media_raw_audio_format::wildcard;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -