]>
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$ | |
58b9c9ba | 7 | // wxWindows licence |
e8482f24 | 8 | // -------------------------------------------------------------------------- |
e8482f24 | 9 | |
92a19c2e | 10 | #include "wx/wxprec.h" |
c42b1de6 GL |
11 | |
12 | #ifndef WX_PRECOMP | |
13 | #include "wx/defs.h" | |
14 | #include "wx/memory.h" | |
15 | #include "wx/log.h" | |
16 | #endif | |
17 | ||
59917a74 JS |
18 | #include "wx/mmedia/sndbase.h" |
19 | #include "wx/mmedia/sndfile.h" | |
20 | #include "wx/mmedia/sndpcm.h" | |
21 | #include "wx/mmedia/sndmsad.h" | |
e8482f24 GL |
22 | |
23 | // -------------------------------------------------------------------------- | |
24 | // wxSoundFormatMSAdpcm | |
25 | // -------------------------------------------------------------------------- | |
26 | ||
27 | wxSoundFormatMSAdpcm::wxSoundFormatMSAdpcm() | |
28 | : m_srate(22050) | |
29 | { | |
c42b1de6 GL |
30 | m_ncoefs = 0; |
31 | m_coefs_len = 0; | |
32 | m_coefs = NULL; | |
e8482f24 GL |
33 | } |
34 | ||
35 | wxSoundFormatMSAdpcm::~wxSoundFormatMSAdpcm() | |
36 | { | |
c42b1de6 GL |
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 | ||
e8482f24 GL |
45 | } |
46 | ||
47 | void wxSoundFormatMSAdpcm::SetSampleRate(wxUint32 srate) | |
48 | { | |
49 | m_srate = srate; | |
50 | } | |
51 | ||
52 | wxUint32 wxSoundFormatMSAdpcm::GetSampleRate() const | |
53 | { | |
54 | return m_srate; | |
55 | } | |
56 | ||
c42b1de6 GL |
57 | void wxSoundFormatMSAdpcm::SetChannels(wxUint16 nchannels) |
58 | { | |
59 | m_nchannels = nchannels; | |
60 | } | |
61 | ||
62 | wxUint16 wxSoundFormatMSAdpcm::GetChannels() const | |
63 | { | |
64 | return m_nchannels; | |
65 | } | |
66 | ||
42c37dec | 67 | void wxSoundFormatMSAdpcm::SetCoefs(wxInt16 **WXUNUSED(coefs), wxUint16 ncoefs, |
c42b1de6 GL |
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 | |
b97a1a73 | 78 | m_coefs = new wxInt16 *[ncoefs]; |
c42b1de6 GL |
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 | ||
87 | void 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 | ||
95 | void wxSoundFormatMSAdpcm::SetBlockSize(wxUint16 block_size) | |
96 | { | |
97 | m_block_size = block_size; | |
98 | } | |
99 | ||
100 | wxUint16 wxSoundFormatMSAdpcm::GetBlockSize() const | |
101 | { | |
102 | return m_block_size; | |
103 | } | |
104 | ||
e8482f24 GL |
105 | wxSoundFormatBase *wxSoundFormatMSAdpcm::Clone() const |
106 | { | |
107 | wxSoundFormatMSAdpcm *adpcm = new wxSoundFormatMSAdpcm(); | |
108 | ||
c42b1de6 GL |
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; | |
e8482f24 GL |
113 | return adpcm; |
114 | } | |
115 | ||
116 | wxUint32 wxSoundFormatMSAdpcm::GetTimeFromBytes(wxUint32 bytes) const | |
117 | { | |
c42b1de6 | 118 | return 2 * bytes / (m_nchannels * m_srate); |
e8482f24 GL |
119 | } |
120 | ||
121 | wxUint32 wxSoundFormatMSAdpcm::GetBytesFromTime(wxUint32 time) const | |
122 | { | |
c42b1de6 | 123 | return time * m_nchannels * m_srate / 2; |
e8482f24 GL |
124 | } |
125 | ||
126 | bool wxSoundFormatMSAdpcm::operator !=(const wxSoundFormatBase& frmt2) const | |
127 | { | |
c42b1de6 | 128 | const wxSoundFormatMSAdpcm *adpcm = (const wxSoundFormatMSAdpcm *)&frmt2; |
e8482f24 GL |
129 | |
130 | if (frmt2.GetType() != wxSOUND_MSADPCM) | |
dea7e44a | 131 | return true; |
e8482f24 | 132 | |
c42b1de6 | 133 | return (adpcm->m_srate != m_srate) && (adpcm->m_nchannels != m_nchannels); |
e8482f24 GL |
134 | } |
135 | ||
136 | // -------------------------------------------------------------------------- | |
137 | // wxSoundStreamMSAdpcm | |
138 | // -------------------------------------------------------------------------- | |
139 | wxSoundStreamMSAdpcm::wxSoundStreamMSAdpcm(wxSoundStream& sndio) | |
140 | : wxSoundStreamCodec(sndio) | |
141 | { | |
142 | // PCM converter | |
143 | m_router = new wxSoundRouterStream(sndio); | |
dea7e44a WS |
144 | m_got_header = false; |
145 | m_stereo = false; | |
e8482f24 GL |
146 | } |
147 | ||
148 | wxSoundStreamMSAdpcm::~wxSoundStreamMSAdpcm() | |
149 | { | |
150 | delete m_router; | |
151 | } | |
152 | ||
42c37dec | 153 | wxSoundStream& wxSoundStreamMSAdpcm::Read(void *WXUNUSED(buffer), wxUint32 WXUNUSED(len)) |
e8482f24 GL |
154 | { |
155 | m_snderror = wxSOUND_NOCODEC; | |
156 | m_lastcount = 0; | |
157 | return *this; | |
158 | } | |
159 | ||
160 | static wxInt16 gl_ADPCMcoeff_delta[] = { | |
c42b1de6 GL |
161 | 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, |
162 | 230, 230, 230 | |
e8482f24 GL |
163 | }; |
164 | ||
c42b1de6 GL |
165 | wxUint32 wxSoundStreamMSAdpcm::DecodeMonoADPCM(const void *in_buffer, |
166 | void *out_buffer, | |
167 | wxUint32 in_len) | |
e8482f24 | 168 | { |
c42b1de6 GL |
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 | } | |
e8482f24 | 214 | } |
c42b1de6 GL |
215 | |
216 | return out_len; | |
217 | ||
218 | #undef GET_DATA_16 | |
219 | #undef GET_DATA_8 | |
220 | } | |
221 | ||
222 | wxUint32 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; | |
e8482f24 | 230 | |
c42b1de6 GL |
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); | |
e8482f24 | 248 | |
c42b1de6 GL |
249 | GET_DATA_16(state0->samp1); |
250 | GET_DATA_16(state1->samp1); | |
251 | GET_DATA_16(state0->samp2); | |
252 | GET_DATA_16(state1->samp2); | |
e8482f24 | 253 | |
c42b1de6 GL |
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 | ||
287 | void 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 | ||
323 | wxSoundStream& 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 | ||
e8482f24 GL |
343 | return *this; |
344 | } | |
345 | ||
346 | wxUint32 wxSoundStreamMSAdpcm::GetBestSize() const | |
347 | { | |
348 | return m_sndio->GetBestSize() / 2; | |
349 | } | |
350 | ||
351 | bool wxSoundStreamMSAdpcm::SetSoundFormat(const wxSoundFormatBase& format) | |
352 | { | |
c42b1de6 | 353 | if (format.GetType() != wxSOUND_MSADPCM) { |
e8482f24 | 354 | m_snderror = wxSOUND_INVFRMT; |
dea7e44a | 355 | return false; |
e8482f24 GL |
356 | } |
357 | ||
358 | wxSoundFormatPcm pcm; | |
c42b1de6 GL |
359 | wxSoundFormatMSAdpcm *adpcm; |
360 | wxUint16 ncoefs, coefs_len; | |
e8482f24 GL |
361 | |
362 | wxSoundStreamCodec::SetSoundFormat(format); | |
363 | ||
c42b1de6 GL |
364 | adpcm = (wxSoundFormatMSAdpcm *)m_sndformat; |
365 | ||
366 | adpcm->GetCoefs(m_coefs, ncoefs, coefs_len); | |
367 | ||
368 | if (!ncoefs) { | |
f815011f | 369 | wxLogError(wxT("Number of ADPCM coefficients must be non null")); |
dea7e44a | 370 | return false; |
c42b1de6 | 371 | } |
e8482f24 GL |
372 | |
373 | pcm.SetSampleRate(adpcm->GetSampleRate()); | |
374 | pcm.SetBPS(16); | |
375 | pcm.SetChannels(adpcm->GetChannels()); | |
dea7e44a | 376 | pcm.Signed(true); |
e8482f24 | 377 | pcm.SetOrder(wxBYTE_ORDER); |
c42b1de6 GL |
378 | |
379 | m_stereo = (adpcm->GetChannels() == 2); | |
380 | m_block_size = adpcm->GetBlockSize(); | |
381 | m_next_block = 0; | |
e8482f24 GL |
382 | |
383 | m_router->SetSoundFormat(pcm); | |
384 | ||
dea7e44a | 385 | return true; |
e8482f24 GL |
386 | } |
387 |