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