Skip to content

Instantly share code, notes, and snippets.

@ForeverZer0
Created January 25, 2020 20:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ForeverZer0/1d66f9c4660fe106475f0be9e7078657 to your computer and use it in GitHub Desktop.
Save ForeverZer0/1d66f9c4660fe106475f0be9e7078657 to your computer and use it in GitHub Desktop.
Minimal bindings for the libsndfile library.
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
namespace SndFile
{
[SuppressUnmanagedCodeSecurity]
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "IdentifierTypo")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public static class libsndfile
{
private const string LIBRARY = "sndfile";
public delegate sf_count_t sf_vio_get_filelen(IntPtr user);
public delegate sf_count_t sf_vio_seek(sf_count_t offset, SeekOrigin whence, IntPtr user);
public delegate sf_count_t sf_vio_read(IntPtr ptr, sf_count_t count, IntPtr user);
public delegate sf_count_t sf_vio_write(IntPtr ptr, sf_count_t count, IntPtr user);
public delegate sf_count_t sf_vio_tell(IntPtr user);
[StructLayout(LayoutKind.Sequential)]
public struct sf_count_t
{
private readonly UIntPtr value;
public sf_count_t(int value)
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "Size must be 0 or greater.");
this.value = new UIntPtr((uint) value);
}
public sf_count_t(long value)
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "Size must be 0 or greater.");
this.value = new UIntPtr((ulong) value);
}
[CLSCompliant(false)]
public sf_count_t(uint value)
{
this.value = new UIntPtr(value);
}
[CLSCompliant(false)]
public sf_count_t(ulong value)
{
this.value = new UIntPtr(value);
}
public long Value => Convert.ToInt64(value.ToUInt64());
}
public struct SF_VIRTUAL_IO
{
sf_vio_get_filelen get_filelen;
sf_vio_seek seek;
sf_vio_read read;
sf_vio_write write;
sf_vio_tell tell;
}
[StructLayout(LayoutKind.Sequential)]
public struct SF_INFO
{
public readonly sf_count_t frames;
public readonly int samplerate;
public readonly int channels;
public readonly int format;
public readonly int sections;
public readonly int seekable;
}
/// <summary>
/// Opaque type used as a handle to a native audio stream.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct SNDFILE
{
/// <summary>
/// The native handle, used to enforce pointer size for the structure to use without ref
/// </summary>
private readonly IntPtr handle;
}
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
private static extern SNDFILE sf_open(byte[] path, FileAccess mode, out SF_INFO info); // TODO: Mode
public static SNDFILE sf_open(string path, FileAccess mode, out SF_INFO info)
{
var bytes = Encoding.UTF8.GetBytes(path);
return sf_open(bytes, mode, out info);
}
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern SNDFILE sf_open_virtual(SF_VIRTUAL_IO io, FileAccess mode, out SF_INFO info, IntPtr user);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool sf_format_check(ref SF_INFO info);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_seek(SNDFILE sndfile, sf_count_t frames, SeekOrigin whence);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern SF_ERROR sf_error(SNDFILE sndfile); // TODO: Error Code
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern int sf_close(SNDFILE sndfile);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sf_strerror")]
private static extern IntPtr sf_strerror_ptr(SNDFILE sndfile);
public static string sf_strerror(SNDFILE sndfile) => Marshal.PtrToStringUTF8(sf_strerror_ptr(sndfile));
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sf_error_number")]
private static extern IntPtr sf_error_number_ptr(int error);
public static string sf_error_number(SF_ERROR error) =>
Marshal.PtrToStringUTF8(sf_error_number_ptr((int) error));
public static string sf_error_number(int error) => Marshal.PtrToStringUTF8(sf_error_number_ptr(error));
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern void sf_write_sync(SNDFILE sndfile);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_read_short(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_read_int(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_read_float(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_read_double(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_readf_short(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_readf_int(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_readf_float(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_readf_double(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_write_short(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_write_int(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_write_float(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_write_double(SNDFILE sndfile, IntPtr ptr, sf_count_t items);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_writef_short(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_writef_int(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_writef_float(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_writef_double(SNDFILE sndfile, IntPtr ptr, sf_count_t frames);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_read_raw(SNDFILE sndfile, IntPtr ptr, sf_count_t bytes);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern sf_count_t sf_write_raw(SNDFILE sndfile, IntPtr ptr, sf_count_t bytes);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sf_get_string")]
private static extern IntPtr sf_get_string_ptr(SNDFILE sndfile, int type);
public static string sf_get_string(SNDFILE sndfile, int type) =>
Marshal.PtrToStringUTF8(sf_get_string_ptr(sndfile, type));
public static string sf_get_string(SNDFILE sndfile, SF_STR type) =>
Marshal.PtrToStringUTF8(sf_get_string_ptr(sndfile, (int) type));
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sf_set_string")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool sf_set_string_ptr(SNDFILE file, int type, byte[] str);
public static bool sf_set_string(SNDFILE file, int type, string str)
{
var bytes = Encoding.UTF8.GetBytes(str);
return sf_set_string_ptr(file, type, bytes);
}
public static bool sf_set_string(SNDFILE file, SF_STR type, string str) => sf_set_string(file, (int) type, str);
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
public static extern int sf_command(SNDFILE sndfile, int cmd, IntPtr data, int datasize);
public enum SFC_COMMAND
{
SFC_GET_LIB_VERSION = 0x1000,
SFC_GET_LOG_INFO = 0x1001,
SFC_GET_CURRENT_SF_INFO = 0x1002,
SFC_GET_NORM_DOUBLE = 0x1010,
SFC_GET_NORM_FLOAT = 0x1011,
SFC_SET_NORM_DOUBLE = 0x1012,
SFC_SET_NORM_FLOAT = 0x1013,
SFC_SET_SCALE_FLOAT_INT_READ = 0x1014,
SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015,
SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020,
SFC_GET_SIMPLE_FORMAT = 0x1021,
SFC_GET_FORMAT_INFO = 0x1028,
SFC_GET_FORMAT_MAJOR_COUNT = 0x1030,
SFC_GET_FORMAT_MAJOR = 0x1031,
SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032,
SFC_GET_FORMAT_SUBTYPE = 0x1033,
SFC_CALC_SIGNAL_MAX = 0x1040,
SFC_CALC_NORM_SIGNAL_MAX = 0x1041,
SFC_CALC_MAX_ALL_CHANNELS = 0x1042,
SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043,
SFC_GET_SIGNAL_MAX = 0x1044,
SFC_GET_MAX_ALL_CHANNELS = 0x1045,
SFC_SET_ADD_PEAK_CHUNK = 0x1050,
SFC_UPDATE_HEADER_NOW = 0x1060,
SFC_SET_UPDATE_HEADER_AUTO = 0x1061,
SFC_FILE_TRUNCATE = 0x1080,
SFC_SET_RAW_START_OFFSET = 0x1090,
SFC_SET_DITHER_ON_WRITE = 0x10A0,
SFC_SET_DITHER_ON_READ = 0x10A1,
SFC_GET_DITHER_INFO_COUNT = 0x10A2,
SFC_GET_DITHER_INFO = 0x10A3,
SFC_GET_EMBED_FILE_INFO = 0x10B0,
SFC_SET_CLIPPING = 0x10C0,
SFC_GET_CLIPPING = 0x10C1,
SFC_GET_CUE_COUNT = 0x10CD,
SFC_GET_CUE = 0x10CE,
SFC_SET_CUE = 0x10CF,
SFC_GET_INSTRUMENT = 0x10D0,
SFC_SET_INSTRUMENT = 0x10D1,
SFC_GET_LOOP_INFO = 0x10E0,
SFC_GET_BROADCAST_INFO = 0x10F0,
SFC_SET_BROADCAST_INFO = 0x10F1,
SFC_GET_CHANNEL_MAP_INFO = 0x1100,
SFC_SET_CHANNEL_MAP_INFO = 0x1101,
SFC_RAW_DATA_NEEDS_ENDSWAP = 0x1110,
/* Support for Wavex Ambisonics Format */
SFC_WAVEX_SET_AMBISONIC = 0x1200,
SFC_WAVEX_GET_AMBISONIC = 0x1201,
/*
** RF64 files can be set so that on-close, writable files that have less
** than 4GB of data in them are converted to RIFF/WAV, as per EBU
** recommendations.
*/
SFC_RF64_AUTO_DOWNGRADE = 0x1210,
SFC_SET_VBR_ENCODING_QUALITY = 0x1300,
SFC_SET_COMPRESSION_LEVEL = 0x1301,
/* Cart Chunk support */
SFC_SET_CART_INFO = 0x1400,
SFC_GET_CART_INFO = 0x1401,
/* Opus files original samplerate metadata */
SFC_SET_ORIGINAL_SAMPLERATE = 0x1500,
SFC_GET_ORIGINAL_SAMPLERATE = 0x1501,
/* Following commands for testing only. */
SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001,
/*
** These SFC_SET_ADD_* values are deprecated and will disappear at some
** time in the future. They are guaranteed to be here up to and
** including version 1.0.8 to avoid breakage of existing software.
** They currently do nothing and will continue to do nothing.
*/
SFC_SET_ADD_HEADER_PAD_CHUNK = 0x1051,
SFC_SET_ADD_DITHER_ON_WRITE = 0x1070,
SFC_SET_ADD_DITHER_ON_READ = 0x1071
}
public enum SF_STR
{
SF_STR_TITLE,
SF_STR_COPYRIGHT,
SF_STR_SOFTWARE,
SF_STR_ARTIST,
SF_STR_COMMENT,
SF_STR_DATE,
SF_STR_ALBUM,
SF_STR_LICENSE,
SF_STR_TRACKNUMBER,
SF_STR_GENRE
}
public enum SF_ERROR
{
SF_ERR_NO_ERROR = 0,
SF_ERR_UNRECOGNISED_FORMAT = 1,
SF_ERR_SYSTEM = 2,
SF_ERR_MALFORMED_FILE = 3,
SF_ERR_UNSUPPORTED_ENCODING = 4
}
public enum SF_FORMAT
{
/* Major formats. */
SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian). */
SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */
SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */
SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */
SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */
SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */
SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */
SF_FORMAT_VOC = 0x080000, /* VOC files. */
SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */
SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */
SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */
SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */
SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */
SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */
SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */
SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */
SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */
SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */
SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */
SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */
SF_FORMAT_CAF = 0x180000, /* Core Audio File format */
SF_FORMAT_WVE = 0x190000, /* Psion WVE format */
SF_FORMAT_OGG = 0x200000, /* Xiph OGG container */
SF_FORMAT_MPC2K = 0x210000, /* Akai MPC 2000 sampler */
SF_FORMAT_RF64 = 0x220000, /* RF64 WAV file */
/* Subtypes from here on. */
SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */
SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */
SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */
SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */
SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */
SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */
SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */
SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */
SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */
SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */
SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */
SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */
SF_FORMAT_VOX_ADPCM = 0x0021, /* Oki Dialogic ADPCM encoding. */
SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */
SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */
SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */
SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */
SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */
SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */
SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */
SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */
SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */
SF_FORMAT_VORBIS = 0x0060, /* Xiph Vorbis encoding. */
/* Endian-ness options. */
SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */
SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */
SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */
SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */
SF_FORMAT_SUBMASK = 0x0000FFFF,
SF_FORMAT_TYPEMASK = 0x0FFF0000,
SF_FORMAT_ENDMASK = 0x30000000
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment