1// --------------------------------------------------------------------------
2// Name: sndulaw.cpp
3// Purpose:
4// Date: 08/11/1999
5// Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999
6// CVSID: $Id: sndmsad.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  #include "wx/memory.h"
15  #include "wx/log.h"
16#endif
17
18#include "wx/mmedia/sndbase.h"
19#include "wx/mmedia/sndfile.h"
20#include "wx/mmedia/sndpcm.h"
21#include "wx/mmedia/sndmsad.h"
22
23// --------------------------------------------------------------------------
24// wxSoundFormatMSAdpcm
25// --------------------------------------------------------------------------
26
27wxSoundFormatMSAdpcm::wxSoundFormatMSAdpcm()
28        : m_srate(22050)
29{
30    m_ncoefs    = 0;
31    m_coefs_len = 0;
32    m_coefs     = NULL;
33}
34
35wxSoundFormatMSAdpcm::~wxSoundFormatMSAdpcm()
36{
37    if (m_ncoefs) {
38        wxUint16 i;
39
40        for (i=0;i<m_ncoefs;i++)
41            delete[] m_coefs[i];
42        delete[] m_coefs;
43    }
44
45}
46
47void wxSoundFormatMSAdpcm::SetSampleRate(wxUint32 srate)
48{
49    m_srate = srate;
50}
51
52wxUint32 wxSoundFormatMSAdpcm::GetSampleRate() const
53{
54    return m_srate;
55}
56
57void wxSoundFormatMSAdpcm::SetChannels(wxUint16 nchannels)
58{
59    m_nchannels = nchannels;
60}
61
62wxUint16 wxSoundFormatMSAdpcm::GetChannels() const
63{
64    return m_nchannels;
65}
66
67void wxSoundFormatMSAdpcm::SetCoefs(wxInt16 **WXUNUSED(coefs), wxUint16 ncoefs,
68                                    wxUint16 coefs_len)
69{
70    wxUint16 i;
71
72    if (m_ncoefs) {
73        for (i=0;i<m_ncoefs;i++)
74           delete[] (m_coefs[i]);
75        delete[] m_coefs;
76    }
77    // TODO: Add some memory checking here
78    m_coefs = new wxInt16 *[ncoefs];
79
80    for (i=0;i<ncoefs;i++)
81       m_coefs[i] = new wxInt16[coefs_len];
82
83    m_ncoefs = ncoefs;
84    m_coefs_len = coefs_len;
85}
86
87void wxSoundFormatMSAdpcm::GetCoefs(wxInt16 **& coefs, wxUint16& ncoefs,
88                                    wxUint16& coefs_len) const
89{
90    coefs     = m_coefs;
91    ncoefs    = m_ncoefs;
92    coefs_len = m_coefs_len;
93}
94
95void wxSoundFormatMSAdpcm::SetBlockSize(wxUint16 block_size)
96{
97    m_block_size = block_size;
98}
99
100wxUint16 wxSoundFormatMSAdpcm::GetBlockSize() const
101{
102    return m_block_size;
103}
104
105wxSoundFormatBase *wxSoundFormatMSAdpcm::Clone() const
106{
107    wxSoundFormatMSAdpcm *adpcm = new wxSoundFormatMSAdpcm();
108
109    adpcm->m_srate       = m_srate;
110    adpcm->SetCoefs(m_coefs, m_ncoefs, m_coefs_len);
111    adpcm->m_nchannels   = m_nchannels;
112    adpcm->m_block_size  = m_block_size;
113    return adpcm;
114}
115
116wxUint32 wxSoundFormatMSAdpcm::GetTimeFromBytes(wxUint32 bytes) const
117{
118    return 2 * bytes / (m_nchannels * m_srate);
119}
120
121wxUint32 wxSoundFormatMSAdpcm::GetBytesFromTime(wxUint32 time) const
122{
123    return time * m_nchannels * m_srate / 2;
124}
125
126bool wxSoundFormatMSAdpcm::operator !=(const wxSoundFormatBase& frmt2) const
127{
128    const wxSoundFormatMSAdpcm *adpcm = (const wxSoundFormatMSAdpcm *)&frmt2;
129
130    if (frmt2.GetType() != wxSOUND_MSADPCM)
131        return true;
132
133    return (adpcm->m_srate != m_srate) && (adpcm->m_nchannels != m_nchannels);
134}
135
136// --------------------------------------------------------------------------
137// wxSoundStreamMSAdpcm
138// --------------------------------------------------------------------------
139wxSoundStreamMSAdpcm::wxSoundStreamMSAdpcm(wxSoundStream& sndio)
140        : wxSoundStreamCodec(sndio)
141{
142    // PCM converter
143    m_router     = new wxSoundRouterStream(sndio);
144    m_got_header = false;
145    m_stereo = false;
146}
147
148wxSoundStreamMSAdpcm::~wxSoundStreamMSAdpcm()
149{
150    delete m_router;
151}
152
153wxSoundStream& wxSoundStreamMSAdpcm::Read(void *WXUNUSED(buffer), wxUint32 WXUNUSED(len))
154{
155    m_snderror = wxSOUND_NOCODEC;
156    m_lastcount = 0;
157    return *this;
158}
159
160static wxInt16 gl_ADPCMcoeff_delta[] = {
161    230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307,
162    230, 230, 230
163};
164
165wxUint32 wxSoundStreamMSAdpcm::DecodeMonoADPCM(const void *in_buffer,
166                                               void *out_buffer,
167                                               wxUint32 in_len)
168{
169    wxUint8  *ADPCMdata;
170    wxInt16  *PCMdata;
171    AdpcmState *state;
172    wxUint32 out_len;
173
174    ADPCMdata = (wxUint8 *)in_buffer;
175    PCMdata   = (wxInt16 *)out_buffer;
176    state     = &m_state[0];
177
178#define GET_DATA_16(i) i = *ADPCMdata++, i |= ((wxUint32)(*ADPCMdata++) << 8)
179#define GET_DATA_8(i) i = (*ADPCMdata++)
180
181    out_len = 0;
182    while (in_len != 0) {
183        if (m_next_block == 0) {
184            GET_DATA_8(state->predictor);
185            GET_DATA_16(state->iDelta);
186
187            GET_DATA_16(state->samp1);
188            GET_DATA_16(state->samp2);
189
190            state->coeff[0] = state->coeff[1] = m_coefs[0][ state->predictor ];
191
192            *PCMdata++ = state->samp2;
193            *PCMdata++ = state->samp1;
194            in_len     -= 7;
195            out_len    += 4;
196            m_next_block = m_block_size;
197            continue;
198        }
199
200        while (in_len != 0 && m_next_block != 0) {
201            wxUint8 nib[2];
202
203            GET_DATA_8(nib[0]);
204            nib[1] = (nib[0] >> 4) & 0x0f;
205            nib[0] &= 0x0f;
206
207            Nibble(nib[0], state, &PCMdata);
208            Nibble(nib[1], state, &PCMdata);
209
210            in_len       -= 4;
211            out_len      += 4;
212            m_next_block -= 4;
213        }
214    }
215
216    return out_len;
217
218#undef GET_DATA_16
219#undef GET_DATA_8
220}
221
222wxUint32 wxSoundStreamMSAdpcm::DecodeStereoADPCM(const void *in_buffer,
223                                                 void *out_buffer,
224                                                 wxUint32 in_len)
225{
226    wxUint8  *ADPCMdata;
227    wxInt16  *PCMdata;
228    AdpcmState *state0, *state1;
229    wxUint32 out_len;
230
231    ADPCMdata = (wxUint8 *)in_buffer;
232    PCMdata   = (wxInt16 *)out_buffer;
233
234    state0 = &m_state[0];
235    state1 = &m_state[1];
236
237#define GET_DATA_16(i) i = *ADPCMdata++, i |= ((wxUint32)(*ADPCMdata++) << 8)
238#define GET_DATA_8(i) i = (*ADPCMdata++)
239
240    out_len = 0;
241    while (in_len != 0) {
242        if (!m_next_block) {
243            GET_DATA_8(state0->predictor);
244            GET_DATA_8(state1->predictor);
245
246            GET_DATA_16(state0->iDelta);
247            GET_DATA_16(state1->iDelta);
248
249            GET_DATA_16(state0->samp1);
250            GET_DATA_16(state1->samp1);
251            GET_DATA_16(state0->samp2);
252            GET_DATA_16(state1->samp2);
253
254            *PCMdata++ = state0->samp2;
255            *PCMdata++ = state1->samp2;
256            *PCMdata++ = state0->samp1;
257            *PCMdata++ = state1->samp1;
258
259            in_len     -= 14;
260            out_len    += 8;
261            m_next_block = m_block_size;
262            continue;
263        }
264
265        while (in_len != 0 && m_next_block > 0) {
266            wxUint8 nib[2];
267
268            GET_DATA_8(nib[0]);
269            nib[1] = (nib[0] >> 4) & 0x0f;
270            nib[0] &= 0x0f;
271
272            Nibble(nib[0], state0, &PCMdata);
273            Nibble(nib[1], state1, &PCMdata);
274
275            in_len       -= 4;
276            out_len      += 4;
277            m_next_block -= 4;
278        }
279    }
280
281    return out_len;
282
283#undef GET_DATA_16
284#undef GET_DATA_8
285}
286
287void wxSoundStreamMSAdpcm::Nibble(wxInt8 nyb,
288                                  AdpcmState *state,
289                                  wxInt16 **out_buffer)
290{
291    wxUint32 new_delta;
292    wxInt32  new_sample;
293
294    // First: compute the next delta value
295    new_delta  = (state->iDelta * gl_ADPCMcoeff_delta[nyb]) >> 8;
296    // If null, minor it by 16
297    if (!new_delta)
298        new_delta = 16;
299
300    // Barycentre
301    new_sample = (state->samp1 * state->coeff[0] +
302                  state->samp2 * state->coeff[1]) / 256;
303
304    // Regenerate the sign
305    if (nyb & 0x08)
306        nyb -= 0x10;
307
308    new_sample += state->iDelta * nyb;
309
310    // Samples must be in [-32767, 32768]
311    if (new_sample < -32768)
312        new_sample = -32768;
313    else if (new_sample > 32767)
314        new_sample = 32767;
315
316    state->iDelta = new_delta;
317    state->samp2  = state->samp1;
318    state->samp1  = new_sample;
319
320    *(*out_buffer)++ = new_sample;
321}
322
323wxSoundStream& wxSoundStreamMSAdpcm::Write(const void *buffer, wxUint32 len)
324{
325    wxUint8 *out_buf;
326    wxUint32 new_len;
327
328    // TODO: prealloc the output buffer
329    out_buf = new wxUint8[len*2];
330
331    if (!m_stereo)
332        new_len = DecodeMonoADPCM(buffer, out_buf, len);
333    else
334        new_len = DecodeStereoADPCM(buffer, out_buf, len);
335
336    m_router->Write(out_buf, new_len);
337
338    m_lastcount = len;
339    m_snderror  = wxSOUND_NOERROR;
340
341    delete[] out_buf;
342
343    return *this;
344}
345
346wxUint32 wxSoundStreamMSAdpcm::GetBestSize() const
347{
348    return m_sndio->GetBestSize() / 2;
349}
350
351bool wxSoundStreamMSAdpcm::SetSoundFormat(const wxSoundFormatBase& format)
352{
353    if (format.GetType() != wxSOUND_MSADPCM) {
354        m_snderror = wxSOUND_INVFRMT;
355        return false;
356    }
357
358    wxSoundFormatPcm pcm;
359    wxSoundFormatMSAdpcm *adpcm;
360    wxUint16 ncoefs, coefs_len;
361
362    wxSoundStreamCodec::SetSoundFormat(format);
363
364    adpcm = (wxSoundFormatMSAdpcm *)m_sndformat;
365
366    adpcm->GetCoefs(m_coefs, ncoefs, coefs_len);
367
368    if (!ncoefs) {
369        wxLogError(wxT("Number of ADPCM coefficients must be non null"));
370        return false;
371    }
372
373    pcm.SetSampleRate(adpcm->GetSampleRate());
374    pcm.SetBPS(16);
375    pcm.SetChannels(adpcm->GetChannels());
376    pcm.Signed(true);
377    pcm.SetOrder(wxBYTE_ORDER);
378
379    m_stereo = (adpcm->GetChannels() == 2);
380    m_block_size = adpcm->GetBlockSize();
381    m_next_block = 0;
382
383    m_router->SetSoundFormat(pcm);
384
385    return true;
386}
387
388