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 sw_param = IntPtr.Zero;
204 hw_param = IntPtr.Zero;
205 handle = IntPtr.Zero;
208 public override bool SetFormat (AudioFormat format, int channels, int rate) {
210 uint period_time = 0;
211 uint period_size = 0;
212 uint buffer_size = 0;
213 uint buffer_time = 0;
215 uint sampling_rate = (uint)rate;
217 // Alloc hw params structure
218 alsa_err = snd_pcm_hw_params_malloc (ref hw_param);
221 // get current hardware param
222 snd_pcm_hw_params_any (handle, hw_param);
224 // Set access to SND_PCM_ACCESS_RW_INTERLEAVED
225 snd_pcm_hw_params_set_access (handle, hw_param, 3);
226 // Set format to the file's format
227 snd_pcm_hw_params_set_format (handle, hw_param, (int)format);
228 // Set channel to the file's channel number
229 snd_pcm_hw_params_set_channels (handle, hw_param, (uint)channels);
232 // Set the sampling rate to the closest value
233 snd_pcm_hw_params_set_rate_near (handle, hw_param, ref sampling_rate, ref dir);
236 // Get the maximum buffer time allowed by hardware
237 snd_pcm_hw_params_get_buffer_time_max (hw_param, ref buffer_time, ref dir);
238 // At least, max buffer time = 500ms
239 if (buffer_time > 500000)
240 buffer_time = 500000;
241 // The optimum time for a period is the quarter of the buffer time
243 period_time = buffer_time / 4;
246 snd_pcm_hw_params_set_period_time_near (handle, hw_param, ref period_time, ref dir);
249 snd_pcm_hw_params_set_buffer_time_near (handle, hw_param, ref buffer_time, ref dir);
251 // Get the period size in byte
252 snd_pcm_hw_params_get_period_size (hw_param, ref period_size, ref dir);
253 // Set the chunk size to the periode size
254 // a chunk is a piece of wave raw data send to alsa, data are played chunk by chunk !
255 chunk_size = period_size;
257 snd_pcm_hw_params_get_buffer_size (hw_param, ref buffer_size);
259 // Apply hardware params
260 snd_pcm_hw_params (handle, hw_param);
264 Console.WriteLine ("failed to alloc Alsa hw param struct");
267 alsa_err = snd_pcm_sw_params_malloc (ref sw_param);
269 // get current software param
270 snd_pcm_sw_params_current (handle, sw_param);
272 // Alsa becomes ready when there is at least chunk_size bytes (i.e. period) in its ring buffer !
273 snd_pcm_sw_params_set_avail_min(handle, sw_param, chunk_size);
274 // Alsa starts playing when there is buffer_size (i.e. the buffer is full) bytes in its ring buffer
275 snd_pcm_sw_params_set_start_threshold(handle, sw_param, buffer_size);
277 // apply software param
278 snd_pcm_sw_params(handle, sw_param);
280 Console.WriteLine ("failed to alloc Alsa sw param struct");
283 if (hw_param != IntPtr.Zero) {
284 snd_pcm_hw_params_free (hw_param); // free hw params
285 hw_param = IntPtr.Zero;
287 if (sw_param != IntPtr.Zero) {
288 snd_pcm_sw_params_free (sw_param); // free sw params
289 sw_param = IntPtr.Zero;
292 return alsa_err == 0;
295 public override int PlaySample (byte[] buffer, int num_frames) {
299 frames = snd_pcm_writei (handle, buffer, num_frames);
301 XRunRecovery(frames);
307 public override int XRunRecovery (int err)
311 // when alsa ring buffer UnderRun, snd_pcm_writei return -EPIPE (-32)
313 alsa_err = snd_pcm_prepare (handle);
318 public override void Wait () {
319 snd_pcm_drain (handle);