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 TryAlsa(string name)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 CreateDevice(string name)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 SetFormat(AudioFormat format, int channels, int rate)74 public virtual bool SetFormat (AudioFormat format, int channels, int rate) { 75 return true; 76 } 77 PlaySample(byte[] buffer, int num_frames)78 public virtual int PlaySample (byte[] buffer, int num_frames) { 79 return num_frames; 80 } 81 XRunRecovery(int err)82 public virtual int XRunRecovery (int err) { 83 return err; 84 } 85 Wait()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")] snd_pcm_open(ref IntPtr handle, string pcm_name, int stream, int mode)101 static extern int snd_pcm_open (ref IntPtr handle, string pcm_name, int stream, int mode); 102 103 [DllImport ("libasound")] snd_pcm_close(IntPtr handle)104 static extern int snd_pcm_close (IntPtr handle); 105 106 [DllImport ("libasound")] snd_pcm_drain(IntPtr handle)107 static extern int snd_pcm_drain (IntPtr handle); 108 109 [DllImport ("libasound")] snd_pcm_writei(IntPtr handle, byte[] buf, int size)110 static extern int snd_pcm_writei (IntPtr handle, byte[] buf, int size); 111 112 [DllImport ("libasound")] snd_pcm_set_params(IntPtr handle, int format, int access, int channels, int rate, int soft_resample, int latency)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")] snd_pcm_state(IntPtr handle)116 static extern int snd_pcm_state (IntPtr handle); 117 118 [DllImport ("libasound")] snd_pcm_prepare(IntPtr handle)119 static extern int snd_pcm_prepare (IntPtr handle); 120 121 [DllImport ("libasound")] snd_pcm_hw_params(IntPtr handle, IntPtr param)122 static extern int snd_pcm_hw_params (IntPtr handle, IntPtr param); 123 124 [DllImport ("libasound")] snd_pcm_hw_params_malloc(ref IntPtr param)125 static extern int snd_pcm_hw_params_malloc (ref IntPtr param); 126 127 [DllImport ("libasound")] snd_pcm_hw_params_free(IntPtr param)128 static extern void snd_pcm_hw_params_free (IntPtr param); 129 130 [DllImport ("libasound")] snd_pcm_hw_params_any(IntPtr handle, IntPtr param)131 static extern int snd_pcm_hw_params_any (IntPtr handle, IntPtr param); 132 133 [DllImport ("libasound")] snd_pcm_hw_params_set_access(IntPtr handle, IntPtr param, int access)134 static extern int snd_pcm_hw_params_set_access (IntPtr handle, IntPtr param, int access); 135 136 [DllImport ("libasound")] snd_pcm_hw_params_set_format(IntPtr handle, IntPtr param, int format)137 static extern int snd_pcm_hw_params_set_format (IntPtr handle, IntPtr param, int format); 138 139 [DllImport ("libasound")] snd_pcm_hw_params_set_channels(IntPtr handle, IntPtr param, uint channel)140 static extern int snd_pcm_hw_params_set_channels (IntPtr handle, IntPtr param, uint channel); 141 142 [DllImport ("libasound")] snd_pcm_hw_params_set_rate_near(IntPtr handle, IntPtr param, ref uint rate, ref int dir)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")] snd_pcm_hw_params_set_period_time_near(IntPtr handle, IntPtr param, ref uint period, ref int dir)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")] snd_pcm_hw_params_get_period_size(IntPtr param, ref uint period, ref int dir)149 static extern int snd_pcm_hw_params_get_period_size (IntPtr param, ref uint period, ref int dir); 150 151 [DllImport ("libasound")] snd_pcm_hw_params_set_buffer_size_near(IntPtr handle, IntPtr param, ref uint buff_size)152 static extern int snd_pcm_hw_params_set_buffer_size_near (IntPtr handle, IntPtr param, ref uint buff_size); 153 154 [DllImport ("libasound")] snd_pcm_hw_params_get_buffer_time_max(IntPtr param, ref uint buffer_time, ref int dir)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")] snd_pcm_hw_params_set_buffer_time_near(IntPtr handle, IntPtr param, ref uint BufferTime, ref int dir)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")] snd_pcm_hw_params_get_buffer_size(IntPtr param, ref uint BufferSize)161 static extern int snd_pcm_hw_params_get_buffer_size(IntPtr param, ref uint BufferSize); 162 163 [DllImport ("libasound")] snd_pcm_sw_params(IntPtr handle, IntPtr param)164 static extern int snd_pcm_sw_params (IntPtr handle, IntPtr param); 165 166 [DllImport ("libasound")] snd_pcm_sw_params_malloc(ref IntPtr param)167 static extern int snd_pcm_sw_params_malloc (ref IntPtr param); 168 169 [DllImport ("libasound")] snd_pcm_sw_params_free(IntPtr param)170 static extern void snd_pcm_sw_params_free (IntPtr param); 171 172 [DllImport ("libasound")] snd_pcm_sw_params_current(IntPtr handle, IntPtr param)173 static extern int snd_pcm_sw_params_current(IntPtr handle, IntPtr param); 174 175 [DllImport ("libasound")] snd_pcm_sw_params_set_avail_min(IntPtr handle, IntPtr param, uint frames)176 static extern int snd_pcm_sw_params_set_avail_min(IntPtr handle, IntPtr param, uint frames); 177 178 [DllImport ("libasound")] snd_pcm_sw_params_set_start_threshold(IntPtr handle, IntPtr param, uint StartThreshold)179 static extern int snd_pcm_sw_params_set_start_threshold(IntPtr handle, IntPtr param, uint StartThreshold); 180 AlsaDevice(string name)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 ~AlsaDevice()189 ~AlsaDevice () { 190 Dispose (false); 191 } 192 Dispose()193 public void Dispose () { 194 Dispose (true); 195 GC.SuppressFinalize (this); 196 } 197 Dispose(bool disposing)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 SetFormat(AudioFormat format, int channels, int rate)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 PlaySample(byte[] buffer, int num_frames)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 XRunRecovery(int err)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 Wait()323 public override void Wait () { 324 snd_pcm_drain (handle); 325 } 326 } 327 #endif 328 329 } 330 331