]> git.sesse.net Git - vlc/blob - modules/audio_output/kai.c
transform: non functional changes to support more than 8-bits
[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 };
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55 static int  Open  ( vlc_object_t * );
56 static void Close ( vlc_object_t * );
57 static void Play  ( audio_output_t *_p_aout, block_t *block );
58
59 static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
60
61 /*****************************************************************************
62  * Module descriptor
63  *****************************************************************************/
64 #define KAI_AUDIO_DEVICE_TEXT N_( \
65     "Device" )
66 #define KAI_AUDIO_DEVICE_LONGTEXT N_( \
67     "Select a proper audio device to be used by KAI." )
68
69 #define KAI_AUDIO_EXCLUSIVE_MODE_TEXT N_( \
70     "Open audio in exclusive mode." )
71 #define KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT N_( \
72     "Enable this option if you want your audio not to be interrupted by the " \
73     "other audio." )
74
75 static const char *const ppsz_kai_audio_device[] = {
76     "auto", "dart", "uniaud" };
77 static const char *const ppsz_kai_audio_device_text[] = {
78     N_("Auto"), "DART", "UNIAUD" };
79
80 vlc_module_begin ()
81     set_shortname( "KAI" )
82     set_description( N_("K Audio Interface audio output") )
83     set_capability( "audio output", 100 )
84     set_category( CAT_AUDIO )
85     set_subcategory( SUBCAT_AUDIO_AOUT )
86     add_string( "kai-audio-device", ppsz_kai_audio_device[0],
87                 KAI_AUDIO_DEVICE_TEXT, KAI_AUDIO_DEVICE_LONGTEXT, false )
88         change_string_list( ppsz_kai_audio_device, ppsz_kai_audio_device_text,
89                             0 )
90     add_bool( "kai-audio-exclusive-mode", false,
91               KAI_AUDIO_EXCLUSIVE_MODE_TEXT, KAI_AUDIO_EXCLUSIVE_MODE_LONGTEXT,
92               true )
93     set_callbacks( Open, Close )
94 vlc_module_end ()
95
96 /*****************************************************************************
97  * Open: open the audio device
98  *****************************************************************************/
99 static int Open ( vlc_object_t *p_this )
100 {
101     audio_output_t *p_aout = (audio_output_t *)p_this;
102     aout_sys_t *p_sys;
103     char *psz_mode;
104     ULONG i_kai_mode;
105     KAISPEC ks_wanted, ks_obtained;
106     int i_nb_channels;
107     int i_bytes_per_frame;
108     vlc_value_t val, text;
109     audio_format_t format =  p_aout->format;
110
111     /* Allocate structure */
112     p_aout->sys = calloc( 1, sizeof( aout_sys_t ) );
113
114     if( p_aout->sys == NULL )
115         return VLC_ENOMEM;
116
117     p_sys = p_aout->sys;
118
119     if( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR )
120     {
121         /* The user has selected an audio device. */
122         if ( val.i_int == AOUT_VAR_STEREO )
123         {
124             format.i_physical_channels
125                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
126         }
127         else if ( val.i_int == AOUT_VAR_MONO )
128         {
129             format.i_physical_channels = AOUT_CHAN_CENTER;
130         }
131     }
132
133     psz_mode = var_InheritString( p_aout, "kai-audio-device" );
134     if( !psz_mode )
135         psz_mode = ( char * )ppsz_kai_audio_device[ 0 ];  // "auto"
136
137     i_kai_mode = KAIM_AUTO;
138     if( strcmp( psz_mode, "dart" ) == 0 )
139         i_kai_mode = KAIM_DART;
140     else if( strcmp( psz_mode, "uniaud" ) == 0 )
141         i_kai_mode = KAIM_UNIAUD;
142     msg_Dbg( p_aout, "selected mode = %s", psz_mode );
143
144     if( psz_mode != ppsz_kai_audio_device[ 0 ])
145         free( psz_mode );
146
147     i_nb_channels = aout_FormatNbChannels( &format );
148     if ( i_nb_channels > 2 )
149     {
150         /* KAI doesn't support more than two channels. */
151         i_nb_channels = 2;
152         format.i_physical_channels
153             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
154     }
155
156     /* Support s16l only */
157     format.i_format = VLC_CODEC_S16L;
158
159     aout_FormatPrepare( &format );
160
161     i_bytes_per_frame = format.i_bytes_per_frame;
162
163     /* Initialize library */
164     if( kaiInit( i_kai_mode ))
165     {
166         msg_Err( p_aout, "cannot initialize KAI");
167
168         goto exit_free_sys;
169     }
170
171     ks_wanted.usDeviceIndex   = 0;
172     ks_wanted.ulType          = KAIT_PLAY;
173     ks_wanted.ulBitsPerSample = BPS_16;
174     ks_wanted.ulSamplingRate  = format.i_rate;
175     ks_wanted.ulDataFormat    = MCI_WAVE_FORMAT_PCM;
176     ks_wanted.ulChannels      = i_nb_channels;
177     ks_wanted.ulNumBuffers    = 2;
178     ks_wanted.ulBufferSize    = FRAME_SIZE * i_bytes_per_frame;
179     ks_wanted.fShareable      = !var_InheritBool( p_aout,
180                                                   "kai-audio-exclusive-mode");
181     ks_wanted.pfnCallBack     = KaiCallback;
182     ks_wanted.pCallBackData   = p_aout;
183     msg_Dbg( p_aout, "requested ulBufferSize = %ld", ks_wanted.ulBufferSize );
184
185     /* Open the sound device. */
186     if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
187     {
188         msg_Err( p_aout, "cannot open KAI device");
189
190         goto exit_kai_done;
191     }
192
193     msg_Dbg( p_aout, "open in %s mode",
194              ks_obtained.fShareable ? "shareable" : "exclusive" );
195     msg_Dbg( p_aout, "obtained i_nb_samples = %lu",
196              ks_obtained.ulBufferSize / i_bytes_per_frame );
197     msg_Dbg( p_aout, "obtained i_bytes_per_frame = %d",
198              format.i_bytes_per_frame );
199
200     p_aout->format   = format;
201
202     p_aout->pf_play  = Play;
203     p_aout->pf_pause = aout_PacketPause;
204     p_aout->pf_flush = aout_PacketFlush;
205
206     aout_PacketInit( p_aout, &p_sys->packet,
207                      ks_obtained.ulBufferSize / i_bytes_per_frame );
208     aout_VolumeSoftInit( p_aout );
209
210     if ( var_Type( p_aout, "audio-device" ) == 0 )
211     {
212         /* First launch. */
213         var_Create( p_aout, "audio-device",
214                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
215         text.psz_string = _("Audio Device");
216         var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
217
218         val.i_int = AOUT_VAR_STEREO;
219         text.psz_string = _("Stereo");
220         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
221         val.i_int = AOUT_VAR_MONO;
222         text.psz_string = _("Mono");
223         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
224         if ( i_nb_channels == 2 )
225         {
226             val.i_int = AOUT_VAR_STEREO;
227         }
228         else
229         {
230             val.i_int = AOUT_VAR_MONO;
231         }
232         var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
233         var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
234     }
235
236     var_TriggerCallback( p_aout, "intf-change" );
237
238     /* Prevent SIG_FPE */
239     _control87(MCW_EM, MCW_EM);
240
241     return VLC_SUCCESS;
242
243 exit_kai_done :
244     kaiDone();
245
246 exit_free_sys :
247     free( p_sys );
248
249     return VLC_EGENERIC;
250 }
251
252 /*****************************************************************************
253  * Play: play a sound samples buffer
254  *****************************************************************************/
255 static void Play (audio_output_t *p_aout, block_t *block)
256 {
257     aout_sys_t *p_sys = p_aout->sys;
258
259     kaiPlay( p_sys->hkai );
260
261     aout_PacketPlay( p_aout, block );
262 }
263
264 /*****************************************************************************
265  * Close: close the audio device
266  *****************************************************************************/
267 static void Close ( vlc_object_t *p_this )
268 {
269     audio_output_t *p_aout = (audio_output_t *)p_this;
270     aout_sys_t *p_sys = p_aout->sys;
271
272     kaiClose( p_sys->hkai );
273     kaiDone();
274
275     aout_PacketDestroy( p_aout );
276     free( p_sys );
277 }
278
279 /*****************************************************************************
280  * KaiCallback: what to do once KAI has played sound samples
281  *****************************************************************************/
282 static ULONG APIENTRY KaiCallback( PVOID p_cb_data,
283                                    PVOID p_buffer,
284                                    ULONG i_buf_size )
285 {
286     audio_output_t *p_aout = (audio_output_t *)p_cb_data;
287     aout_buffer_t  *p_aout_buffer;
288     mtime_t current_date, next_date;
289     ULONG i_len;
290
291     /* We have 2 buffers, and a callback function is called right after KAI
292      * runs out of a buffer. So we should get a packet to be played after the
293      * remaining buffer.
294      */
295     next_date = mdate() + ( i_buf_size * 1000000LL
296                                        / p_aout->format.i_bytes_per_frame
297                                        / p_aout->format.i_rate
298                                        * p_aout->format.i_frame_length );
299
300     for (i_len = 0; i_len < i_buf_size;)
301     {
302         current_date = mdate();
303         if( next_date < current_date )
304             next_date = current_date;
305
306         /* Get the next audio data buffer */
307         p_aout_buffer = aout_PacketNext( p_aout, next_date );
308
309         if( p_aout_buffer == NULL )
310         {
311             /* Means we are too early to request a new buffer ?
312              * Try once again.
313              */
314             msleep( AOUT_MIN_PREPARE_TIME );
315             next_date = mdate();
316             p_aout_buffer = aout_PacketNext( p_aout, next_date );
317         }
318
319         if ( p_aout_buffer != NULL )
320         {
321             vlc_memcpy( ( uint8_t * ) p_buffer + i_len,
322                         p_aout_buffer->p_buffer,
323                         p_aout_buffer->i_buffer );
324
325             i_len += p_aout_buffer->i_buffer;
326
327             next_date += p_aout_buffer->i_length;
328
329             aout_BufferFree( p_aout_buffer );
330         }
331         else
332         {
333             vlc_memset( ( uint8_t * ) p_buffer + i_len, 0, i_buf_size - i_len );
334
335             i_len = i_buf_size;
336         }
337     }
338
339     return i_buf_size;
340 }