/* feedseekclr: test program for mpg123clr, showing how to use fuzzy seeking in feeder mode copyright 2009 by the mpg123 project - free software under the terms of the LGPL 2.1 see COPYING and AUTHORS files in distribution or http://mpg123.org based on feedseek.c example for libmpg123. Comment (Malcolm Boczek) this CLR example has been written to allow easy comparison to the original feedseek.c example and uses some constructs that would not normally be used in a C# environment, eg: byte[]/ASCII text, Marshal.Copy, static fields, lots of casts etc. */ /* 1.9.0.0 24-Sep-09 Function names harmonized with libmpg123 (mb) */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Runtime.InteropServices; using mpg123clr; namespace feedseekclr { class Program { const int WAVE_FORMAT_PCM = 0x0001; const int WAVE_FORMAT_IEEE_FLOAT = 0x0003; static BinaryWriter _out; static long totaloffset, dataoffset; static int rate; static mpg123clr.mpg.channelcount channels; static mpg123clr.mpg.enc enc; static short bitspersample, wavformat; // write wav header static void initwav() { uint tmp32 = 0; ushort tmp16 = 0; byte[] rifftxt = new byte[] { (byte)'R', (byte)'I', (byte)'F', (byte)'F' }; byte[] wavetxt = new byte[] { (byte)'W', (byte)'A', (byte)'V', (byte)'E' }; byte[] fmttxt = new byte[] { (byte)'f', (byte)'m', (byte)'t', (byte)' ' }; byte[] datatxt = new byte[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a' }; _out.Write(rifftxt); totaloffset = _out.BaseStream.Position; _out.Write(tmp32); // total size _out.Write(wavetxt); _out.Write(fmttxt); tmp32 = 16; _out.Write(tmp32); // format length tmp16 = (ushort)wavformat; _out.Write(tmp16); // format tmp16 = (ushort)channels; _out.Write(tmp16); // channels tmp32 = (uint)rate; _out.Write(tmp32); // sample rate tmp32 = (uint) (rate * bitspersample / 8 * (int)channels); _out.Write(tmp32); // bytes / second tmp16 = (ushort)(bitspersample / 8 * (int)channels); // float 16 or signed int 16 _out.Write(tmp16); // block align tmp16 = (ushort)bitspersample; _out.Write(tmp16); // bits per sample _out.Write(datatxt); tmp32 = 0; dataoffset = _out.BaseStream.Position; _out.Write(tmp32); // data length } // rewrite wav header with final length infos static void closewav() { uint tmp32 = 0; // ushort tmp16 = 0; int total = (int)_out.BaseStream.Position; _out.Seek((int)totaloffset, SeekOrigin.Begin); tmp32 = (uint)(total - (totaloffset + 4)); _out.Write(tmp32); _out.Seek((int)dataoffset, SeekOrigin.Begin); tmp32 = (uint)(total - (dataoffset + 4)); _out.Write(tmp32); } // determine correct wav format and bits per sample // from mpg123 enc value static void initwavformat() { if ((enc & mpg123clr.mpg.enc.enc_float_64) != 0) { bitspersample = 64; wavformat = WAVE_FORMAT_IEEE_FLOAT; } else if ((enc & mpg123clr.mpg.enc.enc_float_32) != 0) { bitspersample = 32; wavformat = WAVE_FORMAT_IEEE_FLOAT; } else if ((enc & mpg123clr.mpg.enc.enc_16) != 0) { bitspersample = 16; wavformat = WAVE_FORMAT_PCM; } else { bitspersample = 8; wavformat = WAVE_FORMAT_PCM; } } static void Main(string[] args) { const long INBUFF = 16384 * 2 * 2; int ret; mpg123clr.mpg.ErrorCode state; long inoffset,inc = 0; long outc = 0; byte[] buf = new byte[INBUFF]; if (args.Length < 2) { Console.WriteLine("Please supply in and out filenames\n"); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } mpg123clr.mpg.ErrorCode err; err = mpg123.mpg123_init(); mpg123 mp = new mpg123(); err = mp.mpg123_new(); if (err != mpg123clr.mpg.ErrorCode.ok) { Console.WriteLine("Unable to create mpg123 handle: " + mpg123error.mpg123_plain_strerror(err)); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } mp.mpg123_param(mpg123clr.mpg.parms.verbose, 4, 0); err = mp.mpg123_param(mpg123clr.mpg.parms.flags, (int) (mpg123clr.mpg.param_flags.fuzzy | mpg123clr.mpg.param_flags.seekbuffer | mpg123clr.mpg.param_flags.gapless), 0); if (err != mpg123clr.mpg.ErrorCode.ok) { Console.WriteLine("Unable to set library options: " + mp.mpg123_strerror()); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } // Let the seek index auto-grow and contain an entry for every frame err = mp.mpg123_param(mpg123clr.mpg.parms.index_size, -1, 0); if (err != mpg123clr.mpg.ErrorCode.ok) { Console.WriteLine("Unable to set index size: " + mp.mpg123_strerror()); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } // Use float output formats only err = mp.mpg123_format_none(); if (err != mpg123clr.mpg.ErrorCode.ok) { Console.WriteLine("Unable to disable all output formats: " + mp.mpg123_strerror()); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } int[] rates = mp.mpg123_rates(); foreach (int rate in rates) { err = mp.mpg123_format(rate, mpg123clr.mpg.channelcount.both, mpg123clr.mpg.enc.enc_float_32); if (err != mpg123clr.mpg.ErrorCode.ok) { Console.WriteLine("Unable to set float output formats: " + mp.mpg123_strerror()); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } } err = mp.mpg123_open_feed(); if (err != mpg123clr.mpg.ErrorCode.ok) { Console.WriteLine("Unable to open feed: " + mp.mpg123_strerror()); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } string filename = args[0]; BinaryReader _in = new BinaryReader(File.Open(filename, FileMode.Open)); _out = new BinaryWriter(File.Open(args[1], FileMode.Create)); while ((ret = (int)(mp.mpg123_feedseek(95000, SeekOrigin.Begin, out inoffset))) == (int)mpg123clr.mpg.ErrorCode.need_more) // equiv to mpg123_feedseek { buf = _in.ReadBytes((int)INBUFF); if (buf.Length <= 0) break; inc += buf.Length; state = mp.mpg123_feed(buf, (uint)buf.Length); if (state == mpg123clr.mpg.ErrorCode.err) { Console.WriteLine("Feed error: " + mp.mpg123_strerror()); Console.WriteLine("Press any key to exit:"); while (Console.Read() == 0) ; return; } } _in.BaseStream.Seek(inoffset, SeekOrigin.Begin); while (true) { buf = _in.ReadBytes((int)INBUFF); if (buf.Length <= 0) break; inc += buf.Length; err = mp.mpg123_feed(buf, (uint)buf.Length); int num; uint bytes; IntPtr audio; while (err != mpg123clr.mpg.ErrorCode.err && err != mpg123clr.mpg.ErrorCode.need_more) { err = mp.mpg123_decode_frame(out num, out audio, out bytes); if (err == mpg123clr.mpg.ErrorCode.new_format) { mp.mpg123_getformat(out rate, out channels, out enc); initwavformat(); initwav(); } // (Surprisingly?) even though it does a Marshal.Copy it's as efficient as the pointer example below!!! if (bytes > 0) { byte[] outbuf = new byte[bytes]; Marshal.Copy(audio, outbuf, 0, (int)bytes); _out.Write(outbuf, 0, (int)bytes); } // Alternative example of direct usage of audio data via pointers - note it needs "unsafe" // and I'm fairly sure pointers should be "fixed" first // if (bytes > 0) // unsafe{ // byte* p = (byte*)audio; // for (int ii = 0; ii < bytes; ii++) // _out.Write(*p++); // } outc += bytes; } if (err == mpg123clr.mpg.ErrorCode.err) { Console.WriteLine("Error: " + mp.mpg123_strerror()); break; } } Console.WriteLine("Finished"); closewav(); _out.Close(); _in.Close(); mp.mpg123_delete(); mp.Dispose(); mpg123.mpg123_exit(); } } }