Files
haiku-beta6/src/add-ons/media/media-add-ons/legacy/LegacyAudioConsumer.cpp
T
beveloper 440b3fb4ba /boot/home/config/add-ons/media/legacy.media_addon
Adding the legacy (BeOS R3) sound driver interface BMediaAddOn.

Written by Jens Winkler, snapshot from 2002-06-02.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@71 a95241bf-73f2-0310-859d-f6bbb57e9c96
2002-07-11 00:42:58 +00:00

431 lines
8.3 KiB
C++

#include <fcntl.h>
#include <malloc.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <media/Buffer.h>
#include <media/BufferGroup.h>
#include <media/ParameterWeb.h>
#include <media/TimeSource.h>
#include <support/Autolock.h>
#include <support/Debug.h>
#include "driver/audio.h"
#include "driver/sound.h"
#include "LegacyAudioConsumer.h"
#include "driver_io.h"
LegacyAudioConsumer::LegacyAudioConsumer( BMediaAddOn *addon, const char *name, int32 internal_id )
: BMediaNode( name ), BBufferConsumer( B_MEDIA_RAW_AUDIO )
{
mInitStatus = B_NO_INIT;
mAddOn = addon;
mId = internal_id;
mBuffers = NULL;
mThread = -1;
mProcessingLatency = 0LL;
mRunning = false;
mConnected = false;
io_buf1 = NULL;
AddNodeKind( B_PHYSICAL_OUTPUT );
sprintf( device_name, "/dev/audio/old/%s", name );
//open the device driver for output
fd = open( device_name, O_WRONLY );
if ( fd == 0 ) {
return;
}
DRIVER_GET_PLAYBACK_PREFERRED_BUF_SIZE( &mBuffer_size, 0 );
io_buf1 = malloc( 2 * ( sizeof( audio_buffer_header ) + mBuffer_size ) );
if ( io_buf1 == NULL ) {
close( fd );
return;
}
io_buf2 = static_cast<char *>( io_buf1 ) + sizeof( audio_buffer_header ) + mBuffer_size;
static_cast<audio_buffer_header *>( io_buf1 )->reserved_1 = sizeof( audio_buffer_header ) + mBuffer_size;
static_cast<audio_buffer_header *>( io_buf2 )->reserved_1 = sizeof( audio_buffer_header ) + mBuffer_size;
mInitStatus = B_OK;
return;
}
LegacyAudioConsumer::~LegacyAudioConsumer()
{
if ( mInitStatus != B_OK ) {
return;
}
/* Clean up */
if ( mConnected ) {
Disconnected( mInput.source, mInput.destination );
}
if ( mRunning ) {
HandleStop();
}
if ( io_buf1 != NULL ) {
free( static_cast<void *>( io_buf1 ) );
}
if ( fd != 0 ) {
close( fd );
}
}
/* BMediaNode */
BMediaAddOn *
LegacyAudioConsumer::AddOn( int32 *internal_id ) const
{
if ( internal_id ) {
*internal_id = mId;
}
return mAddOn;
}
void
LegacyAudioConsumer::NodeRegistered()
{
mInput.destination.port = ControlPort();
mInput.destination.id = 0;
mInput.source = media_source::null;
mInput.format.type = B_MEDIA_RAW_AUDIO;
mInput.format.u.raw_audio.frame_rate = 44100.0;
mInput.format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
mInput.format.u.raw_audio.channel_count = 2;
mInput.format.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
mInput.format.u.raw_audio.buffer_size = mBuffer_size;
Run();
}
status_t
LegacyAudioConsumer::HandleMessage( int32 message, const void *data, size_t size )
{
status_t err = B_OK;
if ( BBufferConsumer::HandleMessage( message, data, size ) != B_OK ) {
if ( ( err = BMediaNode::HandleMessage( message, data, size ) ) != B_OK ) {
BMediaNode::HandleBadMessage( message, data, size );
}
}
return err;
}
/* BBufferConsumer */
status_t
LegacyAudioConsumer::AcceptFormat( const media_destination &dest, media_format *format )
{
if ( format->type != B_MEDIA_RAW_AUDIO ) {
return B_MEDIA_BAD_FORMAT;
}
format->u.raw_audio.frame_rate = 44100.0;
format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
format->u.raw_audio.channel_count = 2;
format->u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
format->u.raw_audio.buffer_size = mBuffer_size;
return B_OK;
}
status_t
LegacyAudioConsumer::GetNextInput( int32 *cookie, media_input *out_input )
{
if ( *cookie == 0 ) {
memcpy( out_input, &mInput , sizeof( media_input ) );
}
return B_BAD_INDEX;
}
void
LegacyAudioConsumer::BufferReceived( BBuffer *buffer )
{
if ( ! mRunning ) {
buffer->Recycle();
} else {
media_timed_event event( buffer->Header()->start_time,
BTimedEventQueue::B_HANDLE_BUFFER,
buffer,
BTimedEventQueue::B_RECYCLE_BUFFER );
EventQueue()->AddEvent( event );
}
}
void
LegacyAudioConsumer::ProducerDataStatus( const media_destination &for_whom, int32 status,
bigtime_t at_media_time )
{
}
status_t
LegacyAudioConsumer::GetLatencyFor( const media_destination &for_whom, bigtime_t *out_latency,
media_node_id *out_timesource )
{
*out_latency = mBuffer_size * 10000 / 4 / 441;
return B_OK;
}
status_t
LegacyAudioConsumer::Connected( const media_source &producer, const media_destination &where,
const media_format &with_format, media_input *out_input )
{
mInput.source = producer;
//mInput.format = with_format;
mInput.node = Node();
strcpy( mInput.name, "Legacy Audio Consumer" );
mBuffers = new BBufferGroup;
uint32 dummy_data = 0;
int32 change_tag = 1;
BBufferConsumer::SetOutputBuffersFor( producer, mDest, mBuffers, static_cast<void *>( &dummy_data ),
&change_tag, true );
mConnected = true;
return B_OK;
}
void
LegacyAudioConsumer::Disconnected( const media_source &producer, const media_destination &where )
{
if ( mConnected ) {
delete mBuffers;
mInput.source = media_source::null;
mConnected = false;
}
}
status_t
LegacyAudioConsumer::FormatChanged( const media_source &producer, const media_destination &consumer,
int32 change_tag, const media_format &format )
{
return B_OK;
}
/* BMediaEventLooper */
void
LegacyAudioConsumer::HandleEvent( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent )
{
switch(event->type) {
case BTimedEventQueue::B_START:
HandleStart( event->event_time );
break;
case BTimedEventQueue::B_STOP:
HandleStop();
break;
case BTimedEventQueue::B_WARP:
HandleTimeWarp( event->bigdata );
break;
case BTimedEventQueue::B_SEEK:
HandleSeek( event->bigdata );
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
HandleBuffer( static_cast<BBuffer *>( event->pointer ) );
break;
case BTimedEventQueue::B_DATA_STATUS:
case BTimedEventQueue::B_PARAMETER:
default:
break; //HandleEvent: Unhandled event
}
}
/* LegacyAudioConsumer */
void
LegacyAudioConsumer::HandleStart( bigtime_t performance_time )
{
if ( mRunning ) {
return;
}
mPerformanceTimeBase = performance_time;
//allocate buffer available semaphore
mBuffer_avail = create_sem( 0, "legacy audio out buffer avail" );
if ( mBuffer_avail < B_OK ) {
goto init_err1;
}
//allocate buffer free semaphore
mBuffer_free = create_sem( 1, "legacy audio out buffer free" );
if ( mBuffer_free < B_OK ) {
goto init_err2;
}
//allocate output completion semaphore
mBuffer_waitIO = create_sem( 1, "legacy audio out waitIO" );
if ( mBuffer_waitIO < B_OK ) {
goto init_err3;
}
//tell the driver about the playback completion semaphore
DRIVER_SET_PLAYBACK_COMPLETION_SEM( &mBuffer_waitIO, 0 );
mThread = spawn_thread( _run_thread_, "legacy audio output", B_REAL_TIME_PRIORITY, this );
if ( mThread < B_OK ) {
goto init_err4;
}
io_buf = io_buf1;
resume_thread( mThread );
mRunning = true;
return;
init_err4:
delete_sem( mBuffer_waitIO );
init_err3:
delete_sem( mBuffer_free );
init_err2:
delete_sem( mBuffer_avail );
init_err1:
return;
}
void
LegacyAudioConsumer::HandleStop()
{
if ( ! mRunning ) {
return;
}
delete_sem( mBuffer_avail );
delete_sem( mBuffer_free );
delete_sem( mBuffer_waitIO );
wait_for_thread( mThread, &mThread );
mRunning = false;
}
void
LegacyAudioConsumer::HandleTimeWarp( bigtime_t performance_time )
{
mPerformanceTimeBase = performance_time;
}
void
LegacyAudioConsumer::HandleSeek( bigtime_t performance_time )
{
mPerformanceTimeBase = performance_time;
}
void
LegacyAudioConsumer::HandleBuffer( BBuffer *buffer )
{
void *buf;
//wait for free buffer
acquire_sem( mBuffer_free );
//avoid buffer currently in use
buf = static_cast<void *>( ( io_buf == io_buf2 ) ? io_buf1 : io_buf2 );
//prepare buffer
memcpy( static_cast<char *>( buf ) + sizeof( audio_buffer_header ), buffer->Data(), buffer->SizeUsed() );
//tell the io thread a new buffer is ready
release_sem( mBuffer_avail );
}
int32
LegacyAudioConsumer::_run_thread_( void *data )
{
return static_cast<LegacyAudioConsumer *>( data )->RunThread();
}
int32
LegacyAudioConsumer::RunThread()
{
while ( 1 ) {
//wait for next buffer
if ( acquire_sem( mBuffer_avail ) == B_BAD_SEM_ID ) {
return B_OK;
}
//send buffer
DRIVER_UNSAFE_WRITE( io_buf, 0 );
//wait for IO
if ( acquire_sem( mBuffer_waitIO ) == B_BAD_SEM_ID ) {
return B_OK;
}
io_buf = ( io_buf == io_buf2 ) ? io_buf1 : io_buf2;
//mark buffer free
release_sem( mBuffer_free );
}
return B_OK;
}