Xamarin Public Jenkins (auto-signing) 966bba02bb Imported Upstream version 5.2.0.175
Former-commit-id: bb0468d0f257ff100aa895eb5fe583fb5dfbf900
2017-06-07 13:16:24 +00:00

331 lines
8.6 KiB
C#

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
}