]> git.sesse.net Git - vlc/blob - modules/audio_output/kai.c
aout: separate time and play callbacks
[vlc] / modules / audio_output / kai.c
1 /*****************************************************************************
2  * kai.c : KAI audio output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2010 the VideoLAN team
5  *
6  * Authors: KO Myung-Hun <komh@chollian.net>
7  *
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.
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 General Public License for more details.
17  *
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  *****************************************************************************/
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 /*****************************************************************************
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  *****************************************************************************/
46 struct aout_sys_t
47 {
48     aout_packet_t   packet;
49     HKAI            hkai;
50     float           soft_gain;
51     bool            soft_mute;
52     audio_sample_format_t format;
53 };
54
55 /*****************************************************************************
56  * Local prototypes
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 );
61
62 static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
63
64 #include "volume.h"
65
66 /*****************************************************************************
67  * Module descriptor
68  *****************************************************************************/
69 #define KAI_AUDIO_DEVICE_TEXT N_( \
70     "Device" )
71 #define KAI_AUDIO_DEVICE_LONGTEXT N_( \
72     "Select a proper audio device to be used by KAI." )
73
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 " \
78     "other audio." )
79
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" };
84
85 vlc_module_begin ()
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 )
94     add_sw_gain( )
95     add_bool( "kai-audio-exclusive-mode", false,
96               KAI_AUDIO_EXCLUSIVE_MODE_TEXT, KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT,
97               true )
98     set_callbacks( Open, Close )
99 vlc_module_end ()
100
101 /*****************************************************************************
102  * Open: open the audio device
103  *****************************************************************************/
104 static int Start ( audio_output_t *p_aout, audio_sample_format_t *fmt )
105 {
106     aout_sys_t *p_sys = p_aout->sys;
107     char *psz_mode;
108     ULONG i_kai_mode;
109     KAISPEC ks_wanted, ks_obtained;
110     int i_nb_channels;
111     int i_bytes_per_frame;
112     vlc_value_t val, text;
113     audio_sample_format_t format = *fmt;
114
115     if( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR )
116     {
117         /* The user has selected an audio device. */
118         if ( val.i_int == AOUT_VAR_STEREO )
119         {
120             format.i_physical_channels
121                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
122         }
123         else if ( val.i_int == AOUT_VAR_MONO )
124         {
125             format.i_physical_channels = AOUT_CHAN_CENTER;
126         }
127     }
128
129     psz_mode = var_InheritString( p_aout, "kai-audio-device" );
130     if( !psz_mode )
131         psz_mode = ( char * )ppsz_kai_audio_device[ 0 ];  // "auto"
132
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 );
139
140     if( psz_mode != ppsz_kai_audio_device[ 0 ])
141         free( psz_mode );
142
143     i_nb_channels = aout_FormatNbChannels( &format );
144     if ( i_nb_channels > 2 )
145     {
146         /* KAI doesn't support more than two channels. */
147         i_nb_channels = 2;
148         format.i_physical_channels
149             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
150     }
151
152     /* Support s16l only */
153     format.i_format = VLC_CODEC_S16L;
154
155     aout_FormatPrepare( &format );
156
157     i_bytes_per_frame = format.i_bytes_per_frame;
158
159     /* Initialize library */
160     if( kaiInit( i_kai_mode ))
161     {
162         msg_Err( p_aout, "cannot initialize KAI");
163
164         return VLC_EGENERIC;
165     }
166
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 );
180
181     /* Open the sound device. */
182     if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
183     {
184         msg_Err( p_aout, "cannot open KAI device");
185
186         goto exit_kai_done;
187     }
188
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 );
195
196     p_sys->format = *fmt = format;
197
198     p_aout->time_get = aout_PacketTimeGet;
199     p_aout->play  = Play;
200     p_aout->pause = aout_PacketPause;
201     p_aout->flush = aout_PacketFlush;
202
203     aout_SoftVolumeStart( p_aout );
204
205     aout_PacketInit( p_aout, &p_sys->packet,
206                      ks_obtained.ulBufferSize / i_bytes_per_frame, &format );
207
208     if ( var_Type( p_aout, "audio-device" ) == 0 )
209     {
210         /* First launch. */
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 );
215
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 )
223         {
224             val.i_int = AOUT_VAR_STEREO;
225         }
226         else
227         {
228             val.i_int = AOUT_VAR_MONO;
229         }
230         var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
231         var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
232     }
233
234     /* Prevent SIG_FPE */
235     _control87(MCW_EM, MCW_EM);
236
237     return VLC_SUCCESS;
238
239 exit_kai_done :
240     kaiDone();
241
242     return VLC_EGENERIC;
243 }
244
245 /*****************************************************************************
246  * Play: play a sound samples buffer
247  *****************************************************************************/
248 static void Play (audio_output_t *p_aout, block_t *block)
249 {
250     aout_sys_t *p_sys = p_aout->sys;
251
252     kaiPlay( p_sys->hkai );
253
254     aout_PacketPlay( p_aout, block );
255 }
256
257 /*****************************************************************************
258  * Close: close the audio device
259  *****************************************************************************/
260 static void Stop ( audio_output_t *p_aout )
261 {
262     aout_sys_t *p_sys = p_aout->sys;
263
264     kaiClose( p_sys->hkai );
265     kaiDone();
266
267     aout_PacketDestroy( p_aout );
268 }
269
270 /*****************************************************************************
271  * KaiCallback: what to do once KAI has played sound samples
272  *****************************************************************************/
273 static ULONG APIENTRY KaiCallback( PVOID p_cb_data,
274                                    PVOID p_buffer,
275                                    ULONG i_buf_size )
276 {
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;
281     ULONG i_len;
282
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
285      * remaining buffer.
286      */
287     next_date = mdate() + ( i_buf_size * 1000000LL
288                                        / sys->format.i_bytes_per_frame
289                                        / sys->format.i_rate
290                                        * sys->format.i_frame_length );
291
292     for (i_len = 0; i_len < i_buf_size;)
293     {
294         current_date = mdate();
295         if( next_date < current_date )
296             next_date = current_date;
297
298         /* Get the next audio data buffer */
299         p_aout_buffer = aout_PacketNext( p_aout, next_date );
300
301         if( p_aout_buffer == NULL )
302         {
303             /* Means we are too early to request a new buffer ?
304              * Try once again.
305              */
306             msleep( AOUT_MIN_PREPARE_TIME );
307             next_date = mdate();
308             p_aout_buffer = aout_PacketNext( p_aout, next_date );
309         }
310
311         if ( p_aout_buffer != NULL )
312         {
313             memcpy( ( uint8_t * ) p_buffer + i_len,
314                         p_aout_buffer->p_buffer,
315                         p_aout_buffer->i_buffer );
316
317             i_len += p_aout_buffer->i_buffer;
318
319             next_date += p_aout_buffer->i_length;
320
321             block_Release( p_aout_buffer );
322         }
323         else
324         {
325             memset( ( uint8_t * ) p_buffer + i_len, 0, i_buf_size - i_len );
326
327             i_len = i_buf_size;
328         }
329     }
330
331     return i_buf_size;
332 }
333
334 static int Open (vlc_object_t *obj)
335 {
336     audio_output_t *aout = (audio_output_t *)obj;
337     aout_sys_t *sys = calloc( 1, sizeof( aout_sys_t ) );
338
339     if( unlikely( sys == NULL ))
340         return VLC_ENOMEM;
341
342     aout->sys = sys;
343     aout->start = Start;
344     aout->stop = Stop;
345     aout_SoftVolumeInit( aout );
346     return VLC_SUCCESS;
347 }
348
349 static void Close( vlc_object_t *obj )
350 {
351     audio_output_t *aout = (audio_output_t *)obj;
352     aout_sys_t *sys = aout->sys;
353
354     free(sys);
355 }