Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / class / System / System.Media / AudioData.cs
1
2 using System;
3 using System.IO;
4 using System.Runtime.InteropServices;
5
6 namespace Mono.Audio {
7  
8 #if PUBLIC_API
9         public
10 #else
11         internal
12 #endif
13         abstract class AudioData {
14                 protected const int buffer_size = 4096;
15                 bool stopped;
16
17                 public abstract int Channels {
18                         get;
19                 }
20
21                 public abstract int Rate {
22                         get;
23                 }
24
25                 public abstract AudioFormat Format {
26                         get;
27                 }
28
29                 public virtual void Setup (AudioDevice dev) {
30                         dev.SetFormat (Format, Channels, Rate);
31                 }
32
33                 public abstract void Play (AudioDevice dev);
34
35                 public virtual bool IsStopped {
36                         get {
37                                 return stopped;
38                         }
39                         set {
40                                 stopped = value;
41                         }
42                 }
43         }
44
45         /*public enum WavCmpCodes {
46                 Unknown,
47                 PCM,
48                 ADPCM,
49         }*/
50
51 #if PUBLIC_API
52         public
53 #else
54         internal
55 #endif
56         class WavData : AudioData {
57                 Stream stream;
58                 short channels;
59                 ushort frame_divider;
60                 int sample_rate;
61                 int data_len;
62                 long data_offset;
63                 AudioFormat format;
64
65                 public WavData (Stream data) {
66                         stream = data;
67                         byte[] buffer = new byte [12 + 32];
68                         int idx;
69                         
70                         // Read Chunk ID + Format
71                         int c = stream.Read (buffer, 0, 12);
72                         if (c != 12 ||
73                                 buffer [0] != 'R' || buffer [1] != 'I' || buffer [2] != 'F' || buffer [3] != 'F' ||
74                                 buffer [8] != 'W' || buffer [9] != 'A' || buffer [10] != 'V' || buffer [11] != 'E') {
75                                 throw new Exception ("incorrect format" + c);
76                         }
77
78                         // Read SubChunk 1 ID + Size => Must be 'fmt ' !
79                         c = stream.Read (buffer, 0, 8);
80                         if (c == 8 && buffer [0] == 'f' && buffer [1] == 'm' && buffer [2] == 't' && buffer [3] == ' ') {
81                                 int sub_chunk_1_size = buffer [4];
82                                 sub_chunk_1_size |= buffer [5] << 8;
83                                 sub_chunk_1_size |= buffer [6] << 16;
84                                 sub_chunk_1_size |= buffer [7] << 24;
85
86                                 // Read SubChunk 1 Data
87                                 c = stream.Read (buffer, 0, sub_chunk_1_size);
88                                 if (sub_chunk_1_size == c)
89                                 {
90                                         idx = 0;
91                                         int compression = buffer [idx++] | (buffer [idx++] << 8);
92                                         if (compression != 1)
93                                                 throw new Exception ("incorrect format (not PCM)");
94                                         channels = (short)(buffer [idx++] | (buffer [idx++] << 8));
95                                         sample_rate = buffer [idx++];
96                                         sample_rate |= buffer [idx++] << 8;
97                                         sample_rate |= buffer [idx++] << 16;
98                                         sample_rate |= buffer [idx++] << 24;
99                                         int byte_rate = buffer [idx++];
100                                         byte_rate |= buffer [idx++] << 8;
101                                         byte_rate |= buffer [idx++] << 16;
102                                         byte_rate |= buffer [idx++] << 24;
103 //                                      int block_align = buffer [idx++] | (buffer [idx++] << 8);
104                                         int sign_bits = buffer [idx++] | (buffer [idx++] << 8);
105
106                                         switch (sign_bits) {
107                                         case 8:
108                                                 frame_divider = 1;
109                                                 format = AudioFormat.U8; break;
110                                         case 16:
111                                                 frame_divider = 2;
112                                                 format = AudioFormat.S16_LE; break;
113                                         default:
114                                                 throw new Exception ("bits per sample");
115                                         }
116
117                                 } else {
118                                         throw new Exception ("Error: Can't Read "+sub_chunk_1_size+" bytes from stream ("+c+" bytes read");
119                                 }
120                         } else {
121                                 throw new Exception ("incorrect format (fmt)");
122                         }
123
124                         // Read SubChunk 2 ID + Size => Could be 'fact' or 'data' !
125                         c = stream.Read (buffer, 0, 8);
126                         if (c == 8) {
127                                 // If SubChunk 2 ID = fact
128                                 if (buffer [0] == 'f' && buffer [1] == 'a' && buffer [2] == 'c' && buffer [3] == 't') {
129                                         // Read Data
130                                         int sub_chunk_2_size = buffer [4];
131                                         sub_chunk_2_size |= buffer [5] << 8;
132                                         sub_chunk_2_size |= buffer [6] << 16;
133                                         sub_chunk_2_size |= buffer [7] << 24;
134
135                                         c = stream.Read (buffer, 0, sub_chunk_2_size);
136
137                                         // Don't care about this data !
138
139                                         // If there is a fact Chunck, read the next subChunk Id and size (should be data !)
140                                         c = stream.Read (buffer, 0, 8);
141                                 }
142
143                                 if (buffer [0] == 'd' && buffer [1] == 'a' && buffer [2] == 't' && buffer [3] == 'a') {
144                                         // Read Data
145                                         int sub_chunk_2_size = buffer [4];
146                                         sub_chunk_2_size |= buffer [5] << 8;
147                                         sub_chunk_2_size |= buffer [6] << 16;
148                                         sub_chunk_2_size |= buffer [7] << 24;
149
150                                         data_len = sub_chunk_2_size;
151                                         data_offset = stream.Position;
152                                 } else { 
153                                         throw new Exception ("incorrect format (data/fact chunck)");
154                                 }
155                         }
156                 }
157
158                 public override void Play (AudioDevice dev) {
159                         int    fragment_played = 0;
160                         int    total_data_played = 0;
161                         int    chunk_size        = (int)dev.ChunkSize;
162                         int    count             = data_len;
163                         byte[] buffer            = new byte [data_len];
164                         byte[] chunk_to_play     = new byte [chunk_size];
165
166                         // Read only wave data, don't care about file header here !
167                         stream.Position = data_offset;
168                         stream.Read (buffer, 0, data_len); 
169
170                         while (!IsStopped && count >= 0){
171                                 // Copy one chunk from buffer
172                                 Buffer.BlockCopy(buffer, total_data_played, chunk_to_play, 0, chunk_size);
173                                 // play that chunk, !!! the size pass to alsa the number of fragment, a fragment is a sample per channel !!!
174                                 fragment_played = dev.PlaySample (chunk_to_play, chunk_size / (frame_divider * channels));
175
176                                 // If alsa played something, inc the total data played and dec the data to be played
177                                 if (fragment_played > 0) {
178                                         total_data_played  += (fragment_played * frame_divider * channels);
179                                         count              -= (fragment_played * frame_divider * channels);
180                                 }
181                         }
182                 }
183
184                 public override int Channels {
185                         get {return channels;}
186                 }
187                 public override int Rate {
188                         get {return sample_rate;}
189                 }
190                 public override AudioFormat Format {
191                         get {return format;}
192                 }
193         }
194
195         // http://en.wikipedia.org/wiki/Au_file_format
196 #if PUBLIC_API
197         public
198 #else
199         internal
200 #endif
201         class AuData : AudioData {
202                 Stream stream;
203                 short channels;
204                 ushort frame_divider;
205                 int sample_rate;
206                 int data_len ;
207 //              int data_offset;
208                 AudioFormat format;
209
210                 public AuData (Stream data) {
211                         stream = data;
212                         byte[] buffer = new byte [24];
213                         int c = stream.Read (buffer, 0, 24);
214                         if (c != 24 || 
215                                         buffer [0] != '.' || buffer [1] != 's' || buffer [2] != 'n' || buffer [3] != 'd') {
216                                 throw new Exception ("incorrect format" + c);
217                         }
218                         int data_offset = buffer [7];
219                         data_offset |= buffer [6] << 8;
220                         data_offset |= buffer [5] << 16;
221                         data_offset |= buffer [4] << 24;
222                         data_len = buffer [11];
223                         data_len |= buffer [10] << 8;
224                         data_len |= buffer [9] << 16;
225                         data_len |= buffer [8] << 24;
226                         int encoding = buffer [15];
227                         encoding |= buffer [14] << 8;
228                         encoding |= buffer [13] << 16;
229                         encoding |= buffer [12] << 24;
230                         sample_rate = buffer [19];
231                         sample_rate |= buffer [18] << 8;
232                         sample_rate |= buffer [17] << 16;
233                         sample_rate |= buffer [16] << 24;
234                         int chans = buffer [23];
235                         chans |= buffer [22] << 8;
236                         chans |= buffer [21] << 16;
237                         chans |= buffer [20] << 24;
238                         channels = (short)chans;
239                         if (data_offset < 24 || (chans != 1 && chans != 2)) {
240                                 throw new Exception ("incorrect format offset" + data_offset);
241                         }
242                         if (data_offset != 24) {
243                                 for (int l = 24; l < data_offset; ++l)
244                                         stream.ReadByte ();
245                         }
246                         switch (encoding) {
247                         case 1:
248                                 frame_divider = 1;
249                                 format = AudioFormat.MU_LAW; break;
250                         default:
251                                 throw new Exception ("incorrect format encoding" + encoding);
252                         }
253                         if (data_len == -1) {
254                                 data_len = (int)stream.Length - data_offset;
255                         }
256                         // Console.WriteLine ("format: {0}, rate: {1}", format, sample_rate);
257                 }
258
259                 public override void Play (AudioDevice dev) {
260                                                 int    fragment_played = 0;
261                         int    total_data_played = 0;
262                         int    chunk_size        = (int)dev.ChunkSize;
263                         int    count             = data_len;
264                         byte[] buffer            = new byte [data_len];
265                         byte[] chunk_to_play     = new byte [chunk_size];
266                         
267                         // Read only Au data, don't care about file header here !
268                         stream.Position = 0; //(long)data_offset;
269                         stream.Read (buffer, 0, data_len); 
270                         
271                         while (!IsStopped && count >= 0){
272                                 // Copy one chunk from buffer
273                                 Buffer.BlockCopy(buffer, total_data_played, chunk_to_play, 0, chunk_size);
274                                 // play that chunk, !!! the size pass to alsa the number of fragment, a fragment is a sample per channel !!!
275                                 fragment_played = dev.PlaySample (chunk_to_play, chunk_size / (frame_divider * channels));
276                                 
277                                 // If alsa played something, inc the total data played and dec the data to be played
278                                 if (fragment_played > 0) {
279                                         total_data_played  += (fragment_played * frame_divider * channels);
280                                         count              -= (fragment_played * frame_divider * channels);
281                                 }
282                         }
283                 }
284
285                 public override int Channels {
286                         get {return channels;}
287                 }
288                 public override int Rate {
289                         get {return sample_rate;}
290                 }
291                 public override AudioFormat Format {
292                         get {return format;}
293                 }
294         }
295
296 }
297
298