1 /*****************************************************************************
2 * kai.c : KAI audio output plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2010 the VideoLAN team
6 * Authors: KO Myung-Hun <komh@chollian.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 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 General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, 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 /*****************************************************************************
41 * aout_sys_t: KAI audio output method descriptor
42 *****************************************************************************
43 * This structure is part of the audio output thread descriptor.
44 * It describes the specific properties of an audio device.
45 *****************************************************************************/
52 audio_sample_format_t format;
55 /*****************************************************************************
57 *****************************************************************************/
58 static int Open ( vlc_object_t * );
59 static void Close ( vlc_object_t * );
60 static void Play ( audio_output_t *_p_aout, block_t *block );
62 static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
66 /*****************************************************************************
68 *****************************************************************************/
69 #define KAI_AUDIO_DEVICE_TEXT N_( \
71 #define KAI_AUDIO_DEVICE_LONGTEXT N_( \
72 "Select a proper audio device to be used by KAI." )
74 #define KAI_AUDIO_EXCLUSIVE_MODE_TEXT N_( \
75 "Open audio in exclusive mode." )
76 #define KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT N_( \
77 "Enable this option if you want your audio not to be interrupted by the " \
80 static const char *const ppsz_kai_audio_device[] = {
81 "auto", "dart", "uniaud" };
82 static const char *const ppsz_kai_audio_device_text[] = {
83 N_("Auto"), "DART", "UNIAUD" };
86 set_shortname( "KAI" )
87 set_description( N_("K Audio Interface audio output") )
88 set_capability( "audio output", 100 )
89 set_category( CAT_AUDIO )
90 set_subcategory( SUBCAT_AUDIO_AOUT )
91 add_string( "kai-audio-device", ppsz_kai_audio_device[0],
92 KAI_AUDIO_DEVICE_TEXT, KAI_AUDIO_DEVICE_LONGTEXT, false )
93 change_string_list( ppsz_kai_audio_device, ppsz_kai_audio_device_text )
95 add_bool( "kai-audio-exclusive-mode", false,
96 KAI_AUDIO_EXCLUSIVE_MODE_TEXT, KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT,
98 set_callbacks( Open, Close )
101 /*****************************************************************************
102 * Open: open the audio device
103 *****************************************************************************/
104 static int Start ( audio_output_t *p_aout, audio_sample_format_t *fmt )
106 aout_sys_t *p_sys = p_aout->sys;
109 KAISPEC ks_wanted, ks_obtained;
111 int i_bytes_per_frame;
112 vlc_value_t val, text;
113 audio_sample_format_t format = *fmt;
115 if( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR )
117 /* The user has selected an audio device. */
118 if ( val.i_int == AOUT_VAR_STEREO )
120 format.i_physical_channels
121 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
123 else if ( val.i_int == AOUT_VAR_MONO )
125 format.i_physical_channels = AOUT_CHAN_CENTER;
129 psz_mode = var_InheritString( p_aout, "kai-audio-device" );
131 psz_mode = ( char * )ppsz_kai_audio_device[ 0 ]; // "auto"
133 i_kai_mode = KAIM_AUTO;
134 if( strcmp( psz_mode, "dart" ) == 0 )
135 i_kai_mode = KAIM_DART;
136 else if( strcmp( psz_mode, "uniaud" ) == 0 )
137 i_kai_mode = KAIM_UNIAUD;
138 msg_Dbg( p_aout, "selected mode = %s", psz_mode );
140 if( psz_mode != ppsz_kai_audio_device[ 0 ])
143 i_nb_channels = aout_FormatNbChannels( &format );
144 if ( i_nb_channels > 2 )
146 /* KAI doesn't support more than two channels. */
148 format.i_physical_channels
149 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
152 /* Support s16l only */
153 format.i_format = VLC_CODEC_S16L;
155 aout_FormatPrepare( &format );
157 i_bytes_per_frame = format.i_bytes_per_frame;
159 /* Initialize library */
160 if( kaiInit( i_kai_mode ))
162 msg_Err( p_aout, "cannot initialize KAI");
167 ks_wanted.usDeviceIndex = 0;
168 ks_wanted.ulType = KAIT_PLAY;
169 ks_wanted.ulBitsPerSample = BPS_16;
170 ks_wanted.ulSamplingRate = format.i_rate;
171 ks_wanted.ulDataFormat = MCI_WAVE_FORMAT_PCM;
172 ks_wanted.ulChannels = i_nb_channels;
173 ks_wanted.ulNumBuffers = 2;
174 ks_wanted.ulBufferSize = FRAME_SIZE * i_bytes_per_frame;
175 ks_wanted.fShareable = !var_InheritBool( p_aout,
176 "kai-audio-exclusive-mode");
177 ks_wanted.pfnCallBack = KaiCallback;
178 ks_wanted.pCallBackData = p_aout;
179 msg_Dbg( p_aout, "requested ulBufferSize = %ld", ks_wanted.ulBufferSize );
181 /* Open the sound device. */
182 if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
184 msg_Err( p_aout, "cannot open KAI device");
189 msg_Dbg( p_aout, "open in %s mode",
190 ks_obtained.fShareable ? "shareable" : "exclusive" );
191 msg_Dbg( p_aout, "obtained i_nb_samples = %lu",
192 ks_obtained.ulBufferSize / i_bytes_per_frame );
193 msg_Dbg( p_aout, "obtained i_bytes_per_frame = %d",
194 format.i_bytes_per_frame );
196 p_sys->format = *fmt = format;
198 p_aout->time_get = aout_PacketTimeGet;
200 p_aout->pause = aout_PacketPause;
201 p_aout->flush = aout_PacketFlush;
203 aout_SoftVolumeStart( p_aout );
205 aout_PacketInit( p_aout, &p_sys->packet,
206 ks_obtained.ulBufferSize / i_bytes_per_frame, &format );
208 if ( var_Type( p_aout, "audio-device" ) == 0 )
211 var_Create( p_aout, "audio-device",
212 VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
213 text.psz_string = _("Audio Device");
214 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
216 val.i_int = AOUT_VAR_STEREO;
217 text.psz_string = _("Stereo");
218 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
219 val.i_int = AOUT_VAR_MONO;
220 text.psz_string = _("Mono");
221 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
222 if ( i_nb_channels == 2 )
224 val.i_int = AOUT_VAR_STEREO;
228 val.i_int = AOUT_VAR_MONO;
230 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
231 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
234 /* Prevent SIG_FPE */
235 _control87(MCW_EM, MCW_EM);
245 /*****************************************************************************
246 * Play: play a sound samples buffer
247 *****************************************************************************/
248 static void Play (audio_output_t *p_aout, block_t *block)
250 aout_sys_t *p_sys = p_aout->sys;
252 kaiPlay( p_sys->hkai );
254 aout_PacketPlay( p_aout, block );
257 /*****************************************************************************
258 * Close: close the audio device
259 *****************************************************************************/
260 static void Stop ( audio_output_t *p_aout )
262 aout_sys_t *p_sys = p_aout->sys;
264 kaiClose( p_sys->hkai );
267 aout_PacketDestroy( p_aout );
270 /*****************************************************************************
271 * KaiCallback: what to do once KAI has played sound samples
272 *****************************************************************************/
273 static ULONG APIENTRY KaiCallback( PVOID p_cb_data,
277 audio_output_t *p_aout = (audio_output_t *)p_cb_data;
278 aout_sys_t *sys = p_aout->sys;
279 block_t *p_aout_buffer;
280 mtime_t current_date, next_date;
283 /* We have 2 buffers, and a callback function is called right after KAI
284 * runs out of a buffer. So we should get a packet to be played after the
287 next_date = mdate() + ( i_buf_size * 1000000LL
288 / sys->format.i_bytes_per_frame
290 * sys->format.i_frame_length );
292 for (i_len = 0; i_len < i_buf_size;)
294 current_date = mdate();
295 if( next_date < current_date )
296 next_date = current_date;
298 /* Get the next audio data buffer */
299 p_aout_buffer = aout_PacketNext( p_aout, next_date );
301 if( p_aout_buffer == NULL )
303 /* Means we are too early to request a new buffer ?
306 msleep( AOUT_MIN_PREPARE_TIME );
308 p_aout_buffer = aout_PacketNext( p_aout, next_date );
311 if ( p_aout_buffer != NULL )
313 memcpy( ( uint8_t * ) p_buffer + i_len,
314 p_aout_buffer->p_buffer,
315 p_aout_buffer->i_buffer );
317 i_len += p_aout_buffer->i_buffer;
319 next_date += p_aout_buffer->i_length;
321 block_Release( p_aout_buffer );
325 memset( ( uint8_t * ) p_buffer + i_len, 0, i_buf_size - i_len );
334 static int Open (vlc_object_t *obj)
336 audio_output_t *aout = (audio_output_t *)obj;
337 aout_sys_t *sys = calloc( 1, sizeof( aout_sys_t ) );
339 if( unlikely( sys == NULL ))
345 aout_SoftVolumeInit( aout );
349 static void Close( vlc_object_t *obj )
351 audio_output_t *aout = (audio_output_t *)obj;
352 aout_sys_t *sys = aout->sys;