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