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