wxMMedia doc updates
[wxWidgets.git] / contrib / src / mmedia / sndwav.cpp
1 // --------------------------------------------------------------------------
2 // Name: sndwav.cpp
3 // Purpose:
4 // Date: 08/11/1999
5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999
6 // CVSID: $Id$
7 // --------------------------------------------------------------------------
8 #ifdef __GNUG__
9 #pragma implementation "sndwav.cpp"
10 #endif
11
12 #include <wx/wxprec.h>
13
14 #ifndef WX_PRECOMP
15 #include "wx/defs.h"
16 #endif
17
18 #ifdef __BORLANDC__
19 #pragma hdrstop
20 #endif
21
22 #include "wx/stream.h"
23 #include "wx/datstrm.h"
24 #include "wx/filefn.h"
25 #include "wx/mstream.h"
26
27 #include "wx/mmedia/sndbase.h"
28 #include "wx/mmedia/sndcodec.h"
29 #include "wx/mmedia/sndfile.h"
30 #include "wx/mmedia/sndpcm.h"
31 #include "wx/mmedia/sndg72x.h"
32 #include "wx/mmedia/sndmsad.h"
33 #include "wx/mmedia/sndwav.h"
34
35 #define BUILD_SIGNATURE(a,b,c,d) (((wxUint32)a) | (((wxUint32)b) << 8) | (((wxUint32)c) << 16) | (((wxUint32)d) << 24))
36
37 #define RIFF_SIGNATURE BUILD_SIGNATURE('R','I','F','F')
38 #define WAVE_SIGNATURE BUILD_SIGNATURE('W','A','V','E')
39 #define FMT_SIGNATURE BUILD_SIGNATURE('f','m','t',' ')
40 #define DATA_SIGNATURE BUILD_SIGNATURE('d','a','t','a')
41
42 #define HEADER_SIZE 4+4 + 4+4+16 + 4+4
43 // 4+4 => NAME + LEN
44 // 16 => fmt size
45
46 wxSoundWave::wxSoundWave(wxInputStream& stream, wxSoundStream& io_sound)
47 : wxSoundFileStream(stream, io_sound)
48 {
49 m_base_offset = wxInvalidOffset;
50 }
51
52 wxSoundWave::wxSoundWave(wxOutputStream& stream, wxSoundStream& io_sound)
53 : wxSoundFileStream(stream, io_sound)
54 {
55 m_base_offset = wxInvalidOffset;
56 }
57
58 wxSoundWave::~wxSoundWave()
59 {
60 }
61
62 wxString wxSoundWave::GetCodecName() const
63 {
64 return wxString(wxT("wxSoundWave codec"));
65 }
66
67 #define FAIL_WITH(condition, err) if (condition) { m_snderror = err; return FALSE; }
68
69 bool wxSoundWave::CanRead()
70 {
71 wxUint32 len, signature1, signature2;
72 m_snderror = wxSOUND_NOERROR;
73
74 // Test the main signatures:
75 // "RIFF"
76 FAIL_WITH(m_input->Read(&signature1, 4).LastRead() != 4, wxSOUND_INVSTRM);
77
78 if (wxUINT32_SWAP_ON_BE(signature1) != RIFF_SIGNATURE) {
79 m_input->Ungetch(&signature1, 4);
80 return FALSE;
81 }
82
83 // Pass the global length
84 m_input->Read(&len, 4);
85 FAIL_WITH(m_input->LastRead() != 4, wxSOUND_INVSTRM);
86
87 // Get the second signature
88 FAIL_WITH(m_input->Read(&signature2, 4).LastRead() != 4, wxSOUND_INVSTRM);
89 // Ungetch all
90 m_input->Ungetch(&signature2, 4);
91 m_input->Ungetch(&len, 4);
92 m_input->Ungetch(&signature1, 4);
93
94 // Test the second signature
95 if (wxUINT32_SWAP_ON_BE(signature2) != WAVE_SIGNATURE)
96 return FALSE;
97
98 return TRUE;
99 }
100
101 bool wxSoundWave::HandleOutputPCM(wxDataInputStream& data, wxUint32 len,
102 wxUint16 channels,
103 wxUint32 sample_fq, wxUint32 byte_p_sec,
104 wxUint16 byte_p_spl, wxUint16 bits_p_spl)
105 {
106 wxSoundFormatPcm sndformat;
107
108 sndformat.SetSampleRate(sample_fq);
109 sndformat.SetBPS(bits_p_spl);
110 sndformat.SetChannels(channels);
111 sndformat.Signed(TRUE);
112 sndformat.SetOrder(wxLITTLE_ENDIAN);
113
114 if (!SetSoundFormat(sndformat))
115 return FALSE;
116
117 m_input->SeekI(len, wxFromCurrent);
118
119 return TRUE;
120 }
121
122 bool wxSoundWave::HandleOutputMSADPCM(wxDataInputStream& data, wxUint32 len,
123 wxUint16 channels,
124 wxUint32 sample_fq, wxUint32 byte_p_sec,
125 wxUint16 byte_p_spl, wxUint16 bits_p_spl)
126 {
127 wxSoundFormatMSAdpcm sndformat;
128 wxInt16 *coefs[2];
129 wxUint16 coefs_len, i;
130 wxUint16 block_size;
131
132 sndformat.SetSampleRate(sample_fq);
133 sndformat.SetChannels(channels);
134
135 block_size = data.Read16();
136 coefs_len = data.Read16();
137
138 coefs[0] = new wxInt16[coefs_len];
139 coefs[1] = new wxInt16[coefs_len];
140
141 for (i=0;i<coefs_len;i++) {
142 coefs[0][i] = data.Read16();
143 coefs[1][i] = data.Read16();
144 }
145
146 sndformat.SetCoefs(coefs, 2, coefs_len);
147 sndformat.SetBlockSize(block_size);
148
149 delete[] coefs[0];
150 delete[] coefs[1];
151
152 if (!SetSoundFormat(sndformat))
153 return FALSE;
154
155 len -= coefs_len*4 + 4;
156
157 m_input->SeekI(len, wxFromCurrent);
158
159 return TRUE;
160 }
161
162 bool wxSoundWave::HandleOutputG721(wxDataInputStream& data, wxUint32 len,
163 wxUint16 channels,
164 wxUint32 sample_fq, wxUint32 byte_p_sec,
165 wxUint16 byte_p_spl, wxUint16 bits_p_spl)
166 {
167 wxSoundFormatG72X sndformat;
168
169 sndformat.SetSampleRate(sample_fq);
170 sndformat.SetG72XType(wxSOUND_G721);
171
172 if (!SetSoundFormat(sndformat))
173 return FALSE;
174
175 m_input->SeekI(len, wxFromCurrent);
176
177 return TRUE;
178 }
179
180 bool wxSoundWave::PrepareToPlay()
181 {
182 wxUint32 signature, len;
183 bool end_headers;
184
185 if (!m_input) {
186 m_snderror = wxSOUND_INVSTRM;
187 return FALSE;
188 }
189
190 wxDataInputStream data(*m_input);
191 data.BigEndianOrdered(FALSE);
192
193 // Get the first signature
194 FAIL_WITH(m_input->Read(&signature, 4).LastRead() != 4, wxSOUND_INVSTRM);
195 FAIL_WITH(wxUINT32_SWAP_ON_BE(signature) != RIFF_SIGNATURE, wxSOUND_INVSTRM);
196 // "RIFF"
197
198 len = data.Read32();
199 FAIL_WITH(m_input->LastRead() != 4, wxSOUND_INVSTRM);
200 // dummy len
201
202 // Get the second signature
203 FAIL_WITH(m_input->Read(&signature, 4).LastRead() != 4, wxSOUND_INVSTRM);
204 FAIL_WITH(wxUINT32_SWAP_ON_BE(signature) != WAVE_SIGNATURE, wxSOUND_INVSTRM);
205 // "WAVE"
206
207 end_headers = FALSE;
208 // Chunk loop
209 while (!end_headers) {
210 FAIL_WITH(m_input->Read(&signature, 4).LastRead() != 4, wxSOUND_INVSTRM);
211
212 len = data.Read32();
213 FAIL_WITH(m_input->LastRead() != 4, wxSOUND_INVSTRM);
214
215 switch (wxUINT32_SWAP_ON_BE(signature)) {
216 case FMT_SIGNATURE: { // "fmt "
217 wxUint16 format, channels, byte_p_spl, bits_p_spl;
218 wxUint32 sample_fq, byte_p_sec;
219
220 // Get the common parameters
221 data >> format >> channels >> sample_fq
222 >> byte_p_sec >> byte_p_spl >> bits_p_spl;
223 len -= 16;
224
225 switch (format) {
226 case 0x01: // PCM
227 if (!HandleOutputPCM(data, len, channels, sample_fq,
228 byte_p_sec, byte_p_spl,
229 bits_p_spl))
230 return FALSE;
231 break;
232 case 0x02: // MS ADPCM
233 if (!HandleOutputMSADPCM(data, len,
234 channels, sample_fq,
235 byte_p_sec, byte_p_spl,
236 bits_p_spl))
237 return FALSE;
238 break;
239 case 0x40: // G721
240 if (!HandleOutputG721(data, len,
241 channels, sample_fq,
242 byte_p_sec, byte_p_spl,
243 bits_p_spl))
244 return FALSE;
245 break;
246 default:
247 m_snderror = wxSOUND_NOCODEC;
248 return FALSE;
249 }
250 break;
251 }
252 case DATA_SIGNATURE: // "data"
253 m_base_offset = m_input->TellI();
254 end_headers = TRUE;
255 FinishPreparation(len);
256 break;
257 default:
258 // We pass the chunk
259 m_input->SeekI(len, wxFromCurrent);
260 break;
261 }
262 }
263 return TRUE;
264 }
265
266 wxSoundFormatBase *wxSoundWave::HandleInputPCM(wxDataOutputStream& data)
267 {
268 wxUint16 format, channels, byte_p_spl, bits_p_spl;
269 wxUint32 sample_fq, byte_p_sec;
270 wxSoundFormatPcm *pcm;
271
272 pcm = (wxSoundFormatPcm *)(m_sndformat->Clone());
273
274 // Write block length
275 data.Write32(16);
276
277 sample_fq = pcm->GetSampleRate();
278 bits_p_spl = pcm->GetBPS();
279 channels = pcm->GetChannels();
280 byte_p_spl = pcm->GetBPS() / 8;
281 byte_p_sec = pcm->GetBytesFromTime(1);
282 format = 0x01;
283
284 pcm->Signed(TRUE);
285 pcm->SetOrder(wxLITTLE_ENDIAN);
286
287 data << format << channels << sample_fq
288 << byte_p_sec << byte_p_spl << bits_p_spl;
289
290 return pcm;
291 }
292
293 wxSoundFormatBase *wxSoundWave::HandleInputG72X(wxDataOutputStream& data)
294 {
295 wxUint16 format, channels, byte_p_spl, bits_p_spl;
296 wxUint32 sample_fq, byte_p_sec;
297 wxSoundFormatG72X *g72x;
298
299 // Write block length
300 data.Write32(16);
301
302 g72x = (wxSoundFormatG72X *)(m_sndformat->Clone());
303 if (g72x->GetG72XType() != wxSOUND_G721) {
304 delete g72x;
305 return NULL;
306 }
307
308 sample_fq = g72x->GetSampleRate();
309 bits_p_spl = 4;
310 channels = 1;
311 byte_p_spl = 0;
312 byte_p_sec = g72x->GetBytesFromTime(1);
313 format = 0x40;
314 data << format << channels << sample_fq
315 << byte_p_sec << byte_p_spl << bits_p_spl;
316
317 return g72x;
318 }
319
320 bool wxSoundWave::PrepareToRecord(wxUint32 time)
321 {
322 #define WRITE_SIGNATURE(s,sig) \
323 signature = sig; \
324 signature = wxUINT32_SWAP_ON_BE(signature); \
325 FAIL_WITH(s->Write(&signature, 4).LastWrite() != 4, wxSOUND_INVSTRM);
326
327 wxUint32 signature;
328 wxMemoryOutputStream fmt_data;
329
330 if (!m_output) {
331 m_snderror = wxSOUND_INVSTRM;
332 return FALSE;
333 }
334
335 wxDataOutputStream data(*m_output);
336 wxDataOutputStream fmt_d_data(fmt_data);
337
338 data.BigEndianOrdered(FALSE);
339 fmt_d_data.BigEndianOrdered(FALSE);
340
341 WRITE_SIGNATURE(m_output, RIFF_SIGNATURE);
342
343 FAIL_WITH(m_output->LastWrite() != 4, wxSOUND_INVSTRM);
344
345 WRITE_SIGNATURE((&fmt_data), WAVE_SIGNATURE);
346
347 {
348 wxSoundFormatBase *frmt;
349
350 WRITE_SIGNATURE((&fmt_data), FMT_SIGNATURE);
351
352 switch (m_sndformat->GetType()) {
353 case wxSOUND_PCM:
354 frmt = HandleInputPCM(fmt_d_data);
355 break;
356 case wxSOUND_G72X:
357 frmt = HandleInputG72X(fmt_d_data);
358 break;
359 default:
360 m_snderror = wxSOUND_NOCODEC;
361 return FALSE;
362 }
363
364 FAIL_WITH(!frmt, wxSOUND_NOCODEC);
365
366 if (!SetSoundFormat(*frmt)) {
367 delete frmt;
368 return FALSE;
369 }
370
371 delete frmt;
372 }
373
374 data << (fmt_data.GetSize() + m_sndformat->GetBytesFromTime(time));
375
376 // We, finally, copy the header block to the output stream
377 {
378 char *out_buf;
379 out_buf = new char[fmt_data.GetSize()];
380
381 fmt_data.CopyTo(out_buf, fmt_data.GetSize());
382 m_output->Write(out_buf, fmt_data.GetSize());
383
384 delete[] out_buf;
385 }
386
387 WRITE_SIGNATURE(m_output, DATA_SIGNATURE);
388 data.Write32(m_sndformat->GetBytesFromTime(time));
389 return TRUE;
390 }
391
392 bool wxSoundWave::FinishRecording()
393 {
394 if (m_output->SeekO(0, wxFromStart) == wxInvalidOffset)
395 // We can't but there is no error.
396 return TRUE;
397
398 if (m_bytes_left == 0)
399 return TRUE;
400
401 // TODO: Update headers when we stop before the specified time (if possible)
402 return TRUE;
403 }
404
405 bool wxSoundWave::RepositionStream(wxUint32 position)
406 {
407 if (m_base_offset == wxInvalidOffset)
408 return FALSE;
409 m_input->SeekI(m_base_offset, wxFromStart);
410 return TRUE;
411 }
412
413 wxUint32 wxSoundWave::GetData(void *buffer, wxUint32 len)
414 {
415 return m_input->Read(buffer, len).LastRead();
416 }
417
418 wxUint32 wxSoundWave::PutData(const void *buffer, wxUint32 len)
419 {
420 return m_output->Write(buffer, len).LastWrite();
421 }