4 using System.Runtime.InteropServices;
8 /* these are the values used by alsa */
48 protected uint chunk_size;
50 static AudioDevice TryAlsa (string name) {
56 dev = new AlsaDevice (name);
64 public static AudioDevice CreateDevice (string name) {
68 /* if no option is found, return a silent device */
70 dev = new AudioDevice ();
74 public virtual bool SetFormat (AudioFormat format, int channels, int rate) {
78 public virtual int PlaySample (byte[] buffer, int num_frames) {
82 public virtual int XRunRecovery (int err) {
86 public virtual void Wait () {
89 public uint ChunkSize {
90 get { return chunk_size; }
95 class AlsaDevice: AudioDevice, IDisposable {
100 [DllImport ("libasound")]
101 static extern int snd_pcm_open (ref IntPtr handle, string pcm_name, int stream, int mode);
103 [DllImport ("libasound")]
104 static extern int snd_pcm_close (IntPtr handle);
106 [DllImport ("libasound")]
107 static extern int snd_pcm_drain (IntPtr handle);
109 [DllImport ("libasound")]
110 static extern int snd_pcm_writei (IntPtr handle, byte[] buf, int size);
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);
115 [DllImport ("libasound")]
116 static extern int snd_pcm_state (IntPtr handle);
118 [DllImport ("libasound")]
119 static extern int snd_pcm_prepare (IntPtr handle);
121 [DllImport ("libasound")]
122 static extern int snd_pcm_hw_params (IntPtr handle, IntPtr param);
124 [DllImport ("libasound")]
125 static extern int snd_pcm_hw_params_malloc (ref IntPtr param);
127 [DllImport ("libasound")]
128 static extern void snd_pcm_hw_params_free (IntPtr param);
130 [DllImport ("libasound")]
131 static extern int snd_pcm_hw_params_any (IntPtr handle, IntPtr param);
133 [DllImport ("libasound")]
134 static extern int snd_pcm_hw_params_set_access (IntPtr handle, IntPtr param, int access);
136 [DllImport ("libasound")]
137 static extern int snd_pcm_hw_params_set_format (IntPtr handle, IntPtr param, int format);
139 [DllImport ("libasound")]
140 static extern int snd_pcm_hw_params_set_channels (IntPtr handle, IntPtr param, uint channel);
142 [DllImport ("libasound")]
143 static extern int snd_pcm_hw_params_set_rate_near (IntPtr handle, IntPtr param, ref uint rate, ref int dir);
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);
148 [DllImport ("libasound")]
149 static extern int snd_pcm_hw_params_get_period_size (IntPtr param, ref uint period, ref int dir);
151 [DllImport ("libasound")]
152 static extern int snd_pcm_hw_params_set_buffer_size_near (IntPtr handle, IntPtr param, ref uint buff_size);
154 [DllImport ("libasound")]
155 static extern int snd_pcm_hw_params_get_buffer_time_max(IntPtr param, ref uint buffer_time, ref int dir);
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);
160 [DllImport ("libasound")]
161 static extern int snd_pcm_hw_params_get_buffer_size(IntPtr param, ref uint BufferSize);
163 [DllImport ("libasound")]
164 static extern int snd_pcm_sw_params (IntPtr handle, IntPtr param);
166 [DllImport ("libasound")]
167 static extern int snd_pcm_sw_params_malloc (ref IntPtr param);
169 [DllImport ("libasound")]
170 static extern void snd_pcm_sw_params_free (IntPtr param);
172 [DllImport ("libasound")]
173 static extern int snd_pcm_sw_params_current(IntPtr handle, IntPtr param);
175 [DllImport ("libasound")]
176 static extern int snd_pcm_sw_params_set_avail_min(IntPtr handle, IntPtr param, uint frames);
178 [DllImport ("libasound")]
179 static extern int snd_pcm_sw_params_set_start_threshold(IntPtr handle, IntPtr param, uint StartThreshold);
181 public AlsaDevice (string name) {
184 int err = snd_pcm_open (ref handle, name, 0, 0);
186 throw new Exception ("no open " + err);
193 public void Dispose () {
195 GC.SuppressFinalize (this);
198 protected virtual void Dispose (bool disposing) {
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;
213 public override bool SetFormat (AudioFormat format, int channels, int rate) {
215 uint period_time = 0;
216 uint period_size = 0;
217 uint buffer_size = 0;
218 uint buffer_time = 0;
220 uint sampling_rate = (uint)rate;
222 // Alloc hw params structure
223 alsa_err = snd_pcm_hw_params_malloc (ref hw_param);
226 // get current hardware param
227 snd_pcm_hw_params_any (handle, hw_param);
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);
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);
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
248 period_time = buffer_time / 4;
251 snd_pcm_hw_params_set_period_time_near (handle, hw_param, ref period_time, ref dir);
254 snd_pcm_hw_params_set_buffer_time_near (handle, hw_param, ref buffer_time, ref dir);
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;
262 snd_pcm_hw_params_get_buffer_size (hw_param, ref buffer_size);
264 // Apply hardware params
265 snd_pcm_hw_params (handle, hw_param);
269 Console.WriteLine ("failed to alloc Alsa hw param struct");
272 alsa_err = snd_pcm_sw_params_malloc (ref sw_param);
274 // get current software param
275 snd_pcm_sw_params_current (handle, sw_param);
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);
282 // apply software param
283 snd_pcm_sw_params(handle, sw_param);
285 Console.WriteLine ("failed to alloc Alsa sw param struct");
288 if (hw_param != IntPtr.Zero) {
289 snd_pcm_hw_params_free (hw_param); // free hw params
290 hw_param = IntPtr.Zero;
292 if (sw_param != IntPtr.Zero) {
293 snd_pcm_sw_params_free (sw_param); // free sw params
294 sw_param = IntPtr.Zero;
297 return alsa_err == 0;
300 public override int PlaySample (byte[] buffer, int num_frames) {
304 frames = snd_pcm_writei (handle, buffer, num_frames);
306 XRunRecovery(frames);
312 public override int XRunRecovery (int err)
316 // when alsa ring buffer UnderRun, snd_pcm_writei return -EPIPE (-32)
318 alsa_err = snd_pcm_prepare (handle);
323 public override void Wait () {
324 snd_pcm_drain (handle);