* Roku HD1000 audio output.
[vlc.git] / modules / audio_output / hd1000a.cpp
1 /*****************************************************************************
2  * hd1000a.cpp : Roku HD1000 audio output
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id$
6  *
7  * Author: Jon Lech Johansen <jon-vl@nanocrew.net>
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 extern "C"
28 {
29 #include <string.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35
36 #include "aout_internal.h"
37 }
38
39 #include <deschutes/libraries/hdmachinex225/PCMAudioPlayer.h>
40
41 #define FRAME_SIZE 4096
42
43 /*****************************************************************************
44  * aout_sys_t: audio output method descriptor
45  *****************************************************************************
46  * This structure is part of the audio output thread descriptor.
47  * It describes the direct sound specific properties of an audio device.
48  *****************************************************************************/
49 struct aout_sys_t
50 {
51     u32 nAlignment;
52     u32 nSizeMultiple;
53     u32 nBuffers;
54     u32 nBufferSize;
55     void ** ppBuffers;
56     u32 nNextBufferIndex;
57     PCMAudioPlayer * pPlayer;
58 };
59
60 /*****************************************************************************
61  * Local prototypes.
62  *****************************************************************************/
63 static int     Open        ( vlc_object_t * );
64 static void    Close       ( vlc_object_t * );
65
66 static void    Play        ( aout_instance_t * );
67 static int     Thread      ( aout_instance_t * );
68
69 /*****************************************************************************
70  * Module descriptor
71  *****************************************************************************/
72 vlc_module_begin();
73     set_description( N_("HD1000 audio output") );
74     set_capability( "audio output", 100 );
75     set_callbacks( Open, Close );
76 vlc_module_end();
77
78 /*****************************************************************************
79  * Open: open a dummy audio device
80  *****************************************************************************/
81 static int Open( vlc_object_t * p_this )
82 {
83     aout_instance_t * p_aout = (aout_instance_t *)p_this;
84     struct aout_sys_t * p_sys;
85     PCMAudioPlayer * pPlayer;
86     int i_volume;
87
88     /* Allocate structure */
89     p_aout->output.p_sys = p_sys =
90         (aout_sys_t *)malloc( sizeof( aout_sys_t ) );
91     if( p_aout->output.p_sys == NULL )
92     {
93         msg_Err( p_aout, "out of memory" );
94         return VLC_EGENERIC;
95     }
96
97     /* New PCMAudioPlayer */
98     p_sys->pPlayer = pPlayer = new PCMAudioPlayer();
99     if( p_sys->pPlayer == NULL )
100     {
101         msg_Err( p_aout, "out of memory" );
102         free( p_sys );
103         return VLC_EGENERIC;
104     }
105
106     /* Get Buffer Requirements */
107     if( !pPlayer->GetBufferRequirements( p_sys->nAlignment,
108                                          p_sys->nSizeMultiple,
109                                          p_sys->nBuffers ) )
110     {
111         msg_Err( p_aout, "GetBufferRequirements failed" );
112         delete pPlayer;
113         free( p_sys );
114         return VLC_EGENERIC;
115     } 
116
117     p_sys->nBuffers = __MIN( p_sys->nBuffers, 4 );
118
119     p_sys->ppBuffers = (void **)malloc( p_sys->nBuffers * sizeof( void * ) );
120     if( p_sys->ppBuffers == NULL )
121     {
122         msg_Err( p_aout, "out of memory" );
123         delete pPlayer;
124         free( p_sys );
125         return VLC_EGENERIC;
126     }
127
128     /* Open PCMAudioPlayer */
129     p_sys->nBufferSize = FRAME_SIZE * 4;
130     if( !pPlayer->Open( p_sys->nBuffers, p_sys->nBufferSize,
131                         p_sys->ppBuffers ) )
132     {
133         msg_Err( p_aout, "Open failed" );
134         delete pPlayer;
135         free( p_sys->ppBuffers );
136         free( p_sys );
137         return VLC_EGENERIC;
138     }
139
140     p_sys->nNextBufferIndex = 0;
141
142     if( !pPlayer->SetSampleRate( p_aout->output.output.i_rate ) )
143     {
144         p_aout->output.output.i_rate = 44100;
145         if( !pPlayer->SetSampleRate( p_aout->output.output.i_rate ) )
146         {
147             msg_Err( p_aout, "SetSampleRate failed" );
148             pPlayer->Close();
149             delete pPlayer;
150             free( p_sys->ppBuffers );
151             free( p_sys );
152             return VLC_EGENERIC;
153         }
154     }
155
156     p_aout->output.output.i_format = AOUT_FMT_S16_NE;
157     p_aout->output.i_nb_samples = FRAME_SIZE;
158     p_aout->output.output.i_physical_channels
159             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
160     p_aout->output.pf_play = Play;
161     aout_VolumeSoftInit( p_aout );
162
163     i_volume = config_GetInt( p_aout->p_vlc, "volume" );
164     pPlayer->SetVolume( (u32)__MIN( i_volume * 64, 0xFFFF ) );
165
166     /* Create thread and wait for its readiness. */
167     if( vlc_thread_create( p_aout, "aout", Thread,
168                            VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
169     {
170         msg_Err( p_aout, "cannot create OSS thread (%s)", strerror(errno) );
171         pPlayer->Close();
172         delete pPlayer;
173         free( p_sys->ppBuffers );
174         free( p_sys );
175         return VLC_ETHREAD;
176     }
177
178     return VLC_SUCCESS;
179 }
180
181 /*****************************************************************************
182  * Close: close our file
183  *****************************************************************************/
184 static void Close( vlc_object_t * p_this )
185 {
186     u32 i;
187     aout_instance_t * p_aout = (aout_instance_t *)p_this;
188     struct aout_sys_t * p_sys = p_aout->output.p_sys;
189
190     p_aout->b_die = VLC_TRUE;
191     vlc_thread_join( p_aout );
192     p_aout->b_die = VLC_FALSE;
193
194     do
195     {
196         i = p_sys->pPlayer->WaitForBuffer();
197     } while( i != 0 && i != p_sys->nBuffers );
198
199     p_sys->pPlayer->Close();
200     delete p_sys->pPlayer;
201
202     free( p_sys->ppBuffers );
203     free( p_sys );
204 }
205
206 /*****************************************************************************
207  * Play: do nothing
208  *****************************************************************************/
209 static void Play( aout_instance_t * p_aout )
210 {
211 }
212
213 /*****************************************************************************
214  * Thread: thread used to DMA the data to the device
215  *****************************************************************************/
216 static int Thread( aout_instance_t * p_aout )
217 {
218     aout_buffer_t * p_buffer;
219     struct aout_sys_t * p_sys = p_aout->output.p_sys;
220     PCMAudioPlayer * pPlayer = p_sys->pPlayer;
221
222     while( !p_aout->b_die )
223     {
224         pPlayer->WaitForBuffer();
225
226         vlc_mutex_lock( &p_aout->output_fifo_lock );
227         p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
228         vlc_mutex_unlock( &p_aout->output_fifo_lock );
229
230 #define i p_sys->nNextBufferIndex
231         if( p_buffer == NULL )
232         {
233             p_aout->p_vlc->pf_memset( p_sys->ppBuffers[ i ], 0,
234                                       p_sys->nBufferSize ); 
235         }
236         else
237         {
238             p_aout->p_vlc->pf_memcpy( p_sys->ppBuffers[ i ],
239                                       p_buffer->p_buffer,
240                                       p_sys->nBufferSize );
241             aout_BufferFree( p_buffer );
242         }
243
244         if( !pPlayer->QueueBuffer( (s16 *)p_sys->ppBuffers[ i ],
245                                    p_sys->nBufferSize / 2 ) )
246         {
247             msg_Err( p_aout, "QueueBuffer failed" );
248         } 
249
250         i = (i + 1) % p_sys->nBuffers;
251 #undef i
252     }
253
254     return VLC_SUCCESS;
255 }