1 /*****************************************************************************
2 * kai.c : KAI audio output plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2010-2013 VLC authors and VideoLAN
6 * Authors: KO Myung-Hun <komh@chollian.net>
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.
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.
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 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
38 #define FRAME_SIZE 2048
40 #define AUDIO_BUFFER_SIZE_IN_SECONDS ( AOUT_MAX_ADVANCE_TIME / CLOCK_FREQ )
53 typedef struct audio_buffer_t audio_buffer_t;
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 *****************************************************************************/
63 audio_buffer_t *buffer;
67 audio_sample_format_t format;
70 /*****************************************************************************
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 );
80 static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
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 );
89 /*****************************************************************************
91 *****************************************************************************/
92 #define KAI_AUDIO_DEVICE_TEXT N_( \
94 #define KAI_AUDIO_DEVICE_LONGTEXT N_( \
95 "Select a proper audio device to be used by KAI." )
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 " \
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" };
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 )
118 add_bool( "kai-audio-exclusive-mode", false,
119 KAI_AUDIO_EXCLUSIVE_MODE_TEXT, KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT,
121 set_callbacks( Open, Close )
124 /*****************************************************************************
125 * Open: open the audio device
126 *****************************************************************************/
127 static int Start ( audio_output_t *p_aout, audio_sample_format_t *fmt )
129 aout_sys_t *p_sys = p_aout->sys;
132 KAISPEC ks_wanted, ks_obtained;
134 int i_bytes_per_frame;
135 vlc_value_t val, text;
136 audio_sample_format_t format = *fmt;
138 if( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR )
140 /* The user has selected an audio device. */
141 if ( val.i_int == AOUT_VAR_STEREO )
143 format.i_physical_channels
144 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
146 else if ( val.i_int == AOUT_VAR_MONO )
148 format.i_physical_channels = AOUT_CHAN_CENTER;
152 psz_mode = var_InheritString( p_aout, "kai-audio-device" );
154 psz_mode = ( char * )ppsz_kai_audio_device[ 0 ]; // "auto"
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 );
163 if( psz_mode != ppsz_kai_audio_device[ 0 ])
166 i_nb_channels = aout_FormatNbChannels( &format );
167 if ( i_nb_channels > 2 )
169 /* KAI doesn't support more than two channels. */
171 format.i_physical_channels
172 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
175 /* Support S16 only */
176 format.i_format = VLC_CODEC_S16N;
178 aout_FormatPrepare( &format );
180 i_bytes_per_frame = format.i_bytes_per_frame;
182 /* Initialize library */
183 if( kaiInit( i_kai_mode ))
185 msg_Err( p_aout, "cannot initialize KAI");
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 );
204 /* Open the sound device. */
205 if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
207 msg_Err( p_aout, "cannot open KAI device");
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 );
219 p_sys->format = *fmt = format;
221 p_aout->time_get = TimeGet;
223 p_aout->pause = Pause;
224 p_aout->flush = Flush;
226 aout_SoftVolumeStart( p_aout );
228 CreateBuffer( p_aout, AUDIO_BUFFER_SIZE_IN_SECONDS *
229 format.i_rate * format.i_bytes_per_frame );
231 if ( var_Type( p_aout, "audio-device" ) == 0 )
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 );
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 )
247 val.i_int = AOUT_VAR_STEREO;
251 val.i_int = AOUT_VAR_MONO;
253 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
254 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
257 /* Prevent SIG_FPE */
258 _control87(MCW_EM, MCW_EM);
268 /*****************************************************************************
269 * Play: play a sound samples buffer
270 *****************************************************************************/
271 static void Play (audio_output_t *p_aout, block_t *block)
273 aout_sys_t *p_sys = p_aout->sys;
275 kaiPlay( p_sys->hkai );
277 WriteBuffer( p_aout, block->p_buffer, block->i_buffer );
279 block_Release( block );
282 /*****************************************************************************
283 * Close: close the audio device
284 *****************************************************************************/
285 static void Stop ( audio_output_t *p_aout )
287 aout_sys_t *p_sys = p_aout->sys;
289 kaiClose( p_sys->hkai );
292 DestroyBuffer( p_aout );
295 /*****************************************************************************
296 * KaiCallback: what to do once KAI has played sound samples
297 *****************************************************************************/
298 static ULONG APIENTRY KaiCallback( PVOID p_cb_data,
302 audio_output_t *p_aout = (audio_output_t *)p_cb_data;
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 );
312 static int Open (vlc_object_t *obj)
314 audio_output_t *aout = (audio_output_t *)obj;
315 aout_sys_t *sys = calloc( 1, sizeof( aout_sys_t ) );
317 if( unlikely( sys == NULL ))
323 aout_SoftVolumeInit( aout );
327 static void Close( vlc_object_t *obj )
329 audio_output_t *aout = (audio_output_t *)obj;
330 aout_sys_t *sys = aout->sys;
335 static void Pause( audio_output_t *aout, bool pause, mtime_t date )
339 aout_sys_t *sys = aout->sys;
342 kaiPause( sys->hkai );
344 kaiResume( sys->hkai );
347 static void Flush( audio_output_t *aout, bool drain )
349 audio_buffer_t *buffer = aout->sys->buffer;
351 vlc_mutex_lock( &buffer->mutex );
355 while( buffer->length > 0 )
356 vlc_cond_wait( &buffer->cond, &buffer->mutex );
360 buffer->read_pos = buffer->write_pos;
364 vlc_mutex_unlock( &buffer->mutex );
367 static int TimeGet( audio_output_t *aout, mtime_t *restrict delay )
369 aout_sys_t *sys = aout->sys;
370 audio_sample_format_t *format = &sys->format;
371 audio_buffer_t *buffer = sys->buffer;
373 vlc_mutex_lock( &buffer->mutex );
375 *delay = ( buffer->length / format->i_bytes_per_frame ) * CLOCK_FREQ /
378 vlc_mutex_unlock( &buffer->mutex );
383 static int CreateBuffer( audio_output_t *aout, int size )
385 audio_buffer_t *buffer;
387 buffer = calloc( 1, sizeof( *buffer ));
391 buffer->data = malloc( size );
401 vlc_mutex_init( &buffer->mutex );
402 vlc_cond_init( &buffer->cond );
404 aout->sys->buffer = buffer;
409 static void DestroyBuffer( audio_output_t *aout )
411 audio_buffer_t *buffer = aout->sys->buffer;
413 vlc_mutex_destroy( &buffer->mutex );
414 vlc_cond_destroy( &buffer->cond );
416 free( buffer->data );
420 static int ReadBuffer( audio_output_t *aout, uint8_t *data, int size )
422 audio_buffer_t *buffer = aout->sys->buffer;
426 vlc_mutex_lock( &buffer->mutex );
428 len = MIN( buffer->length, size );
429 if( buffer->read_pos + len > buffer->size )
432 len = buffer->size - buffer->read_pos;
436 memcpy( data, buffer->data + buffer->read_pos, len );
438 memcpy( data + len, buffer->data, remain_len );
442 buffer->read_pos += len;
443 buffer->read_pos %= buffer->size;
445 buffer->length -= len;
447 vlc_cond_signal( &buffer->cond );
449 vlc_mutex_unlock( &buffer->mutex );
454 static int WriteBuffer( audio_output_t *aout, uint8_t *data, int size )
456 audio_buffer_t *buffer = aout->sys->buffer;
460 vlc_mutex_lock( &buffer->mutex );
463 * If size is larger than buffer->size, this is locked indefinitely.
465 while( buffer->length + size > buffer->size )
466 vlc_cond_wait( &buffer->cond, &buffer->mutex );
469 if( buffer->write_pos + len > buffer->size )
472 len = buffer->size - buffer->write_pos;
476 memcpy( buffer->data + buffer->write_pos, data, len );
478 memcpy( buffer->data, data + len, remain_len );
480 buffer->write_pos += size;
481 buffer->write_pos %= buffer->size;
483 buffer->length += size;
485 vlc_mutex_unlock( &buffer->mutex );