]> git.sesse.net Git - vlc/blob - modules/audio_output/kai.c
kai: enlarge an audio buffer size to AOUT_MAX_ADVANCE_TIME
[vlc] / modules / audio_output / kai.c
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 "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 )
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( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR )
139     {
140         /* The user has selected an audio device. */
141         if ( val.i_int == AOUT_VAR_STEREO )
142         {
143             format.i_physical_channels
144                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
145         }
146         else if ( val.i_int == AOUT_VAR_MONO )
147         {
148             format.i_physical_channels = AOUT_CHAN_CENTER;
149         }
150     }
151
152     psz_mode = var_InheritString( p_aout, "kai-audio-device" );
153     if( !psz_mode )
154         psz_mode = ( char * )ppsz_kai_audio_device[ 0 ];  // "auto"
155
156     i_kai_mode = KAIM_AUTO;
157     if( strcmp( psz_mode, "dart" ) == 0 )
158         i_kai_mode = KAIM_DART;
159     else if( strcmp( psz_mode, "uniaud" ) == 0 )
160         i_kai_mode = KAIM_UNIAUD;
161     msg_Dbg( p_aout, "selected mode = %s", psz_mode );
162
163     if( psz_mode != ppsz_kai_audio_device[ 0 ])
164         free( psz_mode );
165
166     i_nb_channels = aout_FormatNbChannels( &format );
167     if ( i_nb_channels > 2 )
168     {
169         /* KAI doesn't support more than two channels. */
170         i_nb_channels = 2;
171         format.i_physical_channels
172             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
173     }
174
175     /* Support S16 only */
176     format.i_format = VLC_CODEC_S16N;
177
178     aout_FormatPrepare( &format );
179
180     i_bytes_per_frame = format.i_bytes_per_frame;
181
182     /* Initialize library */
183     if( kaiInit( i_kai_mode ))
184     {
185         msg_Err( p_aout, "cannot initialize KAI");
186
187         return VLC_EGENERIC;
188     }
189
190     ks_wanted.usDeviceIndex   = 0;
191     ks_wanted.ulType          = KAIT_PLAY;
192     ks_wanted.ulBitsPerSample = BPS_16;
193     ks_wanted.ulSamplingRate  = format.i_rate;
194     ks_wanted.ulDataFormat    = MCI_WAVE_FORMAT_PCM;
195     ks_wanted.ulChannels      = i_nb_channels;
196     ks_wanted.ulNumBuffers    = 2;
197     ks_wanted.ulBufferSize    = FRAME_SIZE * i_bytes_per_frame;
198     ks_wanted.fShareable      = !var_InheritBool( p_aout,
199                                                   "kai-audio-exclusive-mode");
200     ks_wanted.pfnCallBack     = KaiCallback;
201     ks_wanted.pCallBackData   = p_aout;
202     msg_Dbg( p_aout, "requested ulBufferSize = %ld", ks_wanted.ulBufferSize );
203
204     /* Open the sound device. */
205     if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
206     {
207         msg_Err( p_aout, "cannot open KAI device");
208
209         goto exit_kai_done;
210     }
211
212     msg_Dbg( p_aout, "open in %s mode",
213              ks_obtained.fShareable ? "shareable" : "exclusive" );
214     msg_Dbg( p_aout, "obtained i_nb_samples = %lu",
215              ks_obtained.ulBufferSize / i_bytes_per_frame );
216     msg_Dbg( p_aout, "obtained i_bytes_per_frame = %d",
217              format.i_bytes_per_frame );
218
219     p_sys->format = *fmt = format;
220
221     p_aout->time_get = TimeGet;
222     p_aout->play     = Play;
223     p_aout->pause    = Pause;
224     p_aout->flush    = Flush;
225
226     aout_SoftVolumeStart( p_aout );
227
228     CreateBuffer( p_aout, AUDIO_BUFFER_SIZE_IN_SECONDS *
229                           format.i_rate * format.i_bytes_per_frame );
230
231     if ( var_Type( p_aout, "audio-device" ) == 0 )
232     {
233         /* First launch. */
234         var_Create( p_aout, "audio-device",
235                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
236         text.psz_string = _("Audio Device");
237         var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
238
239         val.i_int = AOUT_VAR_STEREO;
240         text.psz_string = _("Stereo");
241         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
242         val.i_int = AOUT_VAR_MONO;
243         text.psz_string = _("Mono");
244         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
245         if ( i_nb_channels == 2 )
246         {
247             val.i_int = AOUT_VAR_STEREO;
248         }
249         else
250         {
251             val.i_int = AOUT_VAR_MONO;
252         }
253         var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
254         var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
255     }
256
257     /* Prevent SIG_FPE */
258     _control87(MCW_EM, MCW_EM);
259
260     return VLC_SUCCESS;
261
262 exit_kai_done :
263     kaiDone();
264
265     return VLC_EGENERIC;
266 }
267
268 /*****************************************************************************
269  * Play: play a sound samples buffer
270  *****************************************************************************/
271 static void Play (audio_output_t *p_aout, block_t *block)
272 {
273     aout_sys_t *p_sys = p_aout->sys;
274
275     kaiPlay( p_sys->hkai );
276
277     WriteBuffer( p_aout, block->p_buffer, block->i_buffer );
278
279     block_Release( block );
280 }
281
282 /*****************************************************************************
283  * Close: close the audio device
284  *****************************************************************************/
285 static void Stop ( audio_output_t *p_aout )
286 {
287     aout_sys_t *p_sys = p_aout->sys;
288
289     kaiClose( p_sys->hkai );
290     kaiDone();
291
292     DestroyBuffer( p_aout );
293 }
294
295 /*****************************************************************************
296  * KaiCallback: what to do once KAI has played sound samples
297  *****************************************************************************/
298 static ULONG APIENTRY KaiCallback( PVOID p_cb_data,
299                                    PVOID p_buffer,
300                                    ULONG i_buf_size )
301 {
302     audio_output_t *p_aout = (audio_output_t *)p_cb_data;
303     int i_len;
304
305     i_len = ReadBuffer( p_aout, p_buffer, i_buf_size );
306     if(( ULONG )i_len < i_buf_size )
307         memset(( uint8_t * )p_buffer + i_len, 0, i_buf_size - i_len );
308
309     return i_buf_size;
310 }
311
312 static int Open (vlc_object_t *obj)
313 {
314     audio_output_t *aout = (audio_output_t *)obj;
315     aout_sys_t *sys = calloc( 1, sizeof( aout_sys_t ) );
316
317     if( unlikely( sys == NULL ))
318         return VLC_ENOMEM;
319
320     aout->sys = sys;
321     aout->start = Start;
322     aout->stop = Stop;
323     aout_SoftVolumeInit( aout );
324     return VLC_SUCCESS;
325 }
326
327 static void Close( vlc_object_t *obj )
328 {
329     audio_output_t *aout = (audio_output_t *)obj;
330     aout_sys_t *sys = aout->sys;
331
332     free(sys);
333 }
334
335 static void Pause( audio_output_t *aout, bool pause, mtime_t date )
336 {
337     VLC_UNUSED( date );
338
339     aout_sys_t *sys = aout->sys;
340
341     if( pause )
342         kaiPause( sys->hkai );
343     else
344         kaiResume( sys->hkai );
345 }
346
347 static void Flush( audio_output_t *aout, bool drain )
348 {
349     audio_buffer_t *buffer = aout->sys->buffer;
350
351     vlc_mutex_lock( &buffer->mutex );
352
353     if( drain )
354     {
355         while( buffer->length > 0 )
356             vlc_cond_wait( &buffer->cond, &buffer->mutex );
357     }
358     else
359     {
360         buffer->read_pos = buffer->write_pos;
361         buffer->length   = 0;
362     }
363
364     vlc_mutex_unlock( &buffer->mutex );
365 }
366
367 static int TimeGet( audio_output_t *aout, mtime_t *restrict delay )
368 {
369     aout_sys_t            *sys = aout->sys;
370     audio_sample_format_t *format = &sys->format;
371     audio_buffer_t        *buffer = sys->buffer;
372
373     vlc_mutex_lock( &buffer->mutex );
374
375     *delay = ( buffer->length / format->i_bytes_per_frame ) * CLOCK_FREQ /
376              format->i_rate;
377
378     vlc_mutex_unlock( &buffer->mutex );
379
380     return 0;
381 }
382
383 static int CreateBuffer( audio_output_t *aout, int size )
384 {
385     audio_buffer_t *buffer;
386
387     buffer = calloc( 1, sizeof( *buffer ));
388     if( !buffer )
389         return -1;
390
391     buffer->data = malloc( size );
392     if( !buffer->data )
393     {
394         free( buffer );
395
396         return -1;
397     }
398
399     buffer->size = size;
400
401     vlc_mutex_init( &buffer->mutex );
402     vlc_cond_init( &buffer->cond );
403
404     aout->sys->buffer = buffer;
405
406     return 0;
407 }
408
409 static void DestroyBuffer( audio_output_t *aout )
410 {
411     audio_buffer_t *buffer = aout->sys->buffer;
412
413     vlc_mutex_destroy( &buffer->mutex );
414     vlc_cond_destroy( &buffer->cond );
415
416     free( buffer->data );
417     free( buffer );
418 }
419
420 static int ReadBuffer( audio_output_t *aout, uint8_t *data, int size )
421 {
422     audio_buffer_t *buffer = aout->sys->buffer;
423     int             len;
424     int             remain_len = 0;
425
426     vlc_mutex_lock( &buffer->mutex );
427
428     len = MIN( buffer->length, size );
429     if( buffer->read_pos + len > buffer->size )
430     {
431         remain_len  = len;
432         len         = buffer->size - buffer->read_pos;
433         remain_len -= len;
434     }
435
436     memcpy( data, buffer->data + buffer->read_pos, len );
437     if( remain_len )
438         memcpy( data + len, buffer->data, remain_len );
439
440     len += remain_len;
441
442     buffer->read_pos += len;
443     buffer->read_pos %= buffer->size;
444
445     buffer->length -= len;
446
447     vlc_cond_signal( &buffer->cond );
448
449     vlc_mutex_unlock( &buffer->mutex );
450
451     return len;
452 }
453
454 static int WriteBuffer( audio_output_t *aout, uint8_t *data, int size )
455 {
456     audio_buffer_t *buffer = aout->sys->buffer;
457     int             len;
458     int             remain_len = 0;
459
460     vlc_mutex_lock( &buffer->mutex );
461
462     /* FIXME :
463      * If size is larger than buffer->size, this is locked indefinitely.
464      */
465     while( buffer->length + size > buffer->size )
466         vlc_cond_wait( &buffer->cond, &buffer->mutex );
467
468     len = size;
469     if( buffer->write_pos + len > buffer->size )
470     {
471         remain_len  = len;
472         len         = buffer->size - buffer->write_pos;
473         remain_len -= len;
474     }
475
476     memcpy( buffer->data + buffer->write_pos, data, len );
477     if( remain_len )
478         memcpy( buffer->data, data + len, remain_len );
479
480     buffer->write_pos += size;
481     buffer->write_pos %= buffer->size;
482
483     buffer->length += size;
484
485     vlc_mutex_unlock( &buffer->mutex );
486
487     return size;
488 }