4 using System.Runtime.InteropServices;
8 /* these are the values used by alsa */
48 protected uint chunk_size;
50 static AudioDevice TryAlsa (string name) {
53 dev = new AlsaDevice (name);
60 public static AudioDevice CreateDevice (string name) {
64 /* if no option is found, return a silent device */
66 dev = new AudioDevice ();
70 public virtual bool SetFormat (AudioFormat format, int channels, int rate) {
74 public virtual int PlaySample (byte[] buffer, int num_frames) {
78 public virtual int XRunRecovery (int err) {
82 public virtual void Wait () {
85 public uint ChunkSize {
86 get { return chunk_size; }
90 class AlsaDevice: AudioDevice, IDisposable {
95 [DllImport ("libasound.so.2")]
96 static extern int snd_pcm_open (ref IntPtr handle, string pcm_name, int stream, int mode);
98 [DllImport ("libasound.so.2")]
99 static extern int snd_pcm_close (IntPtr handle);
101 [DllImport ("libasound.so.2")]
102 static extern int snd_pcm_drain (IntPtr handle);
104 [DllImport ("libasound.so.2")]
105 static extern int snd_pcm_writei (IntPtr handle, byte[] buf, int size);
107 [DllImport ("libasound.so.2")]
108 static extern int snd_pcm_set_params (IntPtr handle, int format, int access, int channels, int rate, int soft_resample, int latency);
110 [DllImport ("libasound.so.2")]
111 static extern int snd_pcm_state (IntPtr handle);
113 [DllImport ("libasound.so.2")]
114 static extern int snd_pcm_prepare (IntPtr handle);
116 [DllImport ("libasound.so.2")]
117 static extern int snd_pcm_hw_params (IntPtr handle, IntPtr param);
119 [DllImport ("libasound.so.2")]
120 static extern int snd_pcm_hw_params_malloc (ref IntPtr param);
122 [DllImport ("libasound.so.2")]
123 static extern void snd_pcm_hw_params_free (IntPtr param);
125 [DllImport ("libasound.so.2")]
126 static extern int snd_pcm_hw_params_any (IntPtr handle, IntPtr param);
128 [DllImport ("libasound.so.2")]
129 static extern int snd_pcm_hw_params_set_access (IntPtr handle, IntPtr param, int access);
131 [DllImport ("libasound.so.2")]
132 static extern int snd_pcm_hw_params_set_format (IntPtr handle, IntPtr param, int format);
134 [DllImport ("libasound.so.2")]
135 static extern int snd_pcm_hw_params_set_channels (IntPtr handle, IntPtr param, uint channel);
137 [DllImport ("libasound.so.2")]
138 static extern int snd_pcm_hw_params_set_rate_near (IntPtr handle, IntPtr param, ref uint rate, ref int dir);
140 [DllImport ("libasound.so.2")]
141 static extern int snd_pcm_hw_params_set_period_time_near (IntPtr handle, IntPtr param, ref uint period, ref int dir);
143 [DllImport ("libasound.so.2")]
144 static extern int snd_pcm_hw_params_get_period_size (IntPtr param, ref uint period, ref int dir);
146 [DllImport ("libasound.so.2")]
147 static extern int snd_pcm_hw_params_set_buffer_size_near (IntPtr handle, IntPtr param, ref uint buff_size);
149 [DllImport ("libasound.so.2")]
150 static extern int snd_pcm_hw_params_get_buffer_time_max(IntPtr param, ref uint buffer_time, ref int dir);
152 [DllImport ("libasound.so.2")]
153 static extern int snd_pcm_hw_params_set_buffer_time_near(IntPtr handle, IntPtr param, ref uint BufferTime, ref int dir);
155 [DllImport ("libasound.so.2")]
156 static extern int snd_pcm_hw_params_get_buffer_size(IntPtr param, ref uint BufferSize);
158 [DllImport ("libasound.so.2")]
159 static extern int snd_pcm_sw_params (IntPtr handle, IntPtr param);
161 [DllImport ("libasound.so.2")]
162 static extern int snd_pcm_sw_params_malloc (ref IntPtr param);
164 [DllImport ("libasound.so.2")]
165 static extern void snd_pcm_sw_params_free (IntPtr param);
167 [DllImport ("libasound.so.2")]
168 static extern int snd_pcm_sw_params_current(IntPtr handle, IntPtr param);
170 [DllImport ("libasound.so.2")]
171 static extern int snd_pcm_sw_params_set_avail_min(IntPtr handle, IntPtr param, uint frames);
173 [DllImport ("libasound.so.2")]
174 static extern int snd_pcm_sw_params_set_start_threshold(IntPtr handle, IntPtr param, uint StartThreshold);
176 public AlsaDevice (string name) {
179 int err = snd_pcm_open (ref handle, name, 0, 0);
181 throw new Exception ("no open " + err);
188 public void Dispose () {
190 GC.SuppressFinalize (this);
193 protected virtual void Dispose (bool disposing) {
197 if (sw_param != IntPtr.Zero)
198 snd_pcm_sw_params_free (sw_param);
199 if (hw_param != IntPtr.Zero)
200 snd_pcm_hw_params_free (hw_param);
201 if (handle != IntPtr.Zero)
202 snd_pcm_close (handle);
203 handle = IntPtr.Zero;
206 public override bool SetFormat (AudioFormat format, int channels, int rate) {
208 uint period_time = 0;
209 uint period_size = 0;
210 uint buffer_size = 0;
211 uint buffer_time = 0;
213 uint sampling_rate = (uint)rate;
215 // Alloc hw params structure
216 alsa_err = snd_pcm_hw_params_malloc (ref hw_param);
219 // get current hardware param
220 snd_pcm_hw_params_any (handle, hw_param);
222 // Set access to SND_PCM_ACCESS_RW_INTERLEAVED
223 snd_pcm_hw_params_set_access (handle, hw_param, 3);
224 // Set format to the file's format
225 snd_pcm_hw_params_set_format (handle, hw_param, (int)format);
226 // Set channel to the file's channel number
227 snd_pcm_hw_params_set_channels (handle, hw_param, (uint)channels);
230 // Set the sampling rate to the closest value
231 snd_pcm_hw_params_set_rate_near (handle, hw_param, ref sampling_rate, ref dir);
234 // Get the maximum buffer time allowed by hardware
235 snd_pcm_hw_params_get_buffer_time_max (hw_param, ref buffer_time, ref dir);
236 // At least, max buffer time = 500ms
237 if (buffer_time > 500000)
238 buffer_time = 500000;
239 // The optimum time for a period is the quarter of the buffer time
241 period_time = buffer_time / 4;
244 snd_pcm_hw_params_set_period_time_near (handle, hw_param, ref period_time, ref dir);
247 snd_pcm_hw_params_set_buffer_time_near (handle, hw_param, ref buffer_time, ref dir);
249 // Get the period size in byte
250 snd_pcm_hw_params_get_period_size (hw_param, ref period_size, ref dir);
251 // Set the chunk size to the periode size
252 // a chunk is a piece of wave raw data send to alsa, data are played chunk by chunk !
253 chunk_size = period_size;
255 snd_pcm_hw_params_get_buffer_size (hw_param, ref buffer_size);
257 // Apply hardware params
258 snd_pcm_hw_params (handle, hw_param);
262 Console.WriteLine ("failed to alloc Alsa hw param struct");
265 alsa_err = snd_pcm_sw_params_malloc (ref sw_param);
267 // get current software param
268 snd_pcm_sw_params_current (handle, sw_param);
270 // Alsa becomes ready when there is at least chunk_size bytes (i.e. period) in its ring buffer !
271 snd_pcm_sw_params_set_avail_min(handle, sw_param, chunk_size);
272 // Alsa starts playing when there is buffer_size (i.e. the buffer is full) bytes in its ring buffer
273 snd_pcm_sw_params_set_start_threshold(handle, sw_param, buffer_size);
275 // apply software param
276 snd_pcm_sw_params(handle, sw_param);
278 Console.WriteLine ("failed to alloc Alsa sw param struct");
281 if (hw_param != IntPtr.Zero)
282 snd_pcm_hw_params_free (hw_param); // free hw params
283 if (sw_param != IntPtr.Zero)
284 snd_pcm_sw_params_free (sw_param); // free sw params
286 return alsa_err == 0;
289 public override int PlaySample (byte[] buffer, int num_frames) {
293 frames = snd_pcm_writei (handle, buffer, num_frames);
295 XRunRecovery(frames);
301 public override int XRunRecovery (int err)
305 // when alsa ring buffer UnderRun, snd_pcm_writei return -EPIPE (-32)
307 alsa_err = snd_pcm_prepare (handle);
312 public override void Wait () {
313 snd_pcm_drain (handle);