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