]> git.sesse.net Git - vlc/blob - modules/audio_output/portaudio.c
* modules/audio_output/portaudio.c: portaudio (www.portaudio.com) audio output plugin...
[vlc] / modules / audio_output / portaudio.c
1 /*****************************************************************************
2  * portaudio.c : portaudio audio output plugin
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id$
6  *
7  * Authors: Frederic Ruget <frederic.ruget@free.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/aout.h>
32 #include <portaudio.h>
33
34 #include "aout_internal.h"
35
36 #define FRAME_SIZE 1024              /* The size is in samples, not in bytes */
37 #define FRAMES_NUM 8
38
39 /*****************************************************************************
40  * aout_sys_t: portaudio audio output method descriptor
41  *****************************************************************************/
42 struct aout_sys_t
43 {
44     aout_instance_t *p_aout;
45     PortAudioStream *p_stream;
46     int i_numDevices;
47     int i_nbChannels;
48     PaSampleFormat sampleFormat;
49     int i_sampleSize;
50     PaDeviceID i_deviceId;
51     PaDeviceInfo deviceInfo;
52 };
53
54 /*****************************************************************************
55  * Local prototypes.
56  *****************************************************************************/
57 static int  Open        ( vlc_object_t * );
58 static void Close       ( vlc_object_t * );
59 static void Play        ( aout_instance_t * );
60 static int i_once = 0;
61
62 /*****************************************************************************
63  * Module descriptor
64  *****************************************************************************/
65 #define DEVICE_TEXT N_("Output device")
66 #define DEVICE_LONGTEXT N_("Portaudio identifier for the output device")
67
68 vlc_module_begin();
69     set_description( N_("PORTAUDIO audio output") );
70     add_integer( "portaudio-device", 0, NULL,
71                  DEVICE_TEXT, DEVICE_LONGTEXT, VLC_FALSE );
72     set_capability( "audio output", 40 );
73     set_callbacks( Open, Close );
74 vlc_module_end();
75
76 /* This routine will be called by the PortAudio engine when audio is needed.
77 ** It may called at interrupt level on some machines so don't do anything
78 ** that could mess up the system like calling malloc() or free().
79 */
80 static int paCallback( void *inputBuffer, void *outputBuffer,
81                        unsigned long framesPerBuffer,
82                        PaTimestamp outTime, void *p_cookie )
83 {
84     struct aout_sys_t* p_sys = (struct aout_sys_t*) p_cookie;
85     aout_instance_t * p_aout = p_sys->p_aout;
86     aout_buffer_t *   p_buffer;
87
88     vlc_mutex_lock( &p_aout->output_fifo_lock );
89     p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
90     vlc_mutex_unlock( &p_aout->output_fifo_lock );
91
92     if ( p_buffer != NULL )
93     {
94         p_aout->p_vlc->pf_memcpy( outputBuffer, p_buffer->p_buffer,
95                                   framesPerBuffer * p_sys->i_sampleSize );
96         aout_BufferFree( p_buffer );
97     }
98     else
99     {
100       p_aout->p_vlc->pf_memset( outputBuffer, 0,
101                                 framesPerBuffer * p_sys->i_sampleSize );
102     }
103     return 0;
104 }
105
106 /*****************************************************************************
107  * Open: open the audio device
108  *****************************************************************************/
109 static int Open ( vlc_object_t * p_this )
110 {
111     aout_instance_t *p_aout = (aout_instance_t *)p_this;
112     struct aout_sys_t * p_sys;
113     PortAudioStream *p_stream;
114     vlc_value_t val;
115     PaError i_err;
116     int i_nb_channels;
117     const PaDeviceInfo *p_pdi;
118     int i, j;
119
120     msg_Dbg( p_aout, "Entering Open()");
121
122     /* Allocate p_sys structure */
123     p_sys = (struct aout_sys_t*) malloc( sizeof( aout_sys_t ) );
124     if( p_sys == NULL )
125     {
126         msg_Err( p_aout, "out of memory" );
127         return VLC_ENOMEM;
128     }
129     p_sys->p_aout = p_aout;
130     p_aout->output.p_sys = p_sys;
131
132     /* Output device id */
133     var_Create( p_this, "portaudio-device",
134                 VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
135     var_Get( p_this, "portaudio-device", &val );
136     p_sys->i_deviceId = val.i_int;
137
138     if (! i_once)
139     {
140         i_once = 1;
141         i_err = Pa_Initialize();
142         if ( i_err != paNoError )
143         {
144             msg_Err( p_aout, "Pa_Initialize returned %d : %s", i_err, Pa_GetErrorText( i_err ));
145             return VLC_EGENERIC;
146         }
147     }
148     p_sys->i_numDevices = Pa_CountDevices();
149     if( p_sys->i_numDevices < 0 )
150     {
151         i_err = p_sys->i_numDevices;
152         msg_Err( p_aout, "Pa_CountDevices returned %d : %s", i_err, Pa_GetErrorText( i_err ));
153         (void) Pa_Terminate();
154         return VLC_EGENERIC;
155     }
156     msg_Info( p_aout, "Number of devices = %d", p_sys->i_numDevices );
157     if ( p_sys->i_deviceId >= p_sys->i_numDevices )
158     {
159         msg_Err( p_aout, "Device %d does not exist", p_sys->i_deviceId );
160         (void) Pa_Terminate();
161         return VLC_EGENERIC;
162     }
163     for( i = 0; i < p_sys->i_numDevices; i++ )
164     {
165         p_pdi = Pa_GetDeviceInfo( i );
166         if ( i == p_sys->i_deviceId )
167         {
168             p_sys->deviceInfo = *p_pdi;
169         }
170         msg_Info( p_aout, "---------------------------------------------- #%d", i );
171         msg_Info( p_aout, "Name         = %s", p_pdi->name );
172         msg_Info( p_aout, "Max Inputs   = %d, Max Outputs = %d",
173                  p_pdi->maxInputChannels, p_pdi->maxOutputChannels );
174         if( p_pdi->numSampleRates == -1 )
175         {
176             msg_Info( p_aout, "Sample Rate Range = %f to %f", p_pdi->sampleRates[0], p_pdi->sampleRates[1] );
177         }
178         else
179         {
180             msg_Info( p_aout, "Sample Rates =");
181             for( j = 0; j < p_pdi->numSampleRates; j++ )
182             {
183                 msg_Info( p_aout, " %8.2f,", p_pdi->sampleRates[j] );
184             }
185         }
186         msg_Info( p_aout, "Native Sample Formats = ");
187         if( p_pdi->nativeSampleFormats & paInt8 )        msg_Info( p_aout, "paInt8");
188         if( p_pdi->nativeSampleFormats & paUInt8 )       msg_Info( p_aout, "paUInt8");
189         if( p_pdi->nativeSampleFormats & paInt16 )       msg_Info( p_aout, "paInt16");
190         if( p_pdi->nativeSampleFormats & paInt32 )       msg_Info( p_aout, "paInt32");
191         if( p_pdi->nativeSampleFormats & paFloat32 )     msg_Info( p_aout, "paFloat32");
192         if( p_pdi->nativeSampleFormats & paInt24 )       msg_Info( p_aout, "paInt24");
193         if( p_pdi->nativeSampleFormats & paPackedInt24 ) msg_Info( p_aout, "paPackedInt24");
194     }
195
196     msg_Info( p_aout, "----------------------------------------------");
197
198
199     /*
200 portaudio warning: Number of devices = 3
201 portaudio warning: ---------------------------------------------- #0 DefaultInput DefaultOutput
202 portaudio warning: Name         = PORTAUDIO DirectX Full Duplex Driver
203 portaudio warning: Max Inputs   = 2, Max Outputs = 2
204 portaudio warning: Sample Rates =
205 portaudio warning:  11025.00,
206 portaudio warning:  22050.00,
207 portaudio warning:  32000.00,
208 portaudio warning:  44100.00,
209 portaudio warning:  48000.00,
210 portaudio warning:  88200.00,
211 portaudio warning:  96000.00,
212 portaudio warning: Native Sample Formats = 
213 portaudio warning: paInt16
214 portaudio warning: ---------------------------------------------- #1
215 portaudio warning: Name         = PORTAUDIO Multimedia Driver
216 portaudio warning: Max Inputs   = 2, Max Outputs = 2
217 portaudio warning: Sample Rates =
218 portaudio warning:  11025.00,
219 portaudio warning:  22050.00,
220 portaudio warning:  32000.00,
221 portaudio warning:  44100.00,
222 portaudio warning:  48000.00,
223 portaudio warning:  88200.00,
224 portaudio warning:  96000.00,
225 portaudio warning: Native Sample Formats = 
226 portaudio warning: paInt16
227 portaudio warning: ---------------------------------------------- #2
228 portaudio warning: Name         = E-MU PORTAUDIO
229 portaudio warning: Max Inputs   = 0, Max Outputs = 4
230 portaudio warning: Sample Rates =
231 portaudio warning:  44100.00,
232 portaudio warning:  48000.00,
233 portaudio warning:  96000.00,
234 portaudio warning: Native Sample Formats = 
235 portaudio warning: paInt16
236 portaudio warning: ----------------------------------------------
237      */
238
239     p_aout->output.pf_play = Play;
240     aout_VolumeSoftInit( p_aout );
241
242     /* select audio format */
243     if( p_sys->deviceInfo.nativeSampleFormats & paFloat32 )
244     {
245         p_sys->sampleFormat = paFloat32;
246         p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
247         p_sys->i_sampleSize = 4;
248     }
249     else if( p_sys->deviceInfo.nativeSampleFormats & paInt16 )
250     {
251         p_sys->sampleFormat = paInt16;
252         p_aout->output.output.i_format = AOUT_FMT_S16_NE;
253         p_sys->i_sampleSize = 2;
254     }
255     else
256     {
257         msg_Err( p_aout, "Audio format not supported" );
258         (void) Pa_Terminate();
259         return VLC_EGENERIC;
260     }
261
262     i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
263     msg_Info( p_aout, "nb_channels = %d", i_nb_channels );
264     if ( i_nb_channels > p_sys->deviceInfo.maxOutputChannels )
265     {
266         if ( p_sys->deviceInfo.maxOutputChannels < 1 )
267         {
268             msg_Err( p_aout, "No channel available" );
269             (void) Pa_Terminate();
270             return VLC_EGENERIC;
271         }
272         else if ( p_sys->deviceInfo.maxOutputChannels < 2 )
273         {
274             p_sys->i_nbChannels = 1;
275             p_aout->output.output.i_physical_channels
276             = AOUT_CHAN_CENTER;
277         }
278         else if ( p_sys->deviceInfo.maxOutputChannels < 4 )
279         {
280             p_sys->i_nbChannels = 2;
281             p_aout->output.output.i_physical_channels
282             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
283         }
284         else if ( p_sys->deviceInfo.maxOutputChannels < 6 )
285         {
286             p_sys->i_nbChannels = 4;
287             p_aout->output.output.i_physical_channels
288             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
289                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
290         }
291         else
292         {
293             p_sys->i_nbChannels = 6;
294             p_aout->output.output.i_physical_channels
295             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
296                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
297                | AOUT_CHAN_LFE;
298         }
299     }
300     p_sys->i_sampleSize *= p_sys->i_nbChannels;
301
302     /* Open portaudio stream */
303     p_aout->output.i_nb_samples = FRAME_SIZE;
304     msg_Info( p_aout, "rate = %d", p_aout->output.output.i_rate );
305     msg_Info( p_aout, "samples = %d", p_aout->output.i_nb_samples );
306     
307     i_err = Pa_OpenStream(
308               &p_stream,
309               paNoDevice, 0, 0, 0,  /* no input device */
310               p_sys->i_deviceId,    /* output device */
311               p_sys->i_nbChannels,
312               p_sys->sampleFormat,
313               NULL,
314               (double) p_aout->output.output.i_rate,
315               (unsigned long) p_aout->output.i_nb_samples,  /* FRAMES_PER_BUFFER */
316               FRAMES_NUM, /* number of buffers, if zero then use default minimum */
317               paClipOff,  /* we won't output out of range samples so don't bother clipping them */
318               paCallback, p_sys );
319     if( i_err != paNoError )
320     {
321         msg_Err( p_aout, "Pa_OpenStream returns %d : %s", i_err,
322                  Pa_GetErrorText( i_err ) );
323         (void) Pa_Terminate();
324         return VLC_EGENERIC;
325     }
326
327     p_sys->p_stream = p_stream;
328     i_err = Pa_StartStream( p_stream );
329     if( i_err != paNoError )
330     {
331         (void) Pa_CloseStream( p_stream);
332         (void) Pa_Terminate();
333         return VLC_EGENERIC;
334     }
335
336     msg_Dbg( p_aout, "Leaving Open()" );
337     return VLC_SUCCESS;
338 }
339
340 /*****************************************************************************
341  * Close: close the audio device
342  *****************************************************************************/
343 static void Close ( vlc_object_t *p_this )
344 {
345     aout_instance_t *p_aout = (aout_instance_t *)p_this;
346     struct aout_sys_t * p_sys = p_aout->output.p_sys;
347     PortAudioStream *p_stream = p_sys->p_stream;
348     PaError i_err;
349
350     msg_Dbg( p_aout, "Entering Close()");
351
352     i_err = Pa_AbortStream( p_stream );
353     if ( i_err != paNoError )
354     {
355         msg_Err( p_aout, "Pa_AbortStream: %d (%s)", i_err, Pa_GetErrorText( i_err ) );
356     }
357     i_err = Pa_CloseStream( p_stream );
358     if ( i_err != paNoError )
359     {
360         msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err, Pa_GetErrorText( i_err ) );
361     }
362     i_err = Pa_Terminate();
363     if ( i_err != paNoError )
364     {
365         msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err, Pa_GetErrorText( i_err ) );
366     }
367     msg_Dbg( p_aout, "Leaving Close()");
368 }
369
370 /*****************************************************************************
371  * Play: play sound
372  *****************************************************************************/
373 static void Play( aout_instance_t * p_aout )
374 {
375 }