Merge branch 'master' into config-checks-ipv6
[mono.git] / mcs / class / System / System.Media / AudioDevice.cs
1
2 using System;
3 using System.IO;
4 using System.Runtime.InteropServices;
5
6 namespace Mono.Audio {
7
8         /* these are the values used by alsa */
9 #if PUBLIC_API
10         public
11 #else
12         internal
13 #endif
14         enum AudioFormat {
15                 S8,
16                 U8,
17                 S16_LE,
18                 S16_BE,
19                 U16_LE,
20                 U16_BE,
21                 S24_LE,
22                 S24_BE,
23                 U24_LE,
24                 U24_BE,
25                 S32_LE,
26                 S32_BE,
27                 U32_LE,
28                 U32_BE,
29                 FLOAT_LE,
30                 FLOAT_BE,
31                 FLOAT64_LE,
32                 FLOAT64_BE,
33                 IEC958_SUBFRAME_LE,
34                 IEC958_SUBFRAME_BE,
35                 MU_LAW,
36                 A_LAW,
37                 IMA_ADPCM,
38                 MPEG,
39                 GSM
40         }
41
42 #if PUBLIC_API
43         public
44 #else
45         internal
46 #endif
47         class AudioDevice {
48                 protected uint chunk_size;
49                 
50                 static AudioDevice TryAlsa (string name) {
51 #if XAMMAC_4_5
52                         return null;
53 #else
54                         AudioDevice dev;
55                         try {
56                                 dev = new AlsaDevice (name);
57                                 return dev;
58                         } catch {
59                                 return null;
60                         }
61 #endif
62                 }
63
64                 public static AudioDevice CreateDevice (string name) {
65                         AudioDevice dev;
66
67                         dev = TryAlsa (name);
68                         /* if no option is found, return a silent device */
69                         if (dev == null)
70                                 dev = new AudioDevice ();
71                         return dev;
72                 }
73
74                 public virtual bool SetFormat (AudioFormat format, int channels, int rate) {
75                         return true;
76                 }
77
78                 public virtual int PlaySample (byte[] buffer, int num_frames) {
79                         return num_frames;
80                 }
81                 
82                 public virtual int XRunRecovery (int err) {
83                         return err;
84                 }
85                 
86                 public virtual void Wait () {
87                 }
88                 
89                 public uint ChunkSize {
90                         get { return chunk_size; }
91                 }
92         }
93
94 #if !XAMMAC_4_5
95         class AlsaDevice: AudioDevice, IDisposable {
96                 IntPtr handle;
97                 IntPtr hw_param;
98                 IntPtr sw_param;
99                 
100                 [DllImport ("libasound")]
101                 static extern int snd_pcm_open (ref IntPtr handle, string pcm_name, int stream, int mode);
102
103                 [DllImport ("libasound")]
104                 static extern int snd_pcm_close (IntPtr handle);
105
106                 [DllImport ("libasound")]
107                 static extern int snd_pcm_drain (IntPtr handle);
108
109                 [DllImport ("libasound")]
110                 static extern int snd_pcm_writei (IntPtr handle, byte[] buf, int size);
111
112                 [DllImport ("libasound")]
113                 static extern int snd_pcm_set_params (IntPtr handle, int format, int access, int channels, int rate, int soft_resample, int latency);
114
115                 [DllImport ("libasound")]
116                 static extern int snd_pcm_state (IntPtr handle);
117
118                 [DllImport ("libasound")]
119                 static extern int snd_pcm_prepare (IntPtr handle);
120
121                 [DllImport ("libasound")]
122                 static extern int snd_pcm_hw_params (IntPtr handle, IntPtr param);
123
124                 [DllImport ("libasound")]
125                 static extern int snd_pcm_hw_params_malloc (ref IntPtr param);
126
127                 [DllImport ("libasound")]
128                 static extern void snd_pcm_hw_params_free (IntPtr param);
129
130                 [DllImport ("libasound")]
131                 static extern int snd_pcm_hw_params_any (IntPtr handle, IntPtr param);
132
133                 [DllImport ("libasound")]
134                 static extern int snd_pcm_hw_params_set_access (IntPtr handle, IntPtr param, int access);
135                 
136                 [DllImport ("libasound")]
137                 static extern int snd_pcm_hw_params_set_format (IntPtr handle, IntPtr param, int format);
138
139                 [DllImport ("libasound")]
140                 static extern int snd_pcm_hw_params_set_channels (IntPtr handle, IntPtr param, uint channel);
141
142                 [DllImport ("libasound")]
143                 static extern int snd_pcm_hw_params_set_rate_near (IntPtr handle, IntPtr param, ref uint rate, ref int dir);
144
145                 [DllImport ("libasound")]
146                 static extern int snd_pcm_hw_params_set_period_time_near (IntPtr handle, IntPtr param, ref uint period, ref int dir);
147
148                 [DllImport ("libasound")]
149                 static extern int snd_pcm_hw_params_get_period_size (IntPtr param, ref uint period, ref int dir);
150
151                 [DllImport ("libasound")]
152                 static extern int snd_pcm_hw_params_set_buffer_size_near (IntPtr handle, IntPtr param, ref uint buff_size);
153
154                 [DllImport ("libasound")]
155                 static extern int snd_pcm_hw_params_get_buffer_time_max(IntPtr param, ref uint buffer_time, ref int dir);
156
157                 [DllImport ("libasound")]
158                 static extern int snd_pcm_hw_params_set_buffer_time_near(IntPtr handle, IntPtr param, ref uint BufferTime, ref int dir);
159
160                 [DllImport ("libasound")]
161                 static extern int snd_pcm_hw_params_get_buffer_size(IntPtr param, ref uint BufferSize);
162
163                 [DllImport ("libasound")]
164                 static extern int snd_pcm_sw_params (IntPtr handle, IntPtr param);
165
166                 [DllImport ("libasound")]
167                 static extern int snd_pcm_sw_params_malloc (ref IntPtr param);
168
169                 [DllImport ("libasound")]
170                 static extern void snd_pcm_sw_params_free (IntPtr param);
171
172                 [DllImport ("libasound")]
173                 static extern int snd_pcm_sw_params_current(IntPtr handle, IntPtr param);
174
175                 [DllImport ("libasound")]
176                 static extern int snd_pcm_sw_params_set_avail_min(IntPtr handle, IntPtr param, uint frames);
177
178                 [DllImport ("libasound")]
179                 static extern int snd_pcm_sw_params_set_start_threshold(IntPtr handle, IntPtr param, uint StartThreshold);
180
181                 public AlsaDevice (string name) {
182                         if (name == null)
183                                 name = "default";
184                         int err = snd_pcm_open (ref handle, name, 0, 0);
185                         if (err < 0)
186                                 throw new Exception ("no open " + err);
187                 }
188
189                 ~AlsaDevice () {
190                         Dispose (false);
191                 }
192
193                 public void Dispose () {
194                         Dispose (true);
195                         GC.SuppressFinalize (this);
196                 }
197
198                 protected virtual void Dispose (bool disposing) {
199                         if (disposing) {
200                                 
201                         }
202                         if (sw_param != IntPtr.Zero)
203                                 snd_pcm_sw_params_free (sw_param);
204                         if (hw_param != IntPtr.Zero)
205                                 snd_pcm_hw_params_free (hw_param);
206                         if (handle != IntPtr.Zero)
207                                 snd_pcm_close (handle);
208                         sw_param = IntPtr.Zero;
209                         hw_param = IntPtr.Zero;
210                         handle = IntPtr.Zero;
211                 }
212
213                 public override bool SetFormat (AudioFormat format, int channels, int rate) {
214                         int  alsa_err = -1;
215                         uint period_time = 0;
216                         uint period_size = 0;
217                         uint buffer_size = 0;
218                         uint buffer_time = 0;
219                         int dir = 0;
220                         uint sampling_rate = (uint)rate;
221
222                         // Alloc hw params structure
223                         alsa_err = snd_pcm_hw_params_malloc (ref hw_param);
224
225                         if (alsa_err == 0) {
226                                 // get current hardware param
227                                 snd_pcm_hw_params_any (handle, hw_param);
228
229                                 // Set access to SND_PCM_ACCESS_RW_INTERLEAVED
230                                 snd_pcm_hw_params_set_access (handle, hw_param, 3);
231                                 // Set format to the file's format
232                                 snd_pcm_hw_params_set_format (handle, hw_param, (int)format);
233                                 // Set channel to the file's channel number
234                                 snd_pcm_hw_params_set_channels (handle, hw_param, (uint)channels);
235
236                                 dir = 0;
237                                 // Set the sampling rate to the closest value
238                                 snd_pcm_hw_params_set_rate_near (handle, hw_param, ref sampling_rate, ref dir);
239
240                                 dir = 0;
241                                 // Get the maximum buffer time allowed by hardware
242                                 snd_pcm_hw_params_get_buffer_time_max (hw_param, ref buffer_time, ref dir);
243                                 // At least, max buffer time = 500ms
244                                 if (buffer_time > 500000) 
245                                         buffer_time = 500000;
246                                 // The optimum time for a period is the quarter of the buffer time
247                                 if (buffer_time > 0)
248                                         period_time = buffer_time / 4;
249
250                                 dir = 0;
251                                 snd_pcm_hw_params_set_period_time_near (handle, hw_param, ref period_time, ref dir);
252
253                                 dir = 0;
254                                 snd_pcm_hw_params_set_buffer_time_near (handle, hw_param, ref buffer_time, ref dir);
255
256                                 // Get the period size in byte
257                                 snd_pcm_hw_params_get_period_size (hw_param, ref period_size, ref dir);
258                                 // Set the chunk size to the periode size
259                                 // a chunk is a piece of wave raw data send to alsa, data are played chunk by chunk !
260                                 chunk_size = period_size;
261
262                                 snd_pcm_hw_params_get_buffer_size (hw_param, ref buffer_size);
263
264                                 // Apply hardware params
265                                 snd_pcm_hw_params (handle, hw_param);
266
267
268                         } else {
269                                 Console.WriteLine ("failed to alloc Alsa hw param struct");
270                         }
271
272                         alsa_err = snd_pcm_sw_params_malloc (ref sw_param);
273                         if (alsa_err == 0) {
274                                 // get current software param
275                                 snd_pcm_sw_params_current (handle, sw_param);
276
277                                 // Alsa becomes ready when there is at least chunk_size bytes (i.e. period) in its ring buffer !
278                                 snd_pcm_sw_params_set_avail_min(handle, sw_param, chunk_size);
279                                 // Alsa starts playing when there is buffer_size (i.e. the buffer is full) bytes in its ring buffer
280                                 snd_pcm_sw_params_set_start_threshold(handle, sw_param, buffer_size);
281
282                                 // apply software param
283                                 snd_pcm_sw_params(handle, sw_param);
284                         } else {
285                                 Console.WriteLine ("failed to alloc Alsa sw param struct");
286                         }
287
288                         if (hw_param != IntPtr.Zero) {
289                                 snd_pcm_hw_params_free (hw_param);  // free hw params
290                                 hw_param = IntPtr.Zero;
291                         }
292                         if (sw_param != IntPtr.Zero) {
293                                 snd_pcm_sw_params_free (sw_param);  // free sw params
294                                 sw_param = IntPtr.Zero;
295                         }
296
297                         return alsa_err == 0;
298                 }
299
300                 public override int PlaySample (byte[] buffer, int num_frames) {
301                         int frames;
302
303                         do {
304                                 frames = snd_pcm_writei (handle, buffer, num_frames);
305                                 if (frames < 0)
306                                         XRunRecovery(frames);
307                         }while (frames < 0);
308
309                         return frames;
310                 }
311                 
312                 public override int XRunRecovery (int err)
313                 {
314                         int alsa_err = 0;
315                         
316                         // when alsa ring buffer UnderRun, snd_pcm_writei return -EPIPE (-32)
317                         if (-32 == err) {
318                                 alsa_err = snd_pcm_prepare (handle);
319                         }
320                         return alsa_err;
321                 }
322                 
323                 public override void Wait () {
324                         snd_pcm_drain (handle);
325                 }
326         }
327 #endif
328
329 }
330