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