]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/mmedia/sndcpcm.cpp
simplified (hopefully:) and explained the shared lib versioning system.
[wxWidgets.git] / contrib / src / mmedia / sndcpcm.cpp
1 // --------------------------------------------------------------------------
2 // Name: sndcpcm.cpp
3 // Purpose:
4 // Date: 08/11/1999
5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999, 2000
6 // CVSID: $Id$
7 // --------------------------------------------------------------------------
8 #ifdef __GNUG__
9 #pragma implementation "sndcpcm.cpp"
10 #endif
11
12 #include "wx/wxprec.h"
13
14 #ifndef WX_PRECOMP
15 #include "wx/defs.h"
16 #include "wx/debug.h"
17 #include "wx/log.h"
18 #endif
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #include "wx/mmedia/sndbase.h"
25 #include "wx/mmedia/sndpcm.h"
26 #include "wx/mmedia/sndcpcm.h"
27
28 wxSoundStreamPcm::wxSoundStreamPcm(wxSoundStream& sndio)
29 : wxSoundStreamCodec(sndio)
30 {
31 m_function_in = NULL;
32 m_function_out = NULL;
33 m_prebuffer = NULL;
34 m_prebuffer_size = 0;
35 m_best_size = 0;
36 }
37
38 wxSoundStreamPcm::~wxSoundStreamPcm()
39 {
40 if (m_prebuffer)
41 delete[] m_prebuffer;
42 }
43
44 wxUint32 wxSoundStreamPcm::GetBestSize() const
45 {
46 return m_best_size;
47 }
48
49 // -----------------------------------------------------------------------
50 // "Converters" definitions/implementations
51 // -----------------------------------------------------------------------
52
53 #define DEFINE_CONV(name, input_type, output_type, convert) \
54 static void Convert_##name##(const void *buf_in, void *buf_out, wxUint32 len) \
55 {\
56 register input_type src; \
57 register const input_type *t_buf_in = (input_type *)buf_in; \
58 register output_type *t_buf_out = (output_type *)buf_out; \
59 \
60 while (len > 0) { \
61 src = *t_buf_in++; \
62 *t_buf_out++ = convert; \
63 len -= sizeof(input_type); \
64 } \
65 }
66
67 // TODO: define converters for all other formats (32, 24)
68
69 DEFINE_CONV(8_8_sign, wxUint8, wxUint8, (src ^ 0x80))
70
71 DEFINE_CONV(8_16, wxUint8, wxUint16, (((wxUint16)src) << 8))
72 DEFINE_CONV(8_16_swap, wxUint8, wxUint16, (src))
73 DEFINE_CONV(8_16_sign, wxUint8, wxUint16, (((wxUint16)(src ^ 0x80)) << 8))
74 DEFINE_CONV(8_16_sign_swap, wxUint8, wxUint16, (src ^ 0x80))
75
76 DEFINE_CONV(16_8, wxUint16, wxUint8, (wxUint8)(src >> 8))
77 DEFINE_CONV(16_8_sign, wxUint16, wxUint8, (wxUint8)((src >> 8) ^ 0x80))
78 DEFINE_CONV(16_swap_8, wxUint16, wxUint8, (wxUint8)(src & 0xff))
79 DEFINE_CONV(16_swap_8_sign, wxUint16, wxUint8, (wxUint8)((src & 0xff) ^ 0x80))
80
81 //DEFINE_CONV(24_8, wxUint32, wxUint8, (wxUint8)(src >> 16))
82 //DEFINE_CONV(24_8_sig, wxUint32, wxUint8, (wxUint8)((src >> 16) ^ 0x80))
83
84 //DEFINE_CONV(32_8, wxUint32, wxUint8, (wxUint8)(src >> 24))
85
86 DEFINE_CONV(16_sign, wxUint16, wxUint16, (src ^ 0x8000))
87 DEFINE_CONV(16_swap, wxUint16, wxUint16, (((src & 0xff) << 8) | ((src >> 8) & 0xff)))
88 // Problem.
89 DEFINE_CONV(16_swap_16_sign, wxUint16, wxUint16, ((((src & 0xff) << 8) | ((src >> 8) & 0xff)) ^ 0x80))
90 // DEFINE_CONV(16_sign_16_swap, wxUint16, wxUint16, ((((src & 0xff) << 8) | ((src >> 8) & 0xff)) ^ 0x8000))
91 DEFINE_CONV(16_swap_16_sign_swap, wxUint16, wxUint16, (src ^ 0x80))
92
93 // -----------------------------------------------------------------------
94 // Main PCM stream converter table
95 // -----------------------------------------------------------------------
96 // Definition
97 // XX -> YY
98 // XX -> YY sign
99 //
100 // XX swapped -> YY
101 // XX swapped -> YY sign
102 //
103 // XX swapped -> YY swapped
104 // XX swapped -> YY swapped sign
105 //
106 // XX stereo -> YY mono
107 // XX stereo -> YY mono sign
108 //
109 // XX swapped stereo -> YY swapped mono
110 // XX swapped stereo -> YY swapped mono sign
111 //
112 // XX swapped stereo -> YY swapped mono
113 // XX swapped stereo -> YY swapped mono sign
114
115 static wxSoundStreamPcm::ConverterType s_converters[4][3][2] = {
116 {
117 {
118 NULL,
119 Convert_8_8_sign /* 8 -> 8 sign */
120 },
121 {
122 NULL,
123 NULL
124 },
125 {
126 NULL,
127 NULL
128 }
129 },
130 {
131 {
132 Convert_8_16, /* 8 -> 16 */
133 Convert_8_16_sign /* 8 -> 16 sign */
134 },
135 {
136 Convert_8_16_swap, /* 8 -> 16 swapped */
137 Convert_8_16_sign_swap /* 8 -> 16 sign swapped */
138 },
139 {
140 NULL,
141 NULL
142 }
143 },
144 {
145 {
146 Convert_16_8, /* 16 -> 8 */
147 Convert_16_8_sign /* 16 -> 8 sign */
148 },
149 {
150 Convert_16_swap_8, /* 16 swapped -> 8 */
151 Convert_16_swap_8_sign /* 16 swapped -> 8 sign */
152 },
153 {
154 NULL,
155 NULL
156 },
157 },
158
159 {
160 {
161 NULL, /* 16 -> 16 */
162 Convert_16_sign /* 16 -> 16 sign */
163 },
164 {
165 Convert_16_swap, /* 16 swapped -> 16 */
166 Convert_16_swap_16_sign /* 16 swapped -> 16 sign */
167 },
168 {
169 NULL,
170 Convert_16_swap_16_sign_swap /* 16 swapped -> 16 sign swapped */
171 }
172 }
173 };
174
175 // This is the buffer size multiplier. It gives the needed size of the output size.
176 static float s_converters_multip[] = {1, 2, 0.5, 1};
177
178 //
179 // TODO: Read() and Write() aren't really safe. If you give it a buffer which
180 // is not aligned on 2, you may crash (See converter.def).
181 //
182
183 wxSoundStream& wxSoundStreamPcm::Read(void *buffer, wxUint32 len)
184 {
185 wxUint32 in_bufsize;
186
187 // We must have a multiple of 2
188 len &= 0x01;
189
190 if (!m_function_in) {
191 m_sndio->Read(buffer, len);
192 m_lastcount = m_sndio->GetLastAccess();
193 m_snderror = m_sndio->GetError();
194 return *this;
195 }
196
197 in_bufsize = GetReadSize(len);
198
199 if (len <= m_best_size) {
200 m_sndio->Read(m_prebuffer, in_bufsize);
201 m_snderror = m_sndio->GetError();
202 if (m_snderror != wxSOUND_NOERROR) {
203 m_lastcount = 0;
204 return *this;
205 }
206
207 m_function_in(m_prebuffer, buffer, m_sndio->GetLastAccess());
208 } else {
209 char *temp_buffer;
210
211 temp_buffer = new char[in_bufsize];
212 m_sndio->Read(temp_buffer, in_bufsize);
213
214 m_snderror = m_sndio->GetError();
215 if (m_snderror != wxSOUND_NOERROR) {
216 m_lastcount = 0;
217 return *this;
218 }
219
220 m_function_in(temp_buffer, buffer, m_sndio->GetLastAccess());
221
222 delete[] temp_buffer;
223 }
224
225 m_lastcount = (wxUint32)(m_sndio->GetLastAccess() * m_multiplier_in);
226
227 return *this;
228 }
229
230 wxSoundStream& wxSoundStreamPcm::Write(const void *buffer, wxUint32 len)
231 {
232 wxUint32 out_bufsize;
233
234 if (!m_function_out) {
235 m_sndio->Write(buffer, len);
236 m_lastcount = m_sndio->GetLastAccess();
237 m_snderror = m_sndio->GetError();
238 return *this;
239 }
240
241 out_bufsize = GetWriteSize(len);
242
243 if (len <= m_best_size) {
244 out_bufsize = GetWriteSize(len);
245
246 m_function_out(buffer, m_prebuffer, len);
247 m_sndio->Write(m_prebuffer, out_bufsize);
248 m_snderror = m_sndio->GetError();
249 if (m_snderror != wxSOUND_NOERROR) {
250 m_lastcount = 0;
251 return *this;
252 }
253 } else {
254 char *temp_buffer;
255
256 temp_buffer = new char[out_bufsize];
257 m_function_out(buffer, temp_buffer, len);
258
259 m_sndio->Write(temp_buffer, out_bufsize);
260 m_snderror = m_sndio->GetError();
261 if (m_snderror != wxSOUND_NOERROR) {
262 m_lastcount = 0;
263 return *this;
264 }
265
266 delete[] temp_buffer;
267 }
268
269 m_lastcount = (wxUint32)(m_sndio->GetLastAccess() / m_multiplier_out);
270
271 return *this;
272 }
273
274 bool wxSoundStreamPcm::SetSoundFormat(const wxSoundFormatBase& format)
275 {
276 wxSoundFormatBase *new_format;
277 wxSoundFormatPcm *pcm_format, *pcm_format2;
278
279 if (m_sndio->SetSoundFormat(format)) {
280 m_function_out = NULL;
281 m_function_in = NULL;
282 return TRUE;
283 }
284 if (format.GetType() != wxSOUND_PCM) {
285 m_snderror = wxSOUND_INVFRMT;
286 return FALSE;
287 }
288 if (m_sndformat)
289 delete m_sndformat;
290
291 new_format = m_sndio->GetSoundFormat().Clone();
292 pcm_format = (wxSoundFormatPcm *)&format;
293 pcm_format2 = (wxSoundFormatPcm *)new_format;
294
295 #if 0
296 // ----------------------------------------------------
297 // Test whether we need to resample
298 if (pcm_format->GetSampleRate() != pcm_format2->GetSampleRate()) {
299 wxUint32 src_rate, dst_rate;
300
301 src_rate = pcm_format->GetSampleRate();
302 dst_rate = pcm_format2->GetSampleRate();
303 m_needResampling = TRUE;
304 if (src_rate < dst_rate)
305 m_expandSamples = TRUE;
306 else
307 m_expandSamples = FALSE;
308 m_pitch = (src_rate << FLOATBITS) / dst_rate;
309 }
310 #endif
311 // ----------------------------------------------------
312 // Select table to use:
313 // * 8 bits -> 8 bits
314 // * 16 bits -> 8 bits
315 // * 8 bits -> 16 bits
316 // * 16 bits -> 16 bits
317
318 int table_no, table_no2;
319 int i_sign, i_swap;
320
321 switch (pcm_format->GetBPS()) {
322 case 8:
323 table_no = 0;
324 break;
325 case 16:
326 table_no = 1;
327 break;
328 default:
329 // TODO: Add something here: error, log, ...
330 return FALSE;
331 }
332 switch (pcm_format2->GetBPS()) {
333 case 8:
334 table_no2 = 0;
335 break;
336 case 16:
337 table_no2 = 1;
338 break;
339 default:
340 // TODO: Add something here: error, log, ...
341 return FALSE;
342 }
343
344 if (pcm_format2->Signed() != pcm_format->Signed())
345 i_sign = 1;
346 else
347 i_sign = 0;
348
349 #define MY_ORDER wxBYTE_ORDER
350 #if wxBYTE_ORDER == wxLITTLE_ENDIAN
351 #define OTHER_ORDER wxBIG_ENDIAN
352 #else
353 #define OTHER_ORDER wxLITTLE_ENDIAN
354 #endif
355
356 // --------------------------------------------------------
357 // Find the good converter !
358
359 if (pcm_format->GetOrder() == OTHER_ORDER) {
360 if (pcm_format->GetOrder() == pcm_format2->GetOrder())
361 i_swap = 2;
362 else
363 i_swap = 1;
364 } else {
365 if (pcm_format->GetOrder() == pcm_format2->GetOrder())
366 i_swap = 0;
367 else
368 i_swap = 1;
369 }
370
371 m_function_out = s_converters[table_no*2+table_no2][i_swap][i_sign];
372 m_function_in = s_converters[table_no2*2+table_no][i_swap][i_sign];
373 m_multiplier_out = s_converters_multip[table_no*2+table_no2];
374 m_multiplier_in = s_converters_multip[table_no2*2+table_no2];
375
376 if (m_prebuffer)
377 delete[] m_prebuffer;
378
379 // We try to minimize the need of dynamic memory allocation by preallocating a buffer. But
380 // to be sure it will be efficient we minimize the best size.
381 if (m_multiplier_in < m_multiplier_out) {
382 m_prebuffer_size = (wxUint32)(m_sndio->GetBestSize() *
383 m_multiplier_out);
384 m_best_size = (wxUint32)(m_sndio->GetBestSize() *
385 m_multiplier_in);
386 } else {
387 m_prebuffer_size = (wxUint32)(m_sndio->GetBestSize() *
388 m_multiplier_in);
389 m_best_size = (wxUint32)(m_sndio->GetBestSize() *
390 m_multiplier_out);
391 }
392
393 m_prebuffer = new char[m_prebuffer_size];
394
395 bool SetSoundFormatReturn;
396
397 SetSoundFormatReturn = m_sndio->SetSoundFormat(*new_format);
398 wxASSERT( SetSoundFormatReturn );
399
400 m_sndformat = new_format;
401 return TRUE;
402 }
403
404 wxUint32 wxSoundStreamPcm::GetWriteSize(wxUint32 len) const
405 {
406 // For the moment, it is simple but next time it will become more complicated
407 // (Resampling)
408 return (wxUint32)(len * m_multiplier_out);
409 }
410
411 wxUint32 wxSoundStreamPcm::GetReadSize(wxUint32 len) const
412 {
413 return (wxUint32)(len / m_multiplier_in);
414 }
415
416 // Resampling engine. NOT FINISHED and NOT INCLUDED but this is a first DRAFT.
417
418 #if 0
419
420 #define FLOATBITS 16
421 #define INTBITS 16
422 #define FLOATMASK 0xffff
423 #define INTMASK 0xffff0000
424
425 void ResamplingShrink_##DEPTH##(const void *source, void *destination, wxUint32 len)
426 {
427 wxUint##DEPTH## *source_data, *dest_data;
428 wxUint32 pos;
429
430 source_data = (wxUint##DEPTH## *)source;
431 dest_data = (wxUint##DEPTH## *)destination;
432
433 pos = m_saved_pos;
434 while (len > 0) {
435 // Increment the position in the input buffer
436 pos += m_pitch;
437 if (pos & INTMASK) {
438 pos &= FLOATMASK;
439
440 *dest_data ++ = *source_data;
441 }
442 len--;
443 source_data++;
444 }
445 m_saved_pos = pos;
446 }
447 #endif