Audio output 3. Expect major breakages.
[vlc.git] / modules / audio_output / waveout.c
1 /*****************************************************************************
2  * waveout.c : Windows waveOut plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: waveout.c,v 1.1 2002/08/07 21:36:55 massiot Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
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 <errno.h>                                                 /* ENOMEM */
28 #include <fcntl.h>                                       /* open(), O_WRONLY */
29 #include <string.h>                                            /* strerror() */
30
31 #include <stdlib.h>                            /* calloc(), malloc(), free() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35
36 #include <mmsystem.h>
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int  Open         ( vlc_object_t * );             
42 static void Close        ( vlc_object_t * );                   
43
44 static int  SetFormat    ( aout_thread_t * );  
45 static int  GetBufInfo   ( aout_thread_t *, int );
46 static void Play         ( aout_thread_t *, byte_t *, int );
47
48 /* local functions */
49 static int     OpenWaveOutDevice( aout_thread_t *p_aout );
50
51 /*****************************************************************************
52  * Module descriptor
53  *****************************************************************************/
54 vlc_module_begin();
55     set_description( _("Win32 waveOut extension module") ); 
56     set_capability( "audio output", 250 );
57     set_callbacks( Open, Close );
58 vlc_module_end();
59
60 /*****************************************************************************
61  * aout_sys_t: waveOut audio output method descriptor
62  *****************************************************************************
63  * This structure is part of the audio output thread descriptor.
64  * It describes the waveOut specific properties of an audio device.
65  *****************************************************************************/
66
67 #define NUMBUF 3           /* We use triple buffering to be on the safe side */
68
69 struct aout_sys_t
70 {
71     HWAVEOUT h_waveout;                        /* handle to waveout instance */
72
73     WAVEFORMATEX waveformat;                                 /* Audio format */
74
75     WAVEHDR waveheader[NUMBUF];
76
77     int i_current_buffer;
78
79     DWORD dw_counter;              /* Number of bytes played since beginning */
80 };
81
82 /*****************************************************************************
83  * Open: open the audio device
84  *****************************************************************************
85  * This function opens and setups Win32 waveOut
86  *****************************************************************************/
87 static int Open( vlc_object_t *p_this )
88 {   
89     aout_thread_t *p_aout = (aout_thread_t *)p_this;
90     int i;
91
92     /* Allocate structure */
93     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
94
95     if( p_aout->p_sys == NULL )
96     {
97         msg_Err( p_aout, "out of memory" );
98         return( 1 );
99     }
100
101     p_aout->pf_setformat = SetFormat;
102     p_aout->pf_getbufinfo = GetBufInfo;
103     p_aout->pf_play = Play;
104
105     /* Initialize some variables */
106     p_aout->p_sys->i_current_buffer = 0;
107     for( i=0; i<NUMBUF; i++)
108         p_aout->p_sys->waveheader[i].lpData = malloc( 1 );
109
110     return OpenWaveOutDevice( p_aout );
111
112 }
113
114 /*****************************************************************************
115  * SetFormat: reset the audio device and sets its format
116  *****************************************************************************
117  * This functions set a new audio format.
118  * For this we need to close the current device and create another
119  * one with the desired format.
120  *****************************************************************************/
121 static int SetFormat( aout_thread_t *p_aout )
122 {
123     msg_Dbg( p_aout, "SetFormat" );
124
125     /* Check if the format has changed */
126
127     if( (p_aout->p_sys->waveformat.nChannels != p_aout->i_channels) ||
128         (p_aout->p_sys->waveformat.nSamplesPerSec != p_aout->i_rate) )
129     {
130         /* Before calling waveOutClose we must reset the device */
131         waveOutReset( p_aout->p_sys->h_waveout );
132
133         if( waveOutClose( p_aout->p_sys->h_waveout ) != MMSYSERR_NOERROR )
134         {
135             msg_Err( p_aout, "waveOutClose failed" );
136         }
137
138         return OpenWaveOutDevice( p_aout );
139     }
140
141     return 0;
142 }
143
144 /*****************************************************************************
145  * GetBufInfo: buffer status query
146  *****************************************************************************
147  * returns the number of bytes in the audio buffer that have not yet been
148  * sent to the sound device.
149  *****************************************************************************/
150 static int GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
151 {
152     MMTIME mmtime;
153
154     mmtime.wType = TIME_BYTES;
155     if( (waveOutGetPosition(p_aout->p_sys->h_waveout, &mmtime, sizeof(MMTIME)))
156         != MMSYSERR_NOERROR || (mmtime.wType != TIME_BYTES) )
157     {
158         msg_Warn( p_aout, "waveOutGetPosition failed" );
159         return i_buffer_limit;
160     }
161
162
163 #if 0
164     msg_Dbg( p_aout, "GetBufInfo: %i",
165                       p_aout->p_sys->dw_counter - mmtime.u.cb );
166 #endif
167
168     return (p_aout->p_sys->dw_counter - mmtime.u.cb);
169 }
170
171 /*****************************************************************************
172  * Play: play a sound buffer
173  *****************************************************************************
174  * This function writes a buffer of i_length bytes
175  *****************************************************************************/
176 static void Play( aout_thread_t *p_aout, byte_t *p_buffer, int i_size )
177 {
178     MMRESULT result;
179     int current_buffer = p_aout->p_sys->i_current_buffer;
180
181     p_aout->p_sys->i_current_buffer = (current_buffer + 1) % NUMBUF;
182
183     /* Unprepare the old buffer */
184     waveOutUnprepareHeader( p_aout->p_sys->h_waveout,
185                             &p_aout->p_sys->waveheader[current_buffer],
186                             sizeof(WAVEHDR) );
187
188     /* Prepare the buffer */
189     p_aout->p_sys->waveheader[current_buffer].lpData =
190         realloc( p_aout->p_sys->waveheader[current_buffer].lpData, i_size );
191     if( !p_aout->p_sys->waveheader[current_buffer].lpData )
192     {
193         msg_Err( p_aout, "could not allocate buffer" );
194         return;
195     }
196     p_aout->p_sys->waveheader[current_buffer].dwBufferLength = i_size;
197     p_aout->p_sys->waveheader[current_buffer].dwFlags = 0;
198
199     result = waveOutPrepareHeader( p_aout->p_sys->h_waveout,
200                                    &p_aout->p_sys->waveheader[current_buffer],
201                                    sizeof(WAVEHDR) );
202     if( result != MMSYSERR_NOERROR )
203     {
204         msg_Err( p_aout, "waveOutPrepareHeader failed" );
205         return;
206     }
207
208     /* Send the buffer the waveOut queue */
209     p_aout->p_vlc->pf_memcpy( p_aout->p_sys->waveheader[current_buffer].lpData,
210                               p_buffer, i_size );
211     result = waveOutWrite( p_aout->p_sys->h_waveout,
212                            &p_aout->p_sys->waveheader[current_buffer],
213                            sizeof(WAVEHDR) );
214     if( result != MMSYSERR_NOERROR )
215     {
216         msg_Err( p_aout, "waveOutWrite failed" );
217         return;
218     }
219
220     /* keep track of number of bytes played */
221     p_aout->p_sys->dw_counter += i_size;
222
223 }
224
225 /*****************************************************************************
226  * Close: close the audio device
227  *****************************************************************************/
228 static void Close( vlc_object_t *p_this )
229 {       
230     aout_thread_t *p_aout = (aout_thread_t *)p_this;
231     int i;
232
233     /* Before calling waveOutClose we must reset the device */
234     waveOutReset( p_aout->p_sys->h_waveout );
235
236     /* Close the device */
237     if( waveOutClose( p_aout->p_sys->h_waveout ) != MMSYSERR_NOERROR )
238     {
239         msg_Err( p_aout, "waveOutClose failed" );
240     }
241
242     /* Deallocate memory */
243     for( i=0; i<NUMBUF; i++ )
244         free( p_aout->p_sys->waveheader[i].lpData );
245
246     if( p_aout->p_sys != NULL )
247     { 
248         free( p_aout->p_sys );
249         p_aout->p_sys = NULL;
250     }
251 }
252
253 /*****************************************************************************
254  * OpenWaveOutDevice: open the sound device
255  ****************************************************************************/
256 static int OpenWaveOutDevice( aout_thread_t *p_aout )
257 {
258     MMRESULT result;
259
260     /* initialize played bytes counter */
261     p_aout->p_sys->dw_counter = 0;
262
263     /* Set sound format */
264     p_aout->p_sys->waveformat.wFormatTag       = WAVE_FORMAT_PCM;
265     p_aout->p_sys->waveformat.nChannels        = p_aout->i_channels;
266     p_aout->p_sys->waveformat.nSamplesPerSec   = p_aout->i_rate;
267     p_aout->p_sys->waveformat.wBitsPerSample   = 16;
268     p_aout->p_sys->waveformat.nBlockAlign      =
269         p_aout->p_sys->waveformat.wBitsPerSample / 8 * p_aout->i_channels;
270     p_aout->p_sys->waveformat.nAvgBytesPerSec  =
271         p_aout->p_sys->waveformat.nSamplesPerSec *
272             p_aout->p_sys->waveformat.nBlockAlign;
273
274
275     /* Open the device */
276     result = waveOutOpen( &p_aout->p_sys->h_waveout, WAVE_MAPPER,
277                           &p_aout->p_sys->waveformat,
278                           0 /*callback*/, 0 /*callback data*/, CALLBACK_NULL );
279     if( result != MMSYSERR_NOERROR )
280     {
281         msg_Err( p_aout, "waveOutOpen failed" );
282         return( 1 );
283     }
284
285     return( 0 );
286 }