1 /*****************************************************************************
2 * kai.c : KAI audio output plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2010-2013 VLC authors and VideoLAN
5 *
6 * Authors: KO Myung-Hun <komh@chollian.net>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
22
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_aout.h>
33
34 #include <float.h>
35
36 #include <kai.h>
37
38 #define FRAME_SIZE 2048
39
40 #define AUDIO_BUFFER_SIZE_IN_SECONDS ( AOUT_MAX_ADVANCE_TIME / CLOCK_FREQ )
41
42 struct audio_buffer_t
43 {
44 uint8_t *data;
45 int read_pos;
46 int write_pos;
47 int length;
48 int size;
49 vlc_mutex_t mutex;
50 vlc_cond_t cond;
51 };
52
53 typedef struct audio_buffer_t audio_buffer_t;
54
55 /*****************************************************************************
56 * aout_sys_t: KAI audio output method descriptor
57 *****************************************************************************
58 * This structure is part of the audio output thread descriptor.
59 * It describes the specific properties of an audio device.
60 *****************************************************************************/
61 struct aout_sys_t
62 {
63 audio_buffer_t *buffer;
64 HKAI hkai;
65 float soft_gain;
66 bool soft_mute;
67 audio_sample_format_t format;
68 };
69
70 /*****************************************************************************
71 * Local prototypes
72 *****************************************************************************/
73 static int Open ( vlc_object_t * );
74 static void Close ( vlc_object_t * );
75 static void Play ( audio_output_t *_p_aout, block_t *block );
76 static void Pause ( audio_output_t *, bool, mtime_t );
77 static void Flush ( audio_output_t *, bool );
78 static int TimeGet ( audio_output_t *, mtime_t *restrict );
79
80 static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
81
82 static int CreateBuffer ( audio_output_t *, int );
83 static void DestroyBuffer( audio_output_t * );
84 static int ReadBuffer ( audio_output_t *, uint8_t *, int );
85 static int WriteBuffer ( audio_output_t *, uint8_t *, int );
86
87 #include "audio_output/volume.h"
88
89 /*****************************************************************************
90 * Module descriptor
91 *****************************************************************************/
92 #define KAI_AUDIO_DEVICE_TEXT N_( \
93 "Device" )
94 #define KAI_AUDIO_DEVICE_LONGTEXT N_( \
95 "Select a proper audio device to be used by KAI." )
96
97 #define KAI_AUDIO_EXCLUSIVE_MODE_TEXT N_( \
98 "Open audio in exclusive mode." )
99 #define KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT N_( \
100 "Enable this option if you want your audio not to be interrupted by the " \
101 "other audio." )
102
103 static const char *const ppsz_kai_audio_device[] = {
104 "auto", "dart", "uniaud" };
105 static const char *const ppsz_kai_audio_device_text[] = {
106 N_("Auto"), "DART", "UNIAUD" };
107
108 vlc_module_begin ()
109 set_shortname( "KAI" )
110 set_description( N_("K Audio Interface audio output") )
111 set_capability( "audio output", 100 )
set_category(CAT_AUDIO)112 set_category( CAT_AUDIO )
113 set_subcategory( SUBCAT_AUDIO_AOUT )
114 add_string( "kai-audio-device", ppsz_kai_audio_device[0],
115 KAI_AUDIO_DEVICE_TEXT, KAI_AUDIO_DEVICE_LONGTEXT, false )
116 change_string_list( ppsz_kai_audio_device, ppsz_kai_audio_device_text )
117 add_sw_gain( )
118 add_bool( "kai-audio-exclusive-mode", false,
119 KAI_AUDIO_EXCLUSIVE_MODE_TEXT, KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT,
120 true )
121 set_callbacks( Open, Close )
122 vlc_module_end ()
123
124 /*****************************************************************************
125 * Open: open the audio device
126 *****************************************************************************/
127 static int Start ( audio_output_t *p_aout, audio_sample_format_t *fmt )
128 {
129 aout_sys_t *p_sys = p_aout->sys;
130 char *psz_mode;
131 ULONG i_kai_mode;
132 KAISPEC ks_wanted, ks_obtained;
133 int i_nb_channels;
134 int i_bytes_per_frame;
135 vlc_value_t val, text;
136 audio_sample_format_t format = *fmt;
137
138 if( aout_FormatNbChannels( fmt ) == 0 )
139 return VLC_EGENERIC;
140
141 psz_mode = var_InheritString( p_aout, "kai-audio-device" );
142 if( !psz_mode )
143 psz_mode = ( char * )ppsz_kai_audio_device[ 0 ]; // "auto"
144
145 i_kai_mode = KAIM_AUTO;
146 if( strcmp( psz_mode, "dart" ) == 0 )
147 i_kai_mode = KAIM_DART;
148 else if( strcmp( psz_mode, "uniaud" ) == 0 )
149 i_kai_mode = KAIM_UNIAUD;
150 msg_Dbg( p_aout, "selected mode = %s", psz_mode );
151
152 if( psz_mode != ppsz_kai_audio_device[ 0 ])
153 free( psz_mode );
154
155 i_nb_channels = aout_FormatNbChannels( &format );
156 if ( i_nb_channels >= 2 )
157 {
158 /* KAI doesn't support more than two channels. */
159 i_nb_channels = 2;
160 format.i_physical_channels = AOUT_CHANS_STEREO;
161 }
162 else
163 format.i_physical_channels = AOUT_CHAN_CENTER;
164
165 /* Support S16 only */
166 format.i_format = VLC_CODEC_S16N;
167
168 aout_FormatPrepare( &format );
169
170 i_bytes_per_frame = format.i_bytes_per_frame;
171
172 /* Initialize library */
173 if( kaiInit( i_kai_mode ))
174 {
175 msg_Err( p_aout, "cannot initialize KAI");
176
177 return VLC_EGENERIC;
178 }
179
180 ks_wanted.usDeviceIndex = 0;
181 ks_wanted.ulType = KAIT_PLAY;
182 ks_wanted.ulBitsPerSample = BPS_16;
183 ks_wanted.ulSamplingRate = format.i_rate;
184 ks_wanted.ulDataFormat = MCI_WAVE_FORMAT_PCM;
185 ks_wanted.ulChannels = i_nb_channels;
186 ks_wanted.ulNumBuffers = 2;
187 ks_wanted.ulBufferSize = FRAME_SIZE * i_bytes_per_frame;
188 ks_wanted.fShareable = !var_InheritBool( p_aout,
189 "kai-audio-exclusive-mode");
190 ks_wanted.pfnCallBack = KaiCallback;
191 ks_wanted.pCallBackData = p_aout;
192 msg_Dbg( p_aout, "requested ulBufferSize = %ld", ks_wanted.ulBufferSize );
193
194 /* Open the sound device. */
195 if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
196 {
197 msg_Err( p_aout, "cannot open KAI device");
198
199 goto exit_kai_done;
200 }
201
202 msg_Dbg( p_aout, "open in %s mode",
203 ks_obtained.fShareable ? "shareable" : "exclusive" );
204 msg_Dbg( p_aout, "obtained i_nb_samples = %lu",
205 ks_obtained.ulBufferSize / i_bytes_per_frame );
206 msg_Dbg( p_aout, "obtained i_bytes_per_frame = %d",
207 format.i_bytes_per_frame );
208
209 format.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
210 p_sys->format = *fmt = format;
211
212 p_aout->time_get = TimeGet;
213 p_aout->play = Play;
214 p_aout->pause = Pause;
215 p_aout->flush = Flush;
216
217 aout_SoftVolumeStart( p_aout );
218
219 CreateBuffer( p_aout, AUDIO_BUFFER_SIZE_IN_SECONDS *
220 format.i_rate * format.i_bytes_per_frame );
221
222 /* Prevent SIG_FPE */
223 _control87(MCW_EM, MCW_EM);
224
225 return VLC_SUCCESS;
226
227 exit_kai_done :
228 kaiDone();
229
230 return VLC_EGENERIC;
231 }
232
233 /*****************************************************************************
234 * Play: play a sound samples buffer
235 *****************************************************************************/
Play(audio_output_t * p_aout,block_t * block)236 static void Play (audio_output_t *p_aout, block_t *block)
237 {
238 aout_sys_t *p_sys = p_aout->sys;
239
240 kaiPlay( p_sys->hkai );
241
242 WriteBuffer( p_aout, block->p_buffer, block->i_buffer );
243
244 block_Release( block );
245 }
246
247 /*****************************************************************************
248 * Close: close the audio device
249 *****************************************************************************/
Stop(audio_output_t * p_aout)250 static void Stop ( audio_output_t *p_aout )
251 {
252 aout_sys_t *p_sys = p_aout->sys;
253
254 kaiClose( p_sys->hkai );
255 kaiDone();
256
257 DestroyBuffer( p_aout );
258 }
259
260 /*****************************************************************************
261 * KaiCallback: what to do once KAI has played sound samples
262 *****************************************************************************/
KaiCallback(PVOID p_cb_data,PVOID p_buffer,ULONG i_buf_size)263 static ULONG APIENTRY KaiCallback( PVOID p_cb_data,
264 PVOID p_buffer,
265 ULONG i_buf_size )
266 {
267 audio_output_t *p_aout = (audio_output_t *)p_cb_data;
268 int i_len;
269
270 i_len = ReadBuffer( p_aout, p_buffer, i_buf_size );
271 if(( ULONG )i_len < i_buf_size )
272 memset(( uint8_t * )p_buffer + i_len, 0, i_buf_size - i_len );
273
274 return i_buf_size;
275 }
276
Open(vlc_object_t * obj)277 static int Open (vlc_object_t *obj)
278 {
279 audio_output_t *aout = (audio_output_t *)obj;
280 aout_sys_t *sys = calloc( 1, sizeof( aout_sys_t ) );
281
282 if( unlikely( sys == NULL ))
283 return VLC_ENOMEM;
284
285 aout->sys = sys;
286 aout->start = Start;
287 aout->stop = Stop;
288 aout_SoftVolumeInit( aout );
289 return VLC_SUCCESS;
290 }
291
Close(vlc_object_t * obj)292 static void Close( vlc_object_t *obj )
293 {
294 audio_output_t *aout = (audio_output_t *)obj;
295 aout_sys_t *sys = aout->sys;
296
297 free(sys);
298 }
299
Pause(audio_output_t * aout,bool pause,mtime_t date)300 static void Pause( audio_output_t *aout, bool pause, mtime_t date )
301 {
302 VLC_UNUSED( date );
303
304 aout_sys_t *sys = aout->sys;
305
306 if( pause )
307 kaiPause( sys->hkai );
308 else
309 kaiResume( sys->hkai );
310 }
311
Flush(audio_output_t * aout,bool drain)312 static void Flush( audio_output_t *aout, bool drain )
313 {
314 audio_buffer_t *buffer = aout->sys->buffer;
315
316 vlc_mutex_lock( &buffer->mutex );
317
318 if( drain )
319 {
320 while( buffer->length > 0 )
321 vlc_cond_wait( &buffer->cond, &buffer->mutex );
322 }
323 else
324 {
325 buffer->read_pos = buffer->write_pos;
326 buffer->length = 0;
327 }
328
329 vlc_mutex_unlock( &buffer->mutex );
330 }
331
TimeGet(audio_output_t * aout,mtime_t * restrict delay)332 static int TimeGet( audio_output_t *aout, mtime_t *restrict delay )
333 {
334 aout_sys_t *sys = aout->sys;
335 audio_sample_format_t *format = &sys->format;
336 audio_buffer_t *buffer = sys->buffer;
337
338 vlc_mutex_lock( &buffer->mutex );
339
340 *delay = ( buffer->length / format->i_bytes_per_frame ) * CLOCK_FREQ /
341 format->i_rate;
342
343 vlc_mutex_unlock( &buffer->mutex );
344
345 return 0;
346 }
347
CreateBuffer(audio_output_t * aout,int size)348 static int CreateBuffer( audio_output_t *aout, int size )
349 {
350 audio_buffer_t *buffer;
351
352 buffer = calloc( 1, sizeof( *buffer ));
353 if( !buffer )
354 return -1;
355
356 buffer->data = malloc( size );
357 if( !buffer->data )
358 {
359 free( buffer );
360
361 return -1;
362 }
363
364 buffer->size = size;
365
366 vlc_mutex_init( &buffer->mutex );
367 vlc_cond_init( &buffer->cond );
368
369 aout->sys->buffer = buffer;
370
371 return 0;
372 }
373
DestroyBuffer(audio_output_t * aout)374 static void DestroyBuffer( audio_output_t *aout )
375 {
376 audio_buffer_t *buffer = aout->sys->buffer;
377
378 vlc_mutex_destroy( &buffer->mutex );
379 vlc_cond_destroy( &buffer->cond );
380
381 free( buffer->data );
382 free( buffer );
383 }
384
ReadBuffer(audio_output_t * aout,uint8_t * data,int size)385 static int ReadBuffer( audio_output_t *aout, uint8_t *data, int size )
386 {
387 audio_buffer_t *buffer = aout->sys->buffer;
388 int len;
389 int remain_len = 0;
390
391 vlc_mutex_lock( &buffer->mutex );
392
393 len = MIN( buffer->length, size );
394 if( buffer->read_pos + len > buffer->size )
395 {
396 remain_len = len;
397 len = buffer->size - buffer->read_pos;
398 remain_len -= len;
399 }
400
401 memcpy( data, buffer->data + buffer->read_pos, len );
402 if( remain_len )
403 memcpy( data + len, buffer->data, remain_len );
404
405 len += remain_len;
406
407 buffer->read_pos += len;
408 buffer->read_pos %= buffer->size;
409
410 buffer->length -= len;
411
412 vlc_cond_signal( &buffer->cond );
413
414 vlc_mutex_unlock( &buffer->mutex );
415
416 return len;
417 }
418
WriteBuffer(audio_output_t * aout,uint8_t * data,int size)419 static int WriteBuffer( audio_output_t *aout, uint8_t *data, int size )
420 {
421 audio_buffer_t *buffer = aout->sys->buffer;
422 int len;
423 int remain_len = 0;
424
425 vlc_mutex_lock( &buffer->mutex );
426
427 /* FIXME :
428 * If size is larger than buffer->size, this is locked indefinitely.
429 */
430 while( buffer->length + size > buffer->size )
431 vlc_cond_wait( &buffer->cond, &buffer->mutex );
432
433 len = size;
434 if( buffer->write_pos + len > buffer->size )
435 {
436 remain_len = len;
437 len = buffer->size - buffer->write_pos;
438 remain_len -= len;
439 }
440
441 memcpy( buffer->data + buffer->write_pos, data, len );
442 if( remain_len )
443 memcpy( buffer->data, data + len, remain_len );
444
445 buffer->write_pos += size;
446 buffer->write_pos %= buffer->size;
447
448 buffer->length += size;
449
450 vlc_mutex_unlock( &buffer->mutex );
451
452 return size;
453 }
454