* Use shared AutoDeleter.h instead of custom array deleter.
* Improve code in FileUtils to copy attributes in chunks and improve error checking. Should also copy 0 size attributes now, since those are valid. * Craft SoundConsumer towards more style guide compliance. * Use new (std::nothrow) and check result. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26717 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -7,14 +7,22 @@
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fs_attr.h>
|
||||
#include "array_delete.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
status_t CopyFileData(BFile& dst, BFile& src)
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fs_attr.h>
|
||||
|
||||
#include "AutoDeleter.h"
|
||||
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
|
||||
status_t
|
||||
CopyFileData(BFile& dst, BFile& src)
|
||||
{
|
||||
struct stat src_stat;
|
||||
status_t err = src.GetStat(&src_stat);
|
||||
@@ -24,12 +32,13 @@ status_t CopyFileData(BFile& dst, BFile& src)
|
||||
}
|
||||
|
||||
size_t bufSize = src_stat.st_blksize;
|
||||
if (! bufSize) {
|
||||
if (bufSize == 0)
|
||||
bufSize = 32768;
|
||||
}
|
||||
|
||||
char* buf = new char[bufSize];
|
||||
array_delete<char> bufDelete(buf);
|
||||
char* buf = new (nothrow) char[bufSize];
|
||||
if (buf == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ArrayDeleter<char> _(buf);
|
||||
|
||||
printf("copy data, bufSize = %ld\n", bufSize);
|
||||
// copy data
|
||||
@@ -38,14 +47,18 @@ status_t CopyFileData(BFile& dst, BFile& src)
|
||||
if (bytes > 0) {
|
||||
ssize_t result = dst.Write(buf, bytes);
|
||||
if (result != bytes) {
|
||||
printf("result = %#010lx, bytes = %#010lx\n", (uint32) result,
|
||||
(uint32) bytes);
|
||||
return B_ERROR;
|
||||
fprintf(stderr, "Failed to write %ld bytes: %s\n", bytes,
|
||||
strerror((status_t)result));
|
||||
if (result < 0)
|
||||
return (status_t)result;
|
||||
else
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (bytes < 0) {
|
||||
printf(" bytes = %#010lx\n", (uint32) bytes);
|
||||
return bytes;
|
||||
fprintf(stderr, "Failed to read file: %s\n", strerror(
|
||||
(status_t)bytes));
|
||||
return (status_t)bytes;
|
||||
} else {
|
||||
// EOF
|
||||
break;
|
||||
@@ -64,25 +77,49 @@ status_t CopyFileData(BFile& dst, BFile& src)
|
||||
}
|
||||
|
||||
|
||||
status_t CopyAttributes(BNode& dst, BNode& src)
|
||||
status_t
|
||||
CopyAttributes(BNode& dst, BNode& src)
|
||||
{
|
||||
// copy attributes
|
||||
src.RewindAttrs();
|
||||
char name[B_ATTR_NAME_LENGTH];
|
||||
while (src.GetNextAttrName(name) == B_OK) {
|
||||
char attrName[B_ATTR_NAME_LENGTH];
|
||||
while (src.GetNextAttrName(attrName) == B_OK) {
|
||||
attr_info info;
|
||||
if (src.GetAttrInfo(name, &info) == B_OK) {
|
||||
size_t bufSize = info.size;
|
||||
char* buf = new char[bufSize];
|
||||
array_delete<char> bufDelete = buf;
|
||||
|
||||
// copy one attribute
|
||||
ssize_t bytes = src.ReadAttr(name, info.type, 0, buf, bufSize);
|
||||
if (bytes > 0) {
|
||||
dst.WriteAttr(name, info.type, 0, buf, bufSize);
|
||||
} else {
|
||||
return bytes;
|
||||
if (src.GetAttrInfo(attrName, &info) != B_OK) {
|
||||
fprintf(stderr, "Failed to read info for attribute '%s'\n",
|
||||
attrName);
|
||||
continue;
|
||||
}
|
||||
// copy one attribute in chunks of 4096 bytes
|
||||
size_t size = 4096;
|
||||
uint8 buffer[size];
|
||||
off_t offset = 0;
|
||||
ssize_t read = src.ReadAttr(attrName, info.type, offset, buffer,
|
||||
min_c(size, info.size));
|
||||
if (read < 0) {
|
||||
fprintf(stderr, "Error reading attribute '%s'\n", attrName);
|
||||
return (status_t)read;
|
||||
}
|
||||
// NOTE: Attributes of size 0 are perfectly valid!
|
||||
while (read >= 0) {
|
||||
ssize_t written = dst.WriteAttr(attrName, info.type, offset, buffer,
|
||||
read);
|
||||
if (written != read) {
|
||||
fprintf(stderr, "Error writing attribute '%s'\n", attrName);
|
||||
if (written < 0)
|
||||
return (status_t)written;
|
||||
else
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
offset += read;
|
||||
read = src.ReadAttr(attrName, info.type, offset, buffer,
|
||||
min_c(size, info.size - offset));
|
||||
if (read < 0) {
|
||||
fprintf(stderr, "Error reading attribute '%s'\n", attrName);
|
||||
return (status_t)read;
|
||||
}
|
||||
if (read == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +127,8 @@ status_t CopyAttributes(BNode& dst, BNode& src)
|
||||
}
|
||||
|
||||
|
||||
status_t CopyFile(BFile& dst, BFile& src)
|
||||
status_t
|
||||
CopyFile(BFile& dst, BFile& src)
|
||||
{
|
||||
status_t err = CopyFileData(dst, src);
|
||||
if (err != B_OK)
|
||||
|
||||
@@ -2,6 +2,8 @@ SubDir HAIKU_TOP src apps soundrecorder ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
|
||||
UsePrivateHeaders shared ;
|
||||
|
||||
Application SoundRecorder :
|
||||
DrawButton.cpp
|
||||
DrawingTidbits.cpp
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
#include "RecorderWindow.h"
|
||||
#include "SoundConsumer.h"
|
||||
#include "SoundListView.h"
|
||||
#include "array_delete.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
#if ! NDEBUG
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
#include "SoundConsumer.h"
|
||||
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <OS.h>
|
||||
@@ -15,9 +17,11 @@
|
||||
#include <Buffer.h>
|
||||
#include <TimeSource.h>
|
||||
|
||||
#include "AutoDeleter.h"
|
||||
#include "SoundPrivate.h"
|
||||
#include "SoundConsumer.h"
|
||||
#include "array_delete.h"
|
||||
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
|
||||
// If we don't mind the format changing to another format while
|
||||
@@ -55,7 +59,8 @@ SoundConsumer::SoundConsumer(
|
||||
BMediaNode(name ? name : "SoundConsumer"),
|
||||
BBufferConsumer(B_MEDIA_RAW_AUDIO)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::SoundConsumer(%p, %p, %p, %p)\n", name, recordFunc, notifyFunc, cookie);
|
||||
NODE(stderr, "SoundConsumer::SoundConsumer(%p, %p, %p, %p)\n", name,
|
||||
recordFunc, notifyFunc, cookie);
|
||||
|
||||
if (!name) name = "SoundConsumer";
|
||||
|
||||
@@ -105,10 +110,8 @@ SoundConsumer::~SoundConsumer()
|
||||
|
||||
|
||||
status_t
|
||||
SoundConsumer::SetHooks(
|
||||
SoundProcessFunc recordFunc,
|
||||
SoundNotifyFunc notifyFunc,
|
||||
void * cookie)
|
||||
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.
|
||||
@@ -134,31 +137,27 @@ SoundConsumer::SetHooks(
|
||||
}
|
||||
// Clean up.
|
||||
delete_port(cmd.reply);
|
||||
}
|
||||
else {
|
||||
// Within the service thread, it's OK to just go ahead and do the change.
|
||||
} else {
|
||||
// Within the service thread, it's OK to just go ahead and do the
|
||||
// change.
|
||||
DoHookChange(&cmd);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// BMediaNode-derived methods
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// #pragma mark -BMediaNode-derived methods
|
||||
|
||||
|
||||
port_id SoundConsumer::ControlPort() const
|
||||
port_id
|
||||
SoundConsumer::ControlPort() const
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
|
||||
BMediaAddOn* SoundConsumer::AddOn(
|
||||
int32 * internal_id) const
|
||||
BMediaAddOn*
|
||||
SoundConsumer::AddOn(int32 * internal_id) const
|
||||
{
|
||||
// This object is instantiated inside an application.
|
||||
// Therefore, it has no add-on.
|
||||
@@ -167,8 +166,8 @@ BMediaAddOn* SoundConsumer::AddOn(
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Start(
|
||||
bigtime_t performance_time)
|
||||
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().
|
||||
@@ -180,18 +179,15 @@ void SoundConsumer::Start(
|
||||
m_delta = performance_time - m_tmSeekTo;
|
||||
m_seeking = false;
|
||||
}
|
||||
if (m_notifyHook) {
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_WILL_START, performance_time);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_WILL_START, performance_time);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Stop(
|
||||
bigtime_t performance_time,
|
||||
bool immediate)
|
||||
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().
|
||||
@@ -201,18 +197,15 @@ void SoundConsumer::Stop(
|
||||
// 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) {
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_WILL_STOP, performance_time, immediate);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_WILL_STOP, performance_time, immediate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Seek(
|
||||
bigtime_t media_time,
|
||||
bigtime_t performance_time)
|
||||
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.
|
||||
@@ -220,20 +213,19 @@ void SoundConsumer::Seek(
|
||||
// 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) {
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_WILL_SEEK, performance_time, media_time);
|
||||
}
|
||||
else {
|
||||
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)
|
||||
void
|
||||
SoundConsumer::SetRunMode(run_mode mode)
|
||||
{
|
||||
if (mode == BMediaNode::B_OFFLINE) {
|
||||
// BMediaNode::B_OFFLINE means we don't need to run in
|
||||
@@ -241,8 +233,7 @@ void SoundConsumer::SetRunMode(
|
||||
// thread.
|
||||
int32 new_prio = suggest_thread_priority(B_OFFLINE_PROCESSING);
|
||||
set_thread_priority(m_thread, new_prio);
|
||||
}
|
||||
else {
|
||||
} 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
|
||||
@@ -264,40 +255,39 @@ void SoundConsumer::SetRunMode(
|
||||
// * The amount of time we spend processing is
|
||||
// our ProcessingLatency().
|
||||
bigtime_t period = 10000;
|
||||
if (buffer_duration(m_input.format.u.raw_audio) > 0) {
|
||||
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());
|
||||
period, period / 2, ProcessingLatency());
|
||||
set_thread_priority(m_thread, new_prio);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::TimeWarp(
|
||||
bigtime_t at_real_time,
|
||||
bigtime_t to_performance_time)
|
||||
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 {
|
||||
(*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()
|
||||
void
|
||||
SoundConsumer::Preroll()
|
||||
{
|
||||
// There is nothing for us to do in Preroll()
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::SetTimeSource(
|
||||
BTimeSource * /* time_source */)
|
||||
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
|
||||
@@ -305,17 +295,14 @@ void SoundConsumer::SetTimeSource(
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::HandleMessage(
|
||||
int32 message,
|
||||
const void * data,
|
||||
size_t size)
|
||||
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))
|
||||
{
|
||||
if (BBufferConsumer::HandleMessage(message, data, size) < 0
|
||||
&& BMediaNode::HandleMessage(message, data, size) < 0) {
|
||||
HandleBadMessage(message, data, size);
|
||||
return B_ERROR;
|
||||
}
|
||||
@@ -323,33 +310,23 @@ status_t SoundConsumer::HandleMessage(
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - BBufferConsumer-derived methods
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// BBufferConsumer-derived methods
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
status_t SoundConsumer::AcceptFormat(
|
||||
const media_destination & dest,
|
||||
media_format * format)
|
||||
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) {
|
||||
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) {
|
||||
// If no format is specified, we say we want raw audio.
|
||||
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) {
|
||||
} else if (format->type != B_MEDIA_RAW_AUDIO) {
|
||||
// If a non-raw-audio format is specified, we tell the world what
|
||||
// we want, and that the specified format was unacceptable to us.
|
||||
format->type = B_MEDIA_RAW_AUDIO;
|
||||
format->u.raw_audio = media_raw_audio_format::wildcard;
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
@@ -368,14 +345,13 @@ status_t SoundConsumer::AcceptFormat(
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::GetNextInput(
|
||||
int32 * cookie,
|
||||
media_input * out_input)
|
||||
status_t
|
||||
SoundConsumer::GetNextInput(int32* cookie, media_input* out_input)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer: GetNextInput()\n");
|
||||
// The "next" is kind of misleading, since it's also used for
|
||||
// getting the first (and only) input.
|
||||
if (!*cookie) {
|
||||
if (*cookie == 0) {
|
||||
if (m_input.source == media_source::null) {
|
||||
// If there's no current connection, make sure we return a
|
||||
// reasonable format telling the world we accept any raw audio.
|
||||
@@ -394,16 +370,16 @@ status_t SoundConsumer::GetNextInput(
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::DisposeInputCookie(
|
||||
int32 /* cookie */)
|
||||
void
|
||||
SoundConsumer::DisposeInputCookie(int32 /* cookie */)
|
||||
{
|
||||
// We didn't allocate any memory or set any state in GetNextInput()
|
||||
// so this function is a no-op.
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::BufferReceived(
|
||||
BBuffer * buffer)
|
||||
void
|
||||
SoundConsumer::BufferReceived(BBuffer* buffer)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::BufferReceived()\n");
|
||||
// Whee, a buffer! Update the seek info, if necessary.
|
||||
@@ -413,10 +389,12 @@ void SoundConsumer::BufferReceived(
|
||||
}
|
||||
// If there is a record hook, let the interested party have at it!
|
||||
if (m_recordHook) {
|
||||
(*m_recordHook)(m_cookie, buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio);
|
||||
}
|
||||
else {
|
||||
Record(buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio);
|
||||
(*m_recordHook)(m_cookie, buffer->Header()->start_time-m_delta,
|
||||
buffer->Data(), buffer->Header()->size_used,
|
||||
m_input.format.u.raw_audio);
|
||||
} else {
|
||||
Record(buffer->Header()->start_time-m_delta, buffer->Data(),
|
||||
buffer->Header()->size_used, m_input.format.u.raw_audio);
|
||||
}
|
||||
// Buffers should ALWAYS be recycled, else whomever is producing them
|
||||
// will starve.
|
||||
@@ -424,33 +402,31 @@ void SoundConsumer::BufferReceived(
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::ProducerDataStatus(
|
||||
const media_destination & for_whom,
|
||||
int32 status,
|
||||
bigtime_t at_media_time)
|
||||
void
|
||||
SoundConsumer::ProducerDataStatus(const media_destination& for_whom,
|
||||
int32 status, bigtime_t at_media_time)
|
||||
{
|
||||
if (for_whom == m_input.destination) {
|
||||
// Tell whomever is interested that the upstream producer will or won't
|
||||
// send more data in the immediate future.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_PRODUCER_DATA_STATUS, status, at_media_time);
|
||||
}
|
||||
else {
|
||||
Notify(B_PRODUCER_DATA_STATUS, status, at_media_time);
|
||||
}
|
||||
}
|
||||
if (for_whom != m_input.destination)
|
||||
return;
|
||||
|
||||
// Tell whomever is interested that the upstream producer will or won't
|
||||
// send more data in the immediate future.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_PRODUCER_DATA_STATUS, status,
|
||||
at_media_time);
|
||||
} else
|
||||
Notify(B_PRODUCER_DATA_STATUS, status, at_media_time);
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::GetLatencyFor(
|
||||
const media_destination & for_whom,
|
||||
bigtime_t * out_latency,
|
||||
media_node_id * out_timesource)
|
||||
status_t
|
||||
SoundConsumer::GetLatencyFor(const media_destination& for_whom,
|
||||
bigtime_t* out_latency, media_node_id* out_timesource)
|
||||
{
|
||||
// We only accept requests for the one-and-only input of our Node.
|
||||
if (for_whom != m_input.destination) {
|
||||
if (for_whom != m_input.destination)
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
|
||||
// Tell the world about our latency information (overridable by user).
|
||||
*out_latency = TotalLatency();
|
||||
*out_timesource = TimeSource()->Node().node;
|
||||
@@ -458,32 +434,30 @@ status_t SoundConsumer::GetLatencyFor(
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::Connected(
|
||||
const media_source & producer,
|
||||
const media_destination & where,
|
||||
const media_format & with_format,
|
||||
media_input * out_input)
|
||||
status_t
|
||||
SoundConsumer::Connected(const media_source& producer,
|
||||
const media_destination& where, const media_format& with_format,
|
||||
media_input* out_input)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::Connected()\n");
|
||||
// Only accept connection requests when we're not already connected.
|
||||
if (m_input.source != media_source::null) {
|
||||
if (m_input.source != media_source::null)
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
|
||||
// Only accept connection requests on the one-and-only available input.
|
||||
if (where != m_input.destination) {
|
||||
if (where != m_input.destination)
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
|
||||
// Other than that, we accept pretty much anything. The format has been
|
||||
// pre-cleared through AcceptFormat(), and we accept any format anyway.
|
||||
m_input.source = producer;
|
||||
m_input.format = with_format;
|
||||
// Tell whomever is interested that there's now a connection.
|
||||
if (m_notifyHook) {
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_CONNECTED, m_input.name);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_CONNECTED, m_input.name);
|
||||
}
|
||||
|
||||
// This is the most important line -- return our connection information
|
||||
// to the world so it can use it!
|
||||
*out_input = m_input;
|
||||
@@ -491,75 +465,69 @@ status_t SoundConsumer::Connected(
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Disconnected(
|
||||
const media_source & producer,
|
||||
const media_destination & where)
|
||||
void
|
||||
SoundConsumer::Disconnected(const media_source& producer,
|
||||
const media_destination& where)
|
||||
{
|
||||
// We can't disconnect something which isn't us.
|
||||
if (where != m_input.destination) {
|
||||
if (where != m_input.destination)
|
||||
return;
|
||||
}
|
||||
// We can't disconnect from someone who isn't connected to us.
|
||||
if (producer != m_input.source) {
|
||||
if (producer != m_input.source)
|
||||
return;
|
||||
}
|
||||
// Tell the interested party that it's time to leave.
|
||||
if (m_notifyHook) {
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_DISCONNECTED);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_DISCONNECTED);
|
||||
}
|
||||
// Mark ourselves as not-connected.
|
||||
m_input.source = media_source::null;
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::FormatChanged(
|
||||
const media_source & producer,
|
||||
const media_destination & consumer,
|
||||
int32 from_change_count,
|
||||
const media_format & format)
|
||||
status_t
|
||||
SoundConsumer::FormatChanged(const media_source& producer,
|
||||
const media_destination& consumer, int32 from_change_count,
|
||||
const media_format& format)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::Connected()\n");
|
||||
// The up-stream guy feels like changing the format. If we can accept
|
||||
// arbitrary format changes, we just say "OK". If, however, we're recording
|
||||
// to a file, that's not such a good idea; we only accept format changes
|
||||
// that are compatible with the format we're already using. You set this
|
||||
// behaviour at compile time by defining ACCEPT_ANY_FORMAT_CHANGE to 1 or 0.
|
||||
// behaviour at compile time by defining ACCEPT_ANY_FORMAT_CHANGE to 1 or
|
||||
// 0.
|
||||
status_t err = B_OK;
|
||||
#if ACCEPT_ANY_FORMAT_CHANGE
|
||||
media_format fmt(format);
|
||||
err = AcceptFormat(m_input.destination, &fmt);
|
||||
#else
|
||||
if (m_input.source != media_source::null) {
|
||||
err = format_is_compatible(format, m_input.format) ? B_OK : B_MEDIA_BAD_FORMAT;
|
||||
err = format_is_compatible(format, m_input.format) ? B_OK
|
||||
: B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
#endif
|
||||
if (err >= B_OK) {
|
||||
m_input.format = format;
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_FORMAT_CHANGED, &m_input.format.u.raw_audio);
|
||||
}
|
||||
else {
|
||||
(*m_notifyHook)(m_cookie, B_FORMAT_CHANGED,
|
||||
&m_input.format.u.raw_audio);
|
||||
} else
|
||||
Notify(B_FORMAT_CHANGED, &m_input.format.u.raw_audio);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundConsumer::DoHookChange(
|
||||
void * msg)
|
||||
SoundConsumer::DoHookChange(void* msg)
|
||||
{
|
||||
// Tell the old guy we're changing the hooks ...
|
||||
if (m_notifyHook) {
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_HOOKS_CHANGED);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_HOOKS_CHANGED);
|
||||
}
|
||||
|
||||
// ... and then do it.
|
||||
set_hooks_q * ptr = (set_hooks_q *)msg;
|
||||
m_recordHook = ptr->process;
|
||||
@@ -569,10 +537,10 @@ SoundConsumer::DoHookChange(
|
||||
|
||||
|
||||
status_t
|
||||
SoundConsumer::ThreadEntry(
|
||||
void * obj)
|
||||
SoundConsumer::ThreadEntry(void* cookie)
|
||||
{
|
||||
((SoundConsumer *)obj)->ServiceThread();
|
||||
SoundConsumer* consumer = (SoundConsumer*)cookie;
|
||||
consumer->ServiceThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -587,9 +555,11 @@ SoundConsumer::ServiceThread()
|
||||
|
||||
// A media kit message will never be bigger than B_MEDIA_MESSAGE_SIZE.
|
||||
// Avoid wasing stack space by dynamically allocating at start.
|
||||
char * msg = new char[B_MEDIA_MESSAGE_SIZE];
|
||||
char* msg = new (nothrow) char[B_MEDIA_MESSAGE_SIZE];
|
||||
if (msg == NULL)
|
||||
return;
|
||||
// Make sure we clean up this data when we exit the function.
|
||||
array_delete<char> msg_delete(msg);
|
||||
ArrayDeleter<char> _(msg);
|
||||
int bad = 0;
|
||||
while (true) {
|
||||
// Call read_port_etc() with a timeout derived from a virtual function,
|
||||
@@ -598,57 +568,50 @@ SoundConsumer::ServiceThread()
|
||||
int32 code = 0;
|
||||
status_t err = read_port_etc(m_port, &code, msg, B_MEDIA_MESSAGE_SIZE,
|
||||
B_TIMEOUT, timeout);
|
||||
MESSAGE(stderr, "SoundConsumer::ServiceThread() port %ld message %#010lx\n", m_port, code);
|
||||
// If we received a message, err will be the size of the message (including 0).
|
||||
if (err >= 0) {
|
||||
MESSAGE(stderr, "SoundConsumer::ServiceThread() port %ld message "
|
||||
"%#010lx\n", m_port, code);
|
||||
// If we received a message, err will be the size of the message
|
||||
// (including 0).
|
||||
if (err >= B_OK) {
|
||||
// Real messages reset the timeout time.
|
||||
m_trTimeout = 0;
|
||||
bad = 0;
|
||||
// Check for our private stop message.
|
||||
if (code == MSG_QUIT_NOW) {
|
||||
if (m_notifyHook) {
|
||||
// Check for our private stop message.
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_NODE_DIES, 0);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_NODE_DIES, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (code == MSG_CHANGE_HOOKS) {
|
||||
// Else check for our private change-hooks message.
|
||||
else if (code == MSG_CHANGE_HOOKS) {
|
||||
DoHookChange(msg);
|
||||
// Write acknowledge to waiting thread.
|
||||
write_port(((set_hooks_q *)msg)->reply, 0, 0, 0);
|
||||
}
|
||||
// Else it has to be a regular media kit message; go ahead and
|
||||
// dispatch it.
|
||||
else {
|
||||
} else {
|
||||
// Else it has to be a regular media kit message;
|
||||
// go ahead and dispatch it.
|
||||
HandleMessage(code, msg, err);
|
||||
}
|
||||
}
|
||||
// Timing out means that there was no buffer. Tell the interested party.
|
||||
else if (err == B_TIMED_OUT) {
|
||||
if (m_notifyHook) {
|
||||
} else if (err == B_TIMED_OUT) {
|
||||
// Timing out means that there was no buffer. Tell the interested
|
||||
// party.
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_OP_TIMED_OUT, timeout);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_OP_TIMED_OUT, timeout);
|
||||
}
|
||||
}
|
||||
// Other errors are bad.
|
||||
else {
|
||||
} else {
|
||||
// Other errors are bad.
|
||||
FPRINTF(stderr, "SoundConsumer: error %#010lx\n", err);
|
||||
bad++;
|
||||
// If we receive three bad reads with no good messages inbetween,
|
||||
// things are probably not going to improve (like the port disappeared
|
||||
// or something) so we call it a day.
|
||||
// things are probably not going to improve (like the port
|
||||
// disappeared or something) so we call it a day.
|
||||
if (bad > 3) {
|
||||
if (m_notifyHook) {
|
||||
if (m_notifyHook)
|
||||
(*m_notifyHook)(m_cookie, B_NODE_DIES, bad, err, code, msg);
|
||||
}
|
||||
else {
|
||||
else
|
||||
Notify(B_NODE_DIES, bad, err, code, msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -664,8 +627,9 @@ SoundConsumer::Timeout()
|
||||
// we've picked is to exponentially back off from one second and upwards.
|
||||
// While it's true that 44 back-offs will run us out of precision in a
|
||||
// bigtime_t, the time to actually reach 44 consecutive back-offs is longer
|
||||
// than the expected market longevity of just about any piece of real estate.
|
||||
// Is that the sound of an impending year-fifteen-million software problem? :-)
|
||||
// than the expected market longevity of just about any piece of real
|
||||
// estate. Is that the sound of an impending year-fifteen-million software
|
||||
// problem? :-)
|
||||
m_trTimeout = (m_trTimeout < 1000000) ? 1000000 : m_trTimeout*2;
|
||||
return m_trTimeout;
|
||||
}
|
||||
@@ -689,12 +653,13 @@ SoundConsumer::TotalLatency()
|
||||
return ProcessingLatency();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
SoundConsumer::Record(
|
||||
bigtime_t /* time */,
|
||||
const void * /* data */,
|
||||
size_t /* size */,
|
||||
const media_raw_audio_format & /* format */)
|
||||
SoundConsumer::Record(bigtime_t /*time*/, const void* /*data*/,
|
||||
size_t /*size*/, const media_raw_audio_format& /*format*/)
|
||||
{
|
||||
// If there is no record hook installed, we instead call this function
|
||||
// for received buffers.
|
||||
@@ -702,10 +667,8 @@ SoundConsumer::Record(
|
||||
|
||||
|
||||
void
|
||||
SoundConsumer::Notify(
|
||||
int32 /* cause */,
|
||||
...)
|
||||
SoundConsumer::Notify(int32 /*cause*/, ...)
|
||||
{
|
||||
// If there is no notification hook installed, we instead call this function
|
||||
// for giving notification of various events.
|
||||
// If there is no notification hook installed, we instead call this
|
||||
// function for giving notification of various events.
|
||||
}
|
||||
|
||||
@@ -7,13 +7,8 @@
|
||||
/ Copyright 1998, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined( _SoundConsumer_h )
|
||||
#define _SoundConsumer_h
|
||||
|
||||
#include <BufferConsumer.h>
|
||||
#include "SoundUtils.h"
|
||||
#ifndef SOUND_CONSUMER_H
|
||||
#define SOUND_CONSUMER_H
|
||||
|
||||
// To use this Consumer:
|
||||
// 1. Create Record and Notify hooks, or subclass SoundConsumer
|
||||
@@ -33,141 +28,120 @@
|
||||
// your Record function will see.
|
||||
// 6: When you're done, disconnect the Consumer, then delete it.
|
||||
|
||||
class SoundConsumer :
|
||||
public BBufferConsumer
|
||||
{
|
||||
public:
|
||||
SoundConsumer(
|
||||
const char * name,
|
||||
SoundProcessFunc recordFunc = NULL,
|
||||
SoundNotifyFunc notifyFunc = NULL,
|
||||
void * cookie = NULL);
|
||||
~SoundConsumer();
|
||||
#include <BufferConsumer.h>
|
||||
#include "SoundUtils.h"
|
||||
|
||||
// This function is OK to call from any thread.
|
||||
status_t SetHooks(
|
||||
SoundProcessFunc recordFunc = NULL,
|
||||
SoundNotifyFunc notifyFunc = NULL,
|
||||
void * cookie = NULL);
|
||||
|
||||
// The MediaNode interface
|
||||
class SoundConsumer : public BBufferConsumer {
|
||||
public:
|
||||
virtual port_id ControlPort() const;
|
||||
virtual BMediaAddOn* AddOn(
|
||||
int32 * internal_id) const; /* Who instantiated you -- or NULL for app class */
|
||||
SoundConsumer(const char* name,
|
||||
SoundProcessFunc recordFunc = NULL,
|
||||
SoundNotifyFunc notifyFunc = NULL,
|
||||
void* cookie = NULL);
|
||||
virtual ~SoundConsumer();
|
||||
|
||||
//This function is OK to call from any thread.
|
||||
status_t SetHooks(SoundProcessFunc recordFunc = NULL,
|
||||
SoundNotifyFunc notifyFunc = NULL,
|
||||
void* cookie = NULL);
|
||||
|
||||
// The MediaNode interface
|
||||
public:
|
||||
virtual port_id ControlPort() const;
|
||||
virtual BMediaAddOn* AddOn(int32* internalID) const;
|
||||
// Who instantiated you -- or NULL for app class
|
||||
|
||||
|
||||
protected:
|
||||
virtual void Start(bigtime_t performanceTime);
|
||||
virtual void Stop(bigtime_t performanceTime, bool immediate);
|
||||
virtual void Seek(bigtime_t mediaTime,
|
||||
bigtime_t performanceTime);
|
||||
virtual void SetRunMode(run_mode mode);
|
||||
virtual void TimeWarp(bigtime_t atRealTime,
|
||||
bigtime_t to_performanceTime);
|
||||
virtual void Preroll();
|
||||
virtual void SetTimeSource(BTimeSource* timeSource);
|
||||
virtual status_t HandleMessage(int32 message, const void* data,
|
||||
size_t size);
|
||||
|
||||
virtual void Start(
|
||||
bigtime_t performance_time);
|
||||
virtual void Stop(
|
||||
bigtime_t performance_time,
|
||||
bool immediate);
|
||||
virtual void Seek(
|
||||
bigtime_t media_time,
|
||||
bigtime_t performance_time);
|
||||
virtual void SetRunMode(
|
||||
run_mode mode);
|
||||
virtual void TimeWarp(
|
||||
bigtime_t at_real_time,
|
||||
bigtime_t to_performance_time);
|
||||
virtual void Preroll();
|
||||
virtual void SetTimeSource(
|
||||
BTimeSource * time_source);
|
||||
virtual status_t HandleMessage(
|
||||
int32 message,
|
||||
const void * data,
|
||||
size_t size);
|
||||
|
||||
// The BufferConsumer interface
|
||||
virtual status_t AcceptFormat(
|
||||
const media_destination & dest,
|
||||
media_format * format);
|
||||
virtual status_t GetNextInput( /* cookie starts at 0 */
|
||||
int32 * cookie,
|
||||
media_input * out_input);
|
||||
virtual void DisposeInputCookie(
|
||||
int32 cookie);
|
||||
virtual void BufferReceived(
|
||||
BBuffer * buffer);
|
||||
virtual void ProducerDataStatus(
|
||||
const media_destination & for_whom,
|
||||
int32 status,
|
||||
bigtime_t at_media_time);
|
||||
virtual status_t GetLatencyFor(
|
||||
const media_destination & for_whom,
|
||||
bigtime_t * out_latency,
|
||||
media_node_id * out_timesource);
|
||||
virtual status_t Connected(
|
||||
const media_source & producer,
|
||||
const media_destination & where,
|
||||
const media_format & with_format,
|
||||
media_input * out_input);
|
||||
virtual void Disconnected(
|
||||
const media_source & producer,
|
||||
const media_destination & where);
|
||||
virtual status_t FormatChanged(
|
||||
const media_source & producer,
|
||||
const media_destination & consumer,
|
||||
int32 from_change_count,
|
||||
const media_format & format);
|
||||
// The BufferConsumer interface
|
||||
virtual status_t AcceptFormat(const media_destination& dest,
|
||||
media_format* format);
|
||||
virtual status_t GetNextInput(int32* cookie,
|
||||
media_input* _input);
|
||||
virtual void DisposeInputCookie(int32 cookie);
|
||||
virtual void BufferReceived(BBuffer* buffer);
|
||||
virtual void ProducerDataStatus(
|
||||
const media_destination& forWhom,
|
||||
int32 status, bigtime_t atMediaTime);
|
||||
virtual status_t GetLatencyFor(const media_destination& forWhom,
|
||||
bigtime_t* _latency,
|
||||
media_node_id* _timesource);
|
||||
virtual status_t Connected(const media_source& producer,
|
||||
const media_destination& where,
|
||||
const media_format& format,
|
||||
media_input* _input);
|
||||
virtual void Disconnected(const media_source& producer,
|
||||
const media_destination& where);
|
||||
virtual status_t FormatChanged(const media_source& producer,
|
||||
const media_destination& consumer,
|
||||
int32 fromChangeCount,
|
||||
const media_format& format);
|
||||
|
||||
protected:
|
||||
// Functions called when no hooks are installed.
|
||||
// OK to override instead of installing hooks.
|
||||
virtual void Record(
|
||||
bigtime_t time,
|
||||
const void * data,
|
||||
size_t size,
|
||||
const media_raw_audio_format & format);
|
||||
virtual void Notify(
|
||||
int32 cause,
|
||||
...);
|
||||
// Functions called when no hooks are installed.
|
||||
// OK to override instead of installing hooks.
|
||||
virtual void Record(bigtime_t time, const void* data,
|
||||
size_t size,
|
||||
const media_raw_audio_format& format);
|
||||
virtual void Notify(int32 cause, ...);
|
||||
|
||||
private:
|
||||
SoundProcessFunc m_recordHook;
|
||||
SoundNotifyFunc m_notifyHook;
|
||||
void * m_cookie;
|
||||
media_input m_input;
|
||||
thread_id m_thread;
|
||||
port_id m_port;
|
||||
SoundProcessFunc m_recordHook;
|
||||
SoundNotifyFunc m_notifyHook;
|
||||
void* m_cookie;
|
||||
media_input m_input;
|
||||
thread_id m_thread;
|
||||
port_id m_port;
|
||||
|
||||
// The times we need to deal with
|
||||
// My notation for times: tr = real time,
|
||||
// tp = performance time, tm = media time.
|
||||
bigtime_t m_trTimeout; // how long to wait on the input port
|
||||
bigtime_t m_tpSeekAt; // when we Seek
|
||||
bigtime_t m_tmSeekTo; // target time for Seek
|
||||
// The times we need to deal with
|
||||
// My notation for times: tr = real time,
|
||||
// tp = performance time, tm = media time.
|
||||
bigtime_t m_trTimeout;
|
||||
// how long to wait on the input port
|
||||
bigtime_t m_tpSeekAt;
|
||||
// when we Seek
|
||||
bigtime_t m_tmSeekTo;
|
||||
// target time for Seek
|
||||
|
||||
// The transformation from media to peformance time.
|
||||
// d = p - m, so m + d = p.
|
||||
// Media time is generally governed by the Seek
|
||||
// function. In our node, we simply use media time as
|
||||
// the time that we report to the record hook function.
|
||||
// If we were a producer node, we might use media time
|
||||
// to track where we were in playing a certain piece
|
||||
// of media. But we aren't.
|
||||
bigtime_t m_delta;
|
||||
// The transformation from media to peformance time.
|
||||
// d = p - m, so m + d = p.
|
||||
// Media time is generally governed by the Seek
|
||||
// function. In our node, we simply use media time as
|
||||
// the time that we report to the record hook function.
|
||||
// If we were a producer node, we might use media time
|
||||
// to track where we were in playing a certain piece
|
||||
// of media. But we aren't.
|
||||
bigtime_t m_delta;
|
||||
|
||||
// State variables
|
||||
bool m_seeking; // a Seek is pending
|
||||
// State variables
|
||||
bool m_seeking;
|
||||
// a Seek is pending
|
||||
|
||||
// Functions to calculate timing values. OK to override.
|
||||
// ProcessingLatency is the time it takes to process a buffer;
|
||||
// TotalLatency is returned to producer; Timeout is passed
|
||||
// to call to read_port_etc() in service thread loop.
|
||||
virtual bigtime_t Timeout();
|
||||
virtual bigtime_t ProcessingLatency();
|
||||
virtual bigtime_t TotalLatency();
|
||||
// The actual thread doing the work
|
||||
static status_t ThreadEntry(
|
||||
void * obj);
|
||||
void ServiceThread();
|
||||
void DoHookChange(
|
||||
void * msg);
|
||||
// Functions to calculate timing values. OK to override.
|
||||
// ProcessingLatency is the time it takes to process a buffer;
|
||||
// TotalLatency is returned to producer; Timeout is passed
|
||||
// to call to read_port_etc() in service thread loop.
|
||||
virtual bigtime_t Timeout();
|
||||
virtual bigtime_t ProcessingLatency();
|
||||
virtual bigtime_t TotalLatency();
|
||||
// The actual thread doing the work
|
||||
static status_t ThreadEntry(void* cookie);
|
||||
void ServiceThread();
|
||||
void DoHookChange(void* message);
|
||||
};
|
||||
|
||||
|
||||
#endif /* _SoundConsumer_h */
|
||||
#endif // SOUND_CONSUMER_H
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: array_delete.h
|
||||
/
|
||||
/ Description: Template for deleting a new[] array of something.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined( _array_delete_h )
|
||||
#define _array_delete_h
|
||||
|
||||
// Oooh! It's a template!
|
||||
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; }
|
||||
};
|
||||
|
||||
|
||||
#endif /* array_delete_h */
|
||||
|
||||
Reference in New Issue
Block a user