source: trunk/csharp/audio/NAudio/WaveFileWriter.cs @ 1896

Revision 1896, 14.2 KB checked in by daniel-weck, 3 years ago (diff)

libs now compile in VS 2005 with .NET 2

Line 
1using System;
2using System.IO;
3
4namespace NAudio.Wave
5{
6    /// <summary>
7    /// This class writes WAV data to a .wav file on disk
8    /// </summary>
9    public class WaveFileWriter : IDisposable
10    {
11        private Stream outStream;
12        private BinaryWriter writer;
13        private long dataSizePos;
14        private long factSampleCountPos;
15        private int dataChunkSize = 0;
16        private WaveFormat format;
17        private bool overwriting;
18        private string filename;
19
20        /// <summary>
21        /// Creates a Wave file by reading all the data from a WaveStream
22        /// </summary>
23        /// <param name="filename">The filename to use</param>
24        /// <param name="stream">The source WaveStream</param>
25        public static void CreateWaveFile(string filename, WaveStream stream)
26        {
27            using (WaveFileWriter writer = new WaveFileWriter(filename, stream.WaveFormat))
28            {
29                byte[] buffer = new byte[stream.GetReadSize(4000)];
30                while (true)
31                {
32                    int bytesRead = stream.Read(buffer, 0, buffer.Length);
33                    if (bytesRead == 0)
34                        break;
35                    writer.WriteData(buffer, 0, bytesRead);
36                }
37            }
38        }
39
40        /// <summary>
41        /// WaveFileWriter that actually writes to a stream
42        /// </summary>
43        /// <param name="outStream">Stream to be written to</param>
44        /// <param name="format">Wave format to use</param>
45        public WaveFileWriter(Stream outStream, WaveFormat format)
46        {
47            this.outStream = outStream;   
48            this.writer = new BinaryWriter(outStream, System.Text.Encoding.ASCII);
49            this.writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
50            this.writer.Write((int)0); // placeholder
51            this.writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt "));
52            this.format = format;
53
54            format.Serialize(this.writer);
55
56            CreateFactChunk();
57
58            WriteDataChunkHeader();
59        }
60
61        /// <summary>
62        /// Creates a new WaveFileWriter, simply overwriting the samples on an existing file
63        /// </summary>
64        /// <param name="filename">The filename</param>
65        [Obsolete("Not planning to keep supporting this, should create derived WaveFileWriter for this type of behaviour if needed")]
66        public WaveFileWriter(string filename)
67        {
68            this.filename = filename;
69            this.outStream = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
70            this.writer = new BinaryWriter(outStream);
71            int dataChunkLength; //
72            long dataChunkPosition;
73            WaveFileReader.ReadWaveHeader(outStream, out format, out dataChunkPosition, out dataChunkLength, null);
74            dataSizePos = dataChunkPosition - 4;
75            overwriting = true;
76        }
77
78        /// <summary>
79        /// Creates a new WaveFileWriter
80        /// </summary>
81        /// <param name="filename">The filename to write to</param>
82        /// <param name="format">The Wave Format of the output data</param>
83        public WaveFileWriter(string filename, WaveFormat format)
84            : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), format)
85        {
86            this.filename = filename;
87        }
88
89        private void WriteDataChunkHeader()
90        {
91            this.writer.Write(System.Text.Encoding.ASCII.GetBytes("data"));
92            dataSizePos = this.outStream.Position;
93            this.writer.Write((int)0); // placeholder
94        }
95
96        private void CreateFactChunk()
97        {
98            if (this.format.Encoding != WaveFormatEncoding.Pcm)
99            {
100                this.writer.Write(System.Text.Encoding.ASCII.GetBytes("fact"));
101                this.writer.Write((int)4);
102                factSampleCountPos = this.outStream.Position;
103                this.writer.Write((int)0); // number of samples
104            }
105        }
106
107
108
109        /// <summary>
110        /// The wave file name
111        /// </summary>
112        public string Filename
113        {
114            get
115            {
116                return filename;
117            }
118        }
119
120        /// <summary>
121        /// Number of bytes of audio
122        /// </summary>
123        public long Length
124        {
125            get
126            {
127                return dataChunkSize;
128            }
129        }
130
131        /// <summary>
132        /// WaveFormat of this wave file
133        /// </summary>
134        public WaveFormat WaveFormat
135        {
136            get
137            {
138                return format;
139            }
140        }
141
142        /// <summary>
143        /// Writes bytes to the WaveFile (assumes they are already in the correct format)
144        /// </summary>
145        /// <param name="data">the buffer containing the wave data</param>
146        /// <param name="offset">the offset from which to start writing</param>
147        /// <param name="count">the number of bytes to write</param>
148        public void WriteData(byte[] data, int offset, int count)
149        {
150            outStream.Write(data, offset, count);
151            dataChunkSize += count;
152        }
153
154        private byte[] value24 = new byte[3]; // keep this around to save us creating it every time
155       
156        /// <summary>
157        /// Writes a single sample to the Wave file
158        /// </summary>
159        /// <param name="sample">the sample to write (assumed floating point with 1.0f as max value)</param>
160        public void WriteSample(float sample)
161        {
162            if (WaveFormat.BitsPerSample == 16)
163            {
164                writer.Write((Int16)(Int16.MaxValue * sample));
165            }
166            else if (WaveFormat.BitsPerSample == 24)
167            {
168                byte[] value = BitConverter.GetBytes((Int32)(Int32.MaxValue * sample));
169                value24[0] = value[1];
170                value24[1] = value[2];
171                value24[2] = value[3];
172                writer.Write(value24);
173            }
174            else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible)
175            {
176                writer.Write(UInt16.MaxValue * (Int32)sample);
177            }
178            else if (WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
179            {
180                writer.Write(sample);
181            }
182            else
183            {
184                throw new ApplicationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported");
185            }
186        }
187
188        /// <summary>
189        /// Writes 16 bit samples to the Wave file
190        /// </summary>
191        /// <param name="data">The buffer containing the wave data</param>
192        /// <param name="offset">The offset from which to start writing</param>
193        /// <param name="count">The number of 16 bit samples to write</param>
194        public void WriteData(short[] data, int offset, int count)
195        {
196            // 16 bit PCM data
197            if (WaveFormat.BitsPerSample == 16)
198            {               
199                for (int sample = 0; sample < count; sample++)
200                {
201                    writer.Write(data[sample + offset]);
202                }
203            }
204            // 24 bit PCM data
205            else if (WaveFormat.BitsPerSample == 24)
206            {
207                byte[] value;
208                for (int sample = 0; sample < count; sample++)
209                {
210                    value = BitConverter.GetBytes(UInt16.MaxValue * (Int32)data[sample + offset]);
211                    value24[0] = value[1];
212                    value24[1] = value[2];
213                    value24[2] = value[3];
214                    writer.Write(value24);
215                }
216            }
217            // 32 bit PCM data
218            else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible)
219            {
220                for (int sample = 0; sample < count; sample++)
221                {
222                    writer.Write(UInt16.MaxValue * (Int32)data[sample + offset]);
223                }
224            }
225            // IEEE float data
226            else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
227            {
228                for (int sample = 0; sample < count; sample++)
229                {
230                    writer.Write((float)data[sample + offset] / (float)(Int16.MaxValue + 1));
231                }
232            }
233            else
234            {
235                throw new ApplicationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported");
236            }
237        }
238
239        /// <summary>
240        /// Writes float samples to the Wave file
241        /// </summary>
242        /// <param name="data">The buffer containing the wave data</param>
243        /// <param name="offset">The offset from which to start writing</param>
244        /// <param name="count">The number of float samples to write</param>
245        [Obsolete("Use the WriteSample method instead")]
246        public void WriteData(float[][] data, int offset, int count)
247        {
248            // 16 bit PCM data
249            if (WaveFormat.BitsPerSample == 16)
250            {
251                for (int sample = 0; sample < count; sample++)
252                {
253                    for (int channel = 0; channel < WaveFormat.Channels; channel++)
254                    {
255                        writer.Write((Int16)(Int16.MaxValue * data[channel][sample + offset]));
256                    }
257                }
258            }
259            // 24 bit PCM data
260            else if (WaveFormat.BitsPerSample == 24)
261            {
262                byte[] value;
263                byte[] value24 = new byte[3];
264                for (int sample = 0; sample < count; sample++)
265                {
266                    for (int channel = 0; channel < WaveFormat.Channels; channel++)
267                    {
268                        value = BitConverter.GetBytes((Int32)(Int32.MaxValue * data[channel][sample + offset]));
269                        value24[0] = value[1];
270                        value24[1] = value[2];
271                        value24[2] = value[3];
272                        writer.Write(value24);
273                    }
274                }
275            }
276            // 32 bit PCM data
277            else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible)
278            {
279                for (int sample = 0; sample < count; sample++)
280                {
281                    for (int channel = 0; channel < WaveFormat.Channels; channel++)
282                    {
283                        writer.Write((Int32)(Int32.MaxValue * data[channel][sample + offset]));
284                    }
285                }
286            }
287            // IEEE float data
288            else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
289            {
290                for (int sample = 0; sample < count; sample++)
291                {
292                    for (int channel = 0; channel < WaveFormat.Channels; channel++)
293                    {
294                        writer.Write(data[channel][sample + offset]);
295                    }
296                }
297            }
298            else
299            {
300                throw new ApplicationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported");
301            }
302        }
303
304        /// <summary>
305        /// Ensures data is written to disk
306        /// </summary>
307        public void Flush()
308        {
309            outStream.Flush();
310        }
311
312        #region IDisposable Members
313
314        /// <summary>
315        /// Closes this WaveFile (calls <see>Dispose</see>)
316        /// </summary>
317        public void Close()
318        {
319            Dispose();
320        }
321
322        /// <summary>
323        /// Closes this WaveFile
324        /// </summary>
325        public void Dispose()
326        {
327            GC.SuppressFinalize(this);
328            Dispose(true);
329        }
330
331        /// <summary>
332        /// Actually performs the close,making sure the header contains the correct data
333        /// </summary>
334        /// <param name="disposing">True if called from <see>Dispose</see></param>
335        protected virtual void Dispose(bool disposing)
336        {
337            if (disposing)
338            {
339                if (outStream != null)
340                {
341                    try
342                    {
343                        if (!overwriting)
344                        {
345                            UpdateHeader(writer);
346                        }
347                    }
348                    finally
349                    {
350                        // in a finally block as we don't want the FileStream to run its disposer in
351                        // the GC thread if the code above caused an IOException (e.g. due to disk full)
352                        outStream.Close(); // will close the underlying base stream
353                        outStream = null;
354                    }
355                }
356            }
357        }
358
359        /// <summary>
360        /// Updates the header with file size information
361        /// </summary>
362        protected virtual void UpdateHeader(BinaryWriter writer)
363        {
364            // in overwrite mode, we will not change the length set at the start
365            // irrespective of whether we actually wrote less or more
366            outStream.Flush();
367            writer.Seek(4, SeekOrigin.Begin);
368            writer.Write((int)(outStream.Length - 8));
369            if (format.Encoding != WaveFormatEncoding.Pcm)
370            {
371                writer.Seek((int)factSampleCountPos, SeekOrigin.Begin);
372                writer.Write((int)((dataChunkSize * 8) / format.BitsPerSample));
373            }
374            writer.Seek((int)dataSizePos, SeekOrigin.Begin);
375            writer.Write((int)(dataChunkSize));
376        }
377
378
379
380        /// <summary>
381        /// Finaliser - should only be called if the user forgot to close this WaveFileWriter
382        /// </summary>
383        ~WaveFileWriter()
384        {
385            System.Diagnostics.Debug.Assert(false, "WaveFileWriter was not disposed");
386            Dispose(false);
387        }
388
389        #endregion
390    }
391}
Note: See TracBrowser for help on using the repository browser.