using System; using System.IO; using System.Runtime.InteropServices; namespace Mono.Audio { #if PUBLIC_API public #else internal #endif abstract class AudioData { protected const int buffer_size = 4096; bool stopped; public abstract int Channels { get; } public abstract int Rate { get; } public abstract AudioFormat Format { get; } public virtual void Setup (AudioDevice dev) { dev.SetFormat (Format, Channels, Rate); } public abstract void Play (AudioDevice dev); public virtual bool IsStopped { get { return stopped; } set { stopped = value; } } } /*public enum WavCmpCodes { Unknown, PCM, ADPCM, }*/ #if PUBLIC_API public #else internal #endif class WavData : AudioData { Stream stream; short channels; ushort frame_divider; int sample_rate; int data_len; long data_offset; AudioFormat format; public WavData (Stream data) { stream = data; byte[] buffer = new byte [12 + 32]; int idx; // Read Chunk ID + Format int c = stream.Read (buffer, 0, 12); if (c != 12 || buffer [0] != 'R' || buffer [1] != 'I' || buffer [2] != 'F' || buffer [3] != 'F' || buffer [8] != 'W' || buffer [9] != 'A' || buffer [10] != 'V' || buffer [11] != 'E') { throw new Exception ("incorrect format" + c); } // Read SubChunk 1 ID + Size => Must be 'fmt ' ! c = stream.Read (buffer, 0, 8); if (c == 8 && buffer [0] == 'f' && buffer [1] == 'm' && buffer [2] == 't' && buffer [3] == ' ') { int sub_chunk_1_size = buffer [4]; sub_chunk_1_size |= buffer [5] << 8; sub_chunk_1_size |= buffer [6] << 16; sub_chunk_1_size |= buffer [7] << 24; // Read SubChunk 1 Data c = stream.Read (buffer, 0, sub_chunk_1_size); if (sub_chunk_1_size == c) { idx = 0; int compression = buffer [idx++] | (buffer [idx++] << 8); if (compression != 1) throw new Exception ("incorrect format (not PCM)"); channels = (short)(buffer [idx++] | (buffer [idx++] << 8)); sample_rate = buffer [idx++]; sample_rate |= buffer [idx++] << 8; sample_rate |= buffer [idx++] << 16; sample_rate |= buffer [idx++] << 24; int byte_rate = buffer [idx++]; byte_rate |= buffer [idx++] << 8; byte_rate |= buffer [idx++] << 16; byte_rate |= buffer [idx++] << 24; // int block_align = buffer [idx++] | (buffer [idx++] << 8); idx += 2; //because, the above line is commented out int sign_bits = buffer [idx++] | (buffer [idx++] << 8); switch (sign_bits) { case 8: frame_divider = 1; format = AudioFormat.U8; break; case 16: frame_divider = 2; format = AudioFormat.S16_LE; break; default: throw new Exception ("bits per sample"); } } else { throw new Exception ("Error: Can't Read "+sub_chunk_1_size+" bytes from stream ("+c+" bytes read"); } } else { throw new Exception ("incorrect format (fmt)"); } // Read SubChunk 2 ID + Size => Could be 'fact' or 'data' ! c = stream.Read (buffer, 0, 8); if (c == 8) { // If SubChunk 2 ID = fact if (buffer [0] == 'f' && buffer [1] == 'a' && buffer [2] == 'c' && buffer [3] == 't') { // Read Data int sub_chunk_2_size = buffer [4]; sub_chunk_2_size |= buffer [5] << 8; sub_chunk_2_size |= buffer [6] << 16; sub_chunk_2_size |= buffer [7] << 24; c = stream.Read (buffer, 0, sub_chunk_2_size); // Don't care about this data ! // If there is a fact Chunck, read the next subChunk Id and size (should be data !) c = stream.Read (buffer, 0, 8); } if (buffer [0] == 'd' && buffer [1] == 'a' && buffer [2] == 't' && buffer [3] == 'a') { // Read Data int sub_chunk_2_size = buffer [4]; sub_chunk_2_size |= buffer [5] << 8; sub_chunk_2_size |= buffer [6] << 16; sub_chunk_2_size |= buffer [7] << 24; data_len = sub_chunk_2_size; data_offset = stream.Position; } else { throw new Exception ("incorrect format (data/fact chunck)"); } } } public override void Play (AudioDevice dev) { int fragment_played = 0; int total_data_played = 0; int chunk_size = (int)dev.ChunkSize; int count = data_len; byte[] buffer = new byte [data_len]; byte[] chunk_to_play = new byte [chunk_size]; // Read only wave data, don't care about file header here ! stream.Position = data_offset; stream.Read (buffer, 0, data_len); while (!IsStopped && count >= 0){ // Copy one chunk from buffer Buffer.BlockCopy(buffer, total_data_played, chunk_to_play, 0, chunk_size); // play that chunk, !!! the size pass to alsa the number of fragment, a fragment is a sample per channel !!! fragment_played = dev.PlaySample (chunk_to_play, chunk_size / (frame_divider * channels)); // If alsa played something, inc the total data played and dec the data to be played if (fragment_played > 0) { total_data_played += (fragment_played * frame_divider * channels); count -= (fragment_played * frame_divider * channels); } } } public override int Channels { get {return channels;} } public override int Rate { get {return sample_rate;} } public override AudioFormat Format { get {return format;} } } // http://en.wikipedia.org/wiki/Au_file_format #if PUBLIC_API public #else internal #endif class AuData : AudioData { Stream stream; short channels; ushort frame_divider; int sample_rate; int data_len ; // int data_offset; AudioFormat format; public AuData (Stream data) { stream = data; byte[] buffer = new byte [24]; int c = stream.Read (buffer, 0, 24); if (c != 24 || buffer [0] != '.' || buffer [1] != 's' || buffer [2] != 'n' || buffer [3] != 'd') { throw new Exception ("incorrect format" + c); } int data_offset = buffer [7]; data_offset |= buffer [6] << 8; data_offset |= buffer [5] << 16; data_offset |= buffer [4] << 24; data_len = buffer [11]; data_len |= buffer [10] << 8; data_len |= buffer [9] << 16; data_len |= buffer [8] << 24; int encoding = buffer [15]; encoding |= buffer [14] << 8; encoding |= buffer [13] << 16; encoding |= buffer [12] << 24; sample_rate = buffer [19]; sample_rate |= buffer [18] << 8; sample_rate |= buffer [17] << 16; sample_rate |= buffer [16] << 24; int chans = buffer [23]; chans |= buffer [22] << 8; chans |= buffer [21] << 16; chans |= buffer [20] << 24; channels = (short)chans; if (data_offset < 24 || (chans != 1 && chans != 2)) { throw new Exception ("incorrect format offset" + data_offset); } if (data_offset != 24) { for (int l = 24; l < data_offset; ++l) stream.ReadByte (); } switch (encoding) { case 1: frame_divider = 1; format = AudioFormat.MU_LAW; break; default: throw new Exception ("incorrect format encoding" + encoding); } if (data_len == -1) { data_len = (int)stream.Length - data_offset; } // Console.WriteLine ("format: {0}, rate: {1}", format, sample_rate); } public override void Play (AudioDevice dev) { int fragment_played = 0; int total_data_played = 0; int chunk_size = (int)dev.ChunkSize; int count = data_len; byte[] buffer = new byte [data_len]; byte[] chunk_to_play = new byte [chunk_size]; // Read only Au data, don't care about file header here ! stream.Position = 0; //(long)data_offset; stream.Read (buffer, 0, data_len); while (!IsStopped && count >= 0){ // Copy one chunk from buffer Buffer.BlockCopy(buffer, total_data_played, chunk_to_play, 0, chunk_size); // play that chunk, !!! the size pass to alsa the number of fragment, a fragment is a sample per channel !!! fragment_played = dev.PlaySample (chunk_to_play, chunk_size / (frame_divider * channels)); // If alsa played something, inc the total data played and dec the data to be played if (fragment_played > 0) { total_data_played += (fragment_played * frame_divider * channels); count -= (fragment_played * frame_divider * channels); } } } public override int Channels { get {return channels;} } public override int Rate { get {return sample_rate;} } public override AudioFormat Format { get {return format;} } } }