1#include <fcntl.h>
2#include <malloc.h>
3#include <math.h>
4#include <stdio.h>
5#include <string.h>
6#include <sys/uio.h>
7#include <unistd.h>
8
9#include <media/Buffer.h>
10#include <media/BufferGroup.h>
11#include <media/ParameterWeb.h>
12#include <media/TimeSource.h>
13
14#include <support/Autolock.h>
15#include <support/Debug.h>
16
17#include "driver_io.h"
18#include "LegacyAudioConsumer.h"
19
20
21LegacyAudioConsumer::LegacyAudioConsumer( BMediaAddOn *addon, const char *name, int32 internal_id )
22	: BMediaNode( name ), BBufferConsumer( B_MEDIA_RAW_AUDIO )
23{
24	mInitStatus = B_NO_INIT;
25
26	mAddOn = addon;
27
28	mId = internal_id;
29
30	mBuffers = NULL;
31
32	mThread = -1;
33
34	mProcessingLatency = 0LL;
35
36	mRunning	= false;
37	mConnected	= false;
38
39	io_buf1 = NULL;
40
41	AddNodeKind( B_PHYSICAL_OUTPUT );
42
43	sprintf( device_name, "/dev/audio/old/%s", name );
44
45	//open the device driver for output
46	fd = open( device_name, O_WRONLY );
47
48	if ( fd == 0 ) {
49		return;
50	}
51
52	mBuffer_size = 4096;
53
54	io_buf1 = static_cast<audio_buffer_header *>(
55				malloc( 2 * ( sizeof( audio_buffer_header ) + mBuffer_size ))
56				);
57
58	if ( io_buf1 == NULL ) {
59		close( fd );
60		return;
61	}
62
63	io_buf2 = reinterpret_cast<audio_buffer_header *>(
64				reinterpret_cast<char *>(const_cast<audio_buffer_header *>( io_buf1 ))
65				+ sizeof( audio_buffer_header )
66				+ mBuffer_size);
67
68	io_buf1->reserved_1 = sizeof( audio_buffer_header ) + mBuffer_size;
69	io_buf2->reserved_1 = sizeof( audio_buffer_header ) + mBuffer_size;
70
71	mInitStatus = B_OK;
72	return;
73}
74
75
76LegacyAudioConsumer::~LegacyAudioConsumer()
77{
78	if ( mInitStatus != B_OK ) {
79		return;
80	}
81
82	/* Clean up */
83	if ( mConnected ) {
84		Disconnected( mInput.source, mInput.destination );
85	}
86
87	if ( mRunning ) {
88		HandleStop();
89	}
90
91	if ( io_buf1 != NULL ) {
92		free( static_cast<void *>(const_cast<audio_buffer_header *>(io_buf1)) );
93	}
94
95	if ( fd != 0 ) {
96		close( fd );
97	}
98}
99
100
101/* BMediaNode */
102
103BMediaAddOn *
104LegacyAudioConsumer::AddOn( int32 *internal_id ) const
105{
106	if ( internal_id ) {
107		*internal_id = mId;
108	}
109
110	return mAddOn;
111}
112
113
114void
115LegacyAudioConsumer::NodeRegistered()
116{
117	mInput.destination.port = ControlPort();
118	mInput.destination.id   = 0;
119
120	mInput.source = media_source::null;
121
122	mInput.format.type = B_MEDIA_RAW_AUDIO;
123	mInput.format.u.raw_audio.frame_rate    = 44100.0;
124	mInput.format.u.raw_audio.format        = media_raw_audio_format::B_AUDIO_SHORT;
125	mInput.format.u.raw_audio.channel_count = 2;
126	mInput.format.u.raw_audio.byte_order    = B_MEDIA_HOST_ENDIAN;
127	mInput.format.u.raw_audio.buffer_size   = mBuffer_size;
128
129	Run();
130}
131
132
133status_t
134LegacyAudioConsumer::HandleMessage( int32 message, const void *data, size_t size )
135{
136	status_t err = B_OK;
137
138	if ( BBufferConsumer::HandleMessage( message, data, size ) != B_OK ) {
139		if ( ( err = BMediaNode::HandleMessage( message, data, size ) ) != B_OK ) {
140			BMediaNode::HandleBadMessage( message, data, size );
141		}
142	}
143
144	return err;
145}
146
147
148/* BBufferConsumer */
149
150status_t
151LegacyAudioConsumer::AcceptFormat( const media_destination &dest, media_format *format )
152{
153	if ( format->type != B_MEDIA_RAW_AUDIO ) {
154		return B_MEDIA_BAD_FORMAT;
155	}
156
157	format->u.raw_audio.frame_rate    = 44100.0;
158	format->u.raw_audio.format        = media_raw_audio_format::B_AUDIO_SHORT;
159	format->u.raw_audio.channel_count = 2;
160	format->u.raw_audio.byte_order    = B_MEDIA_HOST_ENDIAN;
161	format->u.raw_audio.buffer_size   = mBuffer_size;
162
163	return B_OK;
164}
165
166
167status_t
168LegacyAudioConsumer::GetNextInput( int32 *cookie, media_input *out_input )
169{
170	if ( *cookie == 0 ) {
171		memcpy( out_input, &mInput , sizeof( media_input ) );
172	}
173
174	return B_BAD_INDEX;
175}
176
177
178void
179LegacyAudioConsumer::BufferReceived( BBuffer *buffer )
180{
181	if ( ! mRunning ) {
182
183		buffer->Recycle();
184
185	} else {
186
187		media_timed_event event( buffer->Header()->start_time,
188		                         BTimedEventQueue::B_HANDLE_BUFFER,
189		                         buffer,
190		                         BTimedEventQueue::B_RECYCLE_BUFFER );
191
192		EventQueue()->AddEvent( event );
193
194	}
195}
196
197
198void
199LegacyAudioConsumer::ProducerDataStatus( const media_destination &for_whom, int32 status,
200										 bigtime_t at_media_time )
201{
202}
203
204
205status_t
206LegacyAudioConsumer::GetLatencyFor( const media_destination &for_whom, bigtime_t *out_latency,
207									media_node_id *out_timesource )
208{
209	*out_latency = mBuffer_size * 10000 / 4 / 441;
210	return B_OK;
211}
212
213
214status_t
215LegacyAudioConsumer::Connected( const media_source &producer, const media_destination &where,
216							    const media_format &with_format, media_input *out_input )
217{
218	mInput.source = producer;
219	//mInput.format = with_format;
220	mInput.node   = Node();
221	strcpy( mInput.name, "Legacy Audio Consumer" );
222
223	mBuffers = new BBufferGroup;
224
225	uint32 dummy_data = 0;
226	int32  change_tag = 1;
227
228	BBufferConsumer::SetOutputBuffersFor( producer, mDest, mBuffers, static_cast<void *>( &dummy_data ),
229										  &change_tag, true );
230
231	mConnected = true;
232	return B_OK;
233}
234
235
236void
237LegacyAudioConsumer::Disconnected( const media_source &producer, const media_destination &where )
238{
239	if ( mConnected ) {
240		delete mBuffers;
241		mInput.source = media_source::null;
242		mConnected = false;
243	}
244}
245
246status_t
247LegacyAudioConsumer::FormatChanged( const media_source &producer, const media_destination &consumer,
248									int32 change_tag, const media_format &format )
249{
250	return B_OK;
251}
252
253
254/* BMediaEventLooper */
255
256void
257LegacyAudioConsumer::HandleEvent( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent )
258{
259	switch(event->type) {
260		case BTimedEventQueue::B_START:
261			HandleStart( event->event_time );
262			break;
263
264		case BTimedEventQueue::B_STOP:
265			HandleStop();
266			break;
267
268		case BTimedEventQueue::B_WARP:
269			HandleTimeWarp( event->bigdata );
270			break;
271
272		case BTimedEventQueue::B_SEEK:
273			HandleSeek( event->bigdata );
274			break;
275
276		case BTimedEventQueue::B_HANDLE_BUFFER:
277			HandleBuffer( static_cast<BBuffer *>( event->pointer ) );
278			break;
279
280		case BTimedEventQueue::B_DATA_STATUS:
281		case BTimedEventQueue::B_PARAMETER:
282		default:
283			break; //HandleEvent: Unhandled event
284	}
285}
286
287
288/* LegacyAudioConsumer */
289
290void
291LegacyAudioConsumer::HandleStart( bigtime_t performance_time )
292{
293	if ( mRunning ) {
294		return;
295	}
296
297	mPerformanceTimeBase = performance_time;
298
299	//allocate buffer available semaphore
300	mBuffer_avail = create_sem( 0, "legacy audio out buffer avail" );
301
302	if ( mBuffer_avail < B_OK ) {
303		goto init_err1;
304	}
305
306	//allocate buffer free semaphore
307	mBuffer_free = create_sem( 1, "legacy audio out buffer free" );
308
309	if ( mBuffer_free < B_OK ) {
310		goto init_err2;
311	}
312
313	//allocate output completion semaphore
314	mBuffer_waitIO = create_sem( 1, "legacy audio out waitIO" );
315
316	if ( mBuffer_waitIO < B_OK ) {
317		goto init_err3;
318	}
319
320	//tell the driver about the playback completion semaphore
321	DRIVER_SET_PLAYBACK_COMPLETION_SEM( &mBuffer_waitIO, 0 );
322
323	mThread = spawn_thread( _run_thread_, "legacy audio output", B_REAL_TIME_PRIORITY, this );
324
325	if ( mThread < B_OK ) {
326		goto init_err4;
327	}
328
329	io_buf = io_buf1;
330
331	resume_thread( mThread );
332
333	mRunning = true;
334	return;
335
336init_err4:
337	delete_sem( mBuffer_waitIO );
338
339init_err3:
340	delete_sem( mBuffer_free );
341
342init_err2:
343	delete_sem( mBuffer_avail );
344
345init_err1:
346	return;
347}
348
349
350void
351LegacyAudioConsumer::HandleStop()
352{
353	if ( ! mRunning ) {
354		return;
355	}
356
357	delete_sem( mBuffer_avail );
358	delete_sem( mBuffer_free );
359	delete_sem( mBuffer_waitIO );
360	wait_for_thread( mThread, &mThread );
361
362	mRunning = false;
363}
364
365
366void
367LegacyAudioConsumer::HandleTimeWarp( bigtime_t performance_time )
368{
369	mPerformanceTimeBase = performance_time;
370}
371
372
373void
374LegacyAudioConsumer::HandleSeek( bigtime_t performance_time )
375{
376	mPerformanceTimeBase = performance_time;
377}
378
379
380void
381LegacyAudioConsumer::HandleBuffer( BBuffer *buffer )
382{
383	audio_buffer_header *buf;
384
385	//wait for free buffer
386	acquire_sem( mBuffer_free );
387
388	//avoid buffer currently in use
389	buf = const_cast<audio_buffer_header *>( ( io_buf == io_buf2 ) ? io_buf1 : io_buf2 );
390
391	//prepare buffer
392	memcpy( reinterpret_cast<char *>( buf ) + sizeof( audio_buffer_header ), buffer->Data(), buffer->SizeUsed() );
393
394	//tell the io thread a new buffer is ready
395	release_sem( mBuffer_avail );
396}
397
398
399int32
400LegacyAudioConsumer::_run_thread_( void *data )
401{
402	return static_cast<LegacyAudioConsumer *>( data )->RunThread();
403}
404
405
406int32
407LegacyAudioConsumer::RunThread()
408{
409	while ( 1 ) {
410
411		//wait for next buffer
412		if ( acquire_sem( mBuffer_avail ) == B_BAD_SEM_ID ) {
413			return B_OK;
414		}
415
416		//send buffer
417		DRIVER_WRITE_BUFFER( io_buf, 0 );
418
419		//wait for IO
420		if ( acquire_sem( mBuffer_waitIO ) == B_BAD_SEM_ID ) {
421			return B_OK;
422		}
423
424		io_buf = ( io_buf == io_buf2 ) ? io_buf1 : io_buf2;
425
426		//mark buffer free
427		release_sem( mBuffer_free );
428	}
429
430	return B_OK;
431}
432