1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
|
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Mono.Audio {
/* these are the values used by alsa */
#if PUBLIC_API
public
#else
internal
#endif
enum AudioFormat {
S8,
U8,
S16_LE,
S16_BE,
U16_LE,
U16_BE,
S24_LE,
S24_BE,
U24_LE,
U24_BE,
S32_LE,
S32_BE,
U32_LE,
U32_BE,
FLOAT_LE,
FLOAT_BE,
FLOAT64_LE,
FLOAT64_BE,
IEC958_SUBFRAME_LE,
IEC958_SUBFRAME_BE,
MU_LAW,
A_LAW,
IMA_ADPCM,
MPEG,
GSM
}
#if PUBLIC_API
public
#else
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);
return dev;
} catch {
return null;
}
#endif
}
public static AudioDevice CreateDevice (string name) {
AudioDevice dev;
dev = TryAlsa (name);
/* if no option is found, return a silent device */
if (dev == null)
dev = new AudioDevice ();
return dev;
}
public virtual bool SetFormat (AudioFormat format, int channels, int rate) {
return true;
}
public virtual int PlaySample (byte[] buffer, int num_frames) {
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;
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")]
static extern int snd_pcm_close (IntPtr handle);
[DllImport ("libasound")]
static extern int snd_pcm_drain (IntPtr handle);
[DllImport ("libasound")]
static extern int snd_pcm_writei (IntPtr handle, byte[] buf, int size);
[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";
int err = snd_pcm_open (ref handle, name, 0, 0);
if (err < 0)
throw new Exception ("no open " + err);
}
~AlsaDevice () {
Dispose (false);
}
public void Dispose () {
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing) {
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 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;
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
}
|