internal
#endif
class AudioDevice {
-
+ protected uint chunk_size;
+
static AudioDevice TryAlsa (string name) {
+#if XAMMAC_4_5
+ return null;
+#else
AudioDevice dev;
try {
dev = new AlsaDevice (name);
} catch {
return null;
}
+#endif
}
public static AudioDevice CreateDevice (string name) {
return num_frames;
}
+ public virtual int XRunRecovery (int err) {
+ return err;
+ }
+
public virtual void Wait () {
}
+
+ public uint ChunkSize {
+ get { return chunk_size; }
+ }
}
+#if !XAMMAC_4_5
class AlsaDevice: AudioDevice, IDisposable {
IntPtr handle;
-
- [DllImport ("libasound.so.2")]
+ IntPtr hw_param;
+ IntPtr sw_param;
+
+ [DllImport ("libasound")]
static extern int snd_pcm_open (ref IntPtr handle, string pcm_name, int stream, int mode);
- [DllImport ("libasound.so.2")]
+ [DllImport ("libasound")]
static extern int snd_pcm_close (IntPtr handle);
- [DllImport ("libasound.so.2")]
+ [DllImport ("libasound")]
static extern int snd_pcm_drain (IntPtr handle);
- [DllImport ("libasound.so.2")]
+ [DllImport ("libasound")]
static extern int snd_pcm_writei (IntPtr handle, byte[] buf, int size);
- [DllImport ("libasound.so.2")]
+ [DllImport ("libasound")]
static extern int snd_pcm_set_params (IntPtr handle, int format, int access, int channels, int rate, int soft_resample, int latency);
+ [DllImport ("libasound")]
+ static extern int snd_pcm_state (IntPtr handle);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_prepare (IntPtr handle);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params (IntPtr handle, IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_malloc (ref IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern void snd_pcm_hw_params_free (IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_any (IntPtr handle, IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_set_access (IntPtr handle, IntPtr param, int access);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_set_format (IntPtr handle, IntPtr param, int format);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_set_channels (IntPtr handle, IntPtr param, uint channel);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_set_rate_near (IntPtr handle, IntPtr param, ref uint rate, ref int dir);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_set_period_time_near (IntPtr handle, IntPtr param, ref uint period, ref int dir);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_get_period_size (IntPtr param, ref uint period, ref int dir);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_set_buffer_size_near (IntPtr handle, IntPtr param, ref uint buff_size);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_get_buffer_time_max(IntPtr param, ref uint buffer_time, ref int dir);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_set_buffer_time_near(IntPtr handle, IntPtr param, ref uint BufferTime, ref int dir);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_hw_params_get_buffer_size(IntPtr param, ref uint BufferSize);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_sw_params (IntPtr handle, IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_sw_params_malloc (ref IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern void snd_pcm_sw_params_free (IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_sw_params_current(IntPtr handle, IntPtr param);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_sw_params_set_avail_min(IntPtr handle, IntPtr param, uint frames);
+
+ [DllImport ("libasound")]
+ static extern int snd_pcm_sw_params_set_start_threshold(IntPtr handle, IntPtr param, uint StartThreshold);
+
public AlsaDevice (string name) {
if (name == null)
name = "default";
if (disposing) {
}
+ if (sw_param != IntPtr.Zero)
+ snd_pcm_sw_params_free (sw_param);
+ if (hw_param != IntPtr.Zero)
+ snd_pcm_hw_params_free (hw_param);
if (handle != IntPtr.Zero)
snd_pcm_close (handle);
+ sw_param = IntPtr.Zero;
+ hw_param = IntPtr.Zero;
handle = IntPtr.Zero;
}
public override bool SetFormat (AudioFormat format, int channels, int rate) {
- int err = snd_pcm_set_params (handle, (int)format, 3, channels, rate, 1, 500000);
- return err == 0;
+ int alsa_err = -1;
+ uint period_time = 0;
+ uint period_size = 0;
+ uint buffer_size = 0;
+ uint buffer_time = 0;
+ int dir = 0;
+ uint sampling_rate = (uint)rate;
+
+ // Alloc hw params structure
+ alsa_err = snd_pcm_hw_params_malloc (ref hw_param);
+
+ if (alsa_err == 0) {
+ // get current hardware param
+ snd_pcm_hw_params_any (handle, hw_param);
+
+ // Set access to SND_PCM_ACCESS_RW_INTERLEAVED
+ snd_pcm_hw_params_set_access (handle, hw_param, 3);
+ // Set format to the file's format
+ snd_pcm_hw_params_set_format (handle, hw_param, (int)format);
+ // Set channel to the file's channel number
+ snd_pcm_hw_params_set_channels (handle, hw_param, (uint)channels);
+
+ dir = 0;
+ // Set the sampling rate to the closest value
+ snd_pcm_hw_params_set_rate_near (handle, hw_param, ref sampling_rate, ref dir);
+
+ dir = 0;
+ // Get the maximum buffer time allowed by hardware
+ snd_pcm_hw_params_get_buffer_time_max (hw_param, ref buffer_time, ref dir);
+ // At least, max buffer time = 500ms
+ if (buffer_time > 500000)
+ buffer_time = 500000;
+ // The optimum time for a period is the quarter of the buffer time
+ if (buffer_time > 0)
+ period_time = buffer_time / 4;
+
+ dir = 0;
+ snd_pcm_hw_params_set_period_time_near (handle, hw_param, ref period_time, ref dir);
+
+ dir = 0;
+ snd_pcm_hw_params_set_buffer_time_near (handle, hw_param, ref buffer_time, ref dir);
+
+ // Get the period size in byte
+ snd_pcm_hw_params_get_period_size (hw_param, ref period_size, ref dir);
+ // Set the chunk size to the periode size
+ // a chunk is a piece of wave raw data send to alsa, data are played chunk by chunk !
+ chunk_size = period_size;
+
+ snd_pcm_hw_params_get_buffer_size (hw_param, ref buffer_size);
+
+ // Apply hardware params
+ snd_pcm_hw_params (handle, hw_param);
+
+
+ } else {
+ Console.WriteLine ("failed to alloc Alsa hw param struct");
+ }
+
+ alsa_err = snd_pcm_sw_params_malloc (ref sw_param);
+ if (alsa_err == 0) {
+ // get current software param
+ snd_pcm_sw_params_current (handle, sw_param);
+
+ // Alsa becomes ready when there is at least chunk_size bytes (i.e. period) in its ring buffer !
+ snd_pcm_sw_params_set_avail_min(handle, sw_param, chunk_size);
+ // Alsa starts playing when there is buffer_size (i.e. the buffer is full) bytes in its ring buffer
+ snd_pcm_sw_params_set_start_threshold(handle, sw_param, buffer_size);
+
+ // apply software param
+ snd_pcm_sw_params(handle, sw_param);
+ } else {
+ Console.WriteLine ("failed to alloc Alsa sw param struct");
+ }
+
+ if (hw_param != IntPtr.Zero) {
+ snd_pcm_hw_params_free (hw_param); // free hw params
+ hw_param = IntPtr.Zero;
+ }
+ if (sw_param != IntPtr.Zero) {
+ snd_pcm_sw_params_free (sw_param); // free sw params
+ sw_param = IntPtr.Zero;
+ }
+
+ return alsa_err == 0;
}
public override int PlaySample (byte[] buffer, int num_frames) {
- int frames = snd_pcm_writei (handle, buffer, num_frames);
+ int frames;
+
+ do {
+ frames = snd_pcm_writei (handle, buffer, num_frames);
+ if (frames < 0)
+ XRunRecovery(frames);
+ }while (frames < 0);
+
return frames;
}
+ public override int XRunRecovery (int err)
+ {
+ int alsa_err = 0;
+
+ // when alsa ring buffer UnderRun, snd_pcm_writei return -EPIPE (-32)
+ if (-32 == err) {
+ alsa_err = snd_pcm_prepare (handle);
+ }
+ return alsa_err;
+ }
+
public override void Wait () {
snd_pcm_drain (handle);
}
}
+#endif
}