Fix a crash when launching Unix executables.
[wine/multimedia.git] / dlls / msacm / pcmconverter.c
blob3f335d005cebb99fa8c39b306330b71fe40872bd
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * MSACM32 library
6 * Copyright 2000 Eric Pouech
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * FIXME / TODO list
23 * + most of the computation should be done in fixed point arithmetic
24 * instead of floating point (16 bits for integral part, and 16 bits
25 * for fractional part for example)
26 * + implement PCM_FormatSuggest function
27 * + get rid of hack for PCM_DriverProc (msacm32.dll shouldn't export
28 * a DriverProc, but this would require implementing a generic
29 * embedded driver handling scheme in msacm32.dll which isn't done yet
32 #include "config.h"
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <string.h>
38 #include "windef.h"
39 #include "winbase.h"
40 #include "mmsystem.h"
41 #include "mmreg.h"
42 #include "msacm.h"
43 #include "wingdi.h"
44 #include "winnls.h"
45 #include "winuser.h"
47 #include "msacmdrv.h"
48 #include "wineacm.h"
50 #include "wine/debug.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
54 /***********************************************************************
55 * PCM_drvOpen
57 static DWORD PCM_drvOpen(LPCSTR str, PACMDRVOPENDESCW adod)
59 TRACE("(%p, %p)\n", str, adod);
61 return (adod == NULL) ||
62 (adod->fccType == ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC &&
63 adod->fccComp == ACMDRIVERDETAILS_FCCCOMP_UNDEFINED);
66 /***********************************************************************
67 * PCM_drvClose
69 static DWORD PCM_drvClose(DWORD dwDevID)
71 TRACE("(%ld)\n", dwDevID);
73 return 1;
76 #define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
77 #define NUM_OF(a,b) (((a)+(b)-1)/(b))
79 /* flags for fdwDriver */
80 #define PCM_RESAMPLE 1
82 /* data used while converting */
83 typedef struct tagAcmPcmData {
84 /* conversion routine, depending if rate conversion is required */
85 union {
86 void (*cvtKeepRate)(const unsigned char*, int, unsigned char*);
87 void (*cvtChangeRate)(struct tagAcmPcmData*, const unsigned char*,
88 LPDWORD, unsigned char*, LPDWORD);
89 } cvt;
90 /* the following fields are used only with rate conversion) */
91 DWORD srcPos; /* position in source stream */
92 double dstPos; /* position in destination stream */
93 double dstIncr; /* value to increment dst stream when src stream
94 is incremented by 1 */
95 /* last source stream value read */
96 union {
97 unsigned char b; /* 8 bit value */
98 short s; /* 16 bit value */
99 } last[2]; /* two channels max (stereo) */
100 } AcmPcmData;
102 /* table to list all supported formats... those are the basic ones. this
103 * also helps given a unique index to each of the supported formats
105 static struct {
106 int nChannels;
107 int nBits;
108 int rate;
109 } PCM_Formats[] = {
110 {1, 8, 8000}, {2, 8, 8000}, {1, 16, 8000}, {2, 16, 8000},
111 {1, 8, 11025}, {2, 8, 11025}, {1, 16, 11025}, {2, 16, 11025},
112 {1, 8, 22050}, {2, 8, 22050}, {1, 16, 22050}, {2, 16, 22050},
113 {1, 8, 44100}, {2, 8, 44100}, {1, 16, 44100}, {2, 16, 44100},
114 {1, 8, 48000}, {2, 8, 48000}, {1, 16, 48000}, {2, 16, 48000},
115 {1, 8, 96000}, {2, 8, 96000}, {1, 16, 96000}, {2, 16, 96000}
118 /***********************************************************************
119 * PCM_GetFormatIndex
121 static DWORD PCM_GetFormatIndex(LPWAVEFORMATEX wfx)
123 int i;
124 TRACE("(%p)\n", wfx);
126 for (i = 0; i < NUM_PCM_FORMATS; i++) {
127 if (wfx->nChannels == PCM_Formats[i].nChannels &&
128 wfx->nSamplesPerSec == PCM_Formats[i].rate &&
129 wfx->wBitsPerSample == PCM_Formats[i].nBits)
130 return i;
132 return 0xFFFFFFFF;
135 /* PCM Conversions:
137 * parameters:
138 * + 8 bit unsigned vs 16 bit signed
139 * + mono vs stereo (1 or 2 channels)
140 * + sampling rate (8.0, 11.025, 22.05, 44.1 kHz are defined, but algo
141 * shall work in all cases)
143 * mono => stereo: copy the same sample on Left & Right channels
144 * stereo =) mono: use the average value of samples from Left & Right channels
145 * resampling; we lookup for each destination sample the two source adjacent
146 * samples were src <= dst < src+1 (dst is increased by a fractional
147 * value which is equivalent to the increment by one on src); then we
148 * use a linear interpolation between src and src+1
151 /***********************************************************************
152 * C816
154 * Converts a 8 bit sample to a 16 bit one
156 static inline short C816(unsigned char b)
158 return (short)((b+(b << 8))-32768);
161 /***********************************************************************
162 * C168
164 * Converts a 16 bit sample to a 8 bit one (data loss !!)
166 static inline unsigned char C168(short s)
168 return HIBYTE(s) ^ (unsigned char)0x80;
171 /***********************************************************************
172 * R16
174 * Read a 16 bit sample (correctly handles endianess)
176 static inline short R16(const unsigned char* src)
178 return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
181 /***********************************************************************
182 * W16
184 * Write a 16 bit sample (correctly handles endianess)
186 static inline void W16(unsigned char* dst, short s)
188 dst[0] = LOBYTE(s);
189 dst[1] = HIBYTE(s);
192 /***********************************************************************
193 * M16
195 * Convert the (l,r) 16 bit stereo sample into a 16 bit mono
196 * (takes the mid-point of the two values)
198 static inline short M16(short l, short r)
200 return (l + r) / 2;
203 /***********************************************************************
204 * M8
206 * Convert the (l,r) 8 bit stereo sample into a 8 bit mono
207 * (takes the mid-point of the two values)
209 static inline unsigned char M8(unsigned char a, unsigned char b)
211 return (unsigned char)((a + b) / 2);
214 /* the conversion routines without rate conversion are labelled cvt<X><Y><N><M>K
215 * where :
216 * <X> is the (M)ono/(S)tereo configuration of input channel
217 * <Y> is the (M)ono/(S)tereo configuration of output channel
218 * <N> is the number of bits of input channel (8 or 16)
219 * <M> is the number of bits of output channel (8 or 16)
221 * in the parameters, ns is always the number of samples, so the size of input
222 * buffer (resp output buffer) is ns * (<X> == 'Mono' ? 1:2) * (<N> == 8 ? 1:2)
225 static void cvtMM88K(const unsigned char* src, int ns, unsigned char* dst)
227 TRACE("(%p, %d, %p)\n", src, ns, dst);
228 memcpy(dst, src, ns);
231 static void cvtSS88K(const unsigned char* src, int ns, unsigned char* dst)
233 TRACE("(%p, %d, %p)\n", src, ns, dst);
234 memcpy(dst, src, ns * 2);
237 static void cvtMM1616K(const unsigned char* src, int ns, unsigned char* dst)
239 TRACE("(%p, %d, %p)\n", src, ns, dst);
240 memcpy(dst, src, ns * 2);
243 static void cvtSS1616K(const unsigned char* src, int ns, unsigned char* dst)
245 TRACE("(%p, %d, %p)\n", src, ns, dst);
246 memcpy(dst, src, ns * 4);
249 static void cvtMS88K(const unsigned char* src, int ns, unsigned char* dst)
251 TRACE("(%p, %d, %p)\n", src, ns, dst);
253 while (ns--) {
254 *dst++ = *src;
255 *dst++ = *src++;
259 static void cvtMS816K(const unsigned char* src, int ns, unsigned char* dst)
261 short v;
262 TRACE("(%p, %d, %p)\n", src, ns, dst);
264 while (ns--) {
265 v = C816(*src++);
266 W16(dst, v); dst += 2;
267 W16(dst, v); dst += 2;
271 static void cvtMS168K(const unsigned char* src, int ns, unsigned char* dst)
273 unsigned char v;
274 TRACE("(%p, %d, %p)\n", src, ns, dst);
276 while (ns--) {
277 v = C168(R16(src)); src += 2;
278 *dst++ = v;
279 *dst++ = v;
283 static void cvtMS1616K(const unsigned char* src, int ns, unsigned char* dst)
285 short v;
286 TRACE("(%p, %d, %p)\n", src, ns, dst);
288 while (ns--) {
289 v = R16(src); src += 2;
290 W16(dst, v); dst += 2;
291 W16(dst, v); dst += 2;
295 static void cvtSM88K(const unsigned char* src, int ns, unsigned char* dst)
297 TRACE("(%p, %d, %p)\n", src, ns, dst);
299 while (ns--) {
300 *dst++ = M8(src[0], src[1]);
301 src += 2;
305 static void cvtSM816K(const unsigned char* src, int ns, unsigned char* dst)
307 short v;
308 TRACE("(%p, %d, %p)\n", src, ns, dst);
310 while (ns--) {
311 v = M16(C816(src[0]), C816(src[1]));
312 src += 2;
313 W16(dst, v); dst += 2;
317 static void cvtSM168K(const unsigned char* src, int ns, unsigned char* dst)
319 TRACE("(%p, %d, %p)\n", src, ns, dst);
321 while (ns--) {
322 *dst++ = C168(M16(R16(src), R16(src + 2)));
323 src += 4;
327 static void cvtSM1616K(const unsigned char* src, int ns, unsigned char* dst)
329 TRACE("(%p, %d, %p)\n", src, ns, dst);
331 while (ns--) {
332 W16(dst, M16(R16(src),R16(src+2))); dst += 2;
333 src += 4;
337 static void cvtMM816K(const unsigned char* src, int ns, unsigned char* dst)
339 TRACE("(%p, %d, %p)\n", src, ns, dst);
341 while (ns--) {
342 W16(dst, C816(*src++)); dst += 2;
346 static void cvtSS816K(const unsigned char* src, int ns, unsigned char* dst)
348 TRACE("(%p, %d, %p)\n", src, ns, dst);
350 while (ns--) {
351 W16(dst, C816(*src++)); dst += 2;
352 W16(dst, C816(*src++)); dst += 2;
356 static void cvtMM168K(const unsigned char* src, int ns, unsigned char* dst)
358 TRACE("(%p, %d, %p)\n", src, ns, dst);
360 while (ns--) {
361 *dst++ = C168(R16(src)); src += 2;
365 static void cvtSS168K(const unsigned char* src, int ns, unsigned char* dst)
367 TRACE("(%p, %d, %p)\n", src, ns, dst);
369 while (ns--) {
370 *dst++ = C168(R16(src)); src += 2;
371 *dst++ = C168(R16(src)); src += 2;
375 static void (*PCM_ConvertKeepRate[16])(const unsigned char*, int, unsigned char*) = {
376 cvtSS88K, cvtSM88K, cvtMS88K, cvtMM88K,
377 cvtSS816K, cvtSM816K, cvtMS816K, cvtMM816K,
378 cvtSS168K, cvtSM168K, cvtMS168K, cvtMM168K,
379 cvtSS1616K, cvtSM1616K, cvtMS1616K, cvtMM1616K,
382 /***********************************************************************
385 * Interpolate the value at r (r in ]0, 1]) between the two points v1 and v2
386 * Linear interpolation is used
388 static inline double I(double v1, double v2, double r)
390 if (0.0 >= r || r > 1.0) FIXME("r!! %f\n", r);
391 return (1.0 - r) * v1 + r * v2;
394 static void cvtSS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
395 unsigned char* dst, LPDWORD ndst)
397 double r;
398 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
400 while (*nsrc != 0 && *ndst != 0) {
401 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
402 if (*nsrc == 0) return;
403 apd->last[0].b = *src++;
404 apd->last[1].b = *src++;
405 apd->srcPos++;
406 (*nsrc)--;
408 /* now do the interpolation */
409 *dst++ = I(apd->last[0].b, src[0], r);
410 *dst++ = I(apd->last[1].b, src[1], r);
411 apd->dstPos += apd->dstIncr;
412 (*ndst)--;
416 /* the conversion routines with rate conversion are labelled cvt<X><Y><N><M>C
417 * where :
418 * <X> is the (M)ono/(S)tereo configuration of input channel
419 * <Y> is the (M)ono/(S)tereo configuration of output channel
420 * <N> is the number of bits of input channel (8 or 16)
421 * <M> is the number of bits of output channel (8 or 16)
424 static void cvtSM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
425 unsigned char* dst, LPDWORD ndst)
427 double r;
428 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
430 while (*nsrc != 0 && *ndst != 0) {
431 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
432 if (*nsrc == 0) return;
433 apd->last[0].b = *src++;
434 apd->last[1].b = *src++;
435 apd->srcPos++;
436 (*nsrc)--;
438 /* now do the interpolation */
439 if (*nsrc) /* don't go off end of data */
440 *dst++ = I(M8(apd->last[0].b, apd->last[1].b), M8(src[0], src[1]), r);
441 else
442 *dst++ = M8(apd->last[0].b, apd->last[1].b);
443 apd->dstPos += apd->dstIncr;
444 (*ndst)--;
448 static void cvtMS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
449 unsigned char* dst, LPDWORD ndst)
451 double r;
452 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
454 while (*nsrc != 0 && *ndst != 0) {
455 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
456 if (*nsrc == 0) return;
457 apd->last[0].b = *src++;
458 apd->srcPos++;
459 (*nsrc)--;
461 /* now do the interpolation */
462 if (*nsrc) /* don't go off end of data */
463 dst[0] = dst[1] = I(apd->last[0].b, src[0], r);
464 else
465 dst[0] = dst[1] = apd->last[0].b;
466 dst += 2;
467 apd->dstPos += apd->dstIncr;
468 (*ndst)--;
472 static void cvtMM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
473 unsigned char* dst, LPDWORD ndst)
475 double r;
476 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
478 while (*nsrc != 0 && *ndst != 0) {
479 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
480 if (*nsrc == 0) return;
481 apd->last[0].b = *src++;
482 apd->srcPos++;
483 (*nsrc)--;
485 /* now do the interpolation */
486 if (*nsrc) /* don't go off end of data */
487 *dst++ = I(apd->last[0].b, src[0], r);
488 else
489 *dst++ = apd->last[0].b;
490 apd->dstPos += apd->dstIncr;
491 (*ndst)--;
495 static void cvtSS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
496 unsigned char* dst, LPDWORD ndst)
498 double r;
499 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
501 while (*nsrc != 0 && *ndst != 0) {
502 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
503 if (*nsrc == 0) return;
504 apd->last[0].b = *src++;
505 apd->last[1].b = *src++;
506 apd->srcPos++;
507 (*nsrc)--;
509 /* now do the interpolation */
510 if (*nsrc) /* don't go off end of data */
511 W16(dst, I(C816(apd->last[0].b), C816(src[0]), r));
512 else
513 W16(dst, C816(apd->last[0].b));
514 dst += 2;
515 if (*nsrc) /* don't go off end of data */
516 W16(dst, I(C816(apd->last[1].b), C816(src[1]), r));
517 else
518 W16(dst, C816(apd->last[1].b));
519 dst += 2;
520 apd->dstPos += apd->dstIncr;
521 (*ndst)--;
525 static void cvtSM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
526 unsigned char* dst, LPDWORD ndst)
528 double r;
529 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
531 while (*nsrc != 0 && *ndst != 0) {
532 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
533 if (*nsrc == 0) return;
534 apd->last[0].b = *src++;
535 apd->last[1].b = *src++;
536 apd->srcPos++;
537 (*nsrc)--;
539 /* now do the interpolation */
540 if (*nsrc) /* don't go off end of data */
541 W16(dst, I(M16(C816(apd->last[0].b), C816(apd->last[1].b)),
542 M16(C816(src[0]), C816(src[1])), r));
543 else
544 W16(dst, M16(C816(apd->last[0].b), C816(apd->last[1].b)));
545 dst += 2;
546 apd->dstPos += apd->dstIncr;
547 (*ndst)--;
551 static void cvtMS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
552 unsigned char* dst, LPDWORD ndst)
554 double r;
555 short v;
556 TRACE("(%p, %p, %p->(%ld), %p, %p->(%ld))\n", apd, src, nsrc, *nsrc, dst, ndst, *ndst);
558 while (*nsrc != 0 && *ndst != 0) {
559 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
560 if (*nsrc == 0) return;
561 apd->last[0].b = *src++;
562 apd->srcPos++;
563 (*nsrc)--;
565 /* now do the interpolation */
566 if (*nsrc) /* don't go off end of data */
567 v = I(C816(apd->last[0].b), C816(src[0]), r);
568 else
569 v = C816(apd->last[0].b);
570 W16(dst, v); dst += 2;
571 W16(dst, v); dst += 2;
572 apd->dstPos += apd->dstIncr;
573 (*ndst)--;
577 static void cvtMM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
578 unsigned char* dst, LPDWORD ndst)
580 double r;
581 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
583 while (*nsrc != 0 && *ndst != 0) {
584 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
585 if (*nsrc == 0) return;
586 apd->last[0].b = *src++;
587 apd->srcPos++;
588 (*nsrc)--;
590 /* now do the interpolation */
591 if (*nsrc) /* don't go off end of data */
592 W16(dst, I(C816(apd->last[0].b), C816(src[0]), r));
593 else
594 W16(dst, C816(apd->last[0].b));
595 dst += 2;
596 apd->dstPos += apd->dstIncr;
597 (*ndst)--;
601 static void cvtSS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
602 unsigned char* dst, LPDWORD ndst)
604 double r;
605 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
607 while (*nsrc != 0 && *ndst != 0) {
608 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
609 if (*nsrc == 0) return;
610 apd->last[0].s = R16(src); src += 2;
611 apd->last[1].s = R16(src); src += 2;
612 apd->srcPos++;
613 (*nsrc)--;
615 /* now do the interpolation */
616 if (*nsrc) { /* don't go off end of data */
617 *dst++ = C168(I(apd->last[0].s, R16(src) , r));
618 *dst++ = C168(I(apd->last[1].s, R16(src+2), r));
619 } else {
620 *dst++ = C168(apd->last[0].s);
621 *dst++ = C168(apd->last[1].s);
623 apd->dstPos += apd->dstIncr;
624 (*ndst)--;
628 static void cvtSM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
629 unsigned char* dst, LPDWORD ndst)
631 double r;
632 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
634 while (*nsrc != 0 && *ndst != 0) {
635 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
636 if (*nsrc == 0) return;
637 apd->last[0].s = R16(src); src += 2;
638 apd->last[1].s = R16(src); src += 2;
639 apd->srcPos++;
640 (*nsrc)--;
642 /* now do the interpolation */
643 if (*nsrc) /* don't go off end of data */
644 *dst++ = C168(I(M16(apd->last[0].s, apd->last[1].s),
645 M16(R16(src), R16(src + 2)), r));
646 else
647 *dst++ = C168(M16(apd->last[0].s, apd->last[1].s));
648 apd->dstPos += apd->dstIncr;
649 (*ndst)--;
654 static void cvtMS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
655 unsigned char* dst, LPDWORD ndst)
657 double r;
658 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
660 while (*nsrc != 0 && *ndst != 0) {
661 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
662 if (*nsrc == 0) return;
663 apd->last[0].s = R16(src); src += 2;
664 apd->srcPos++;
665 (*nsrc)--;
667 /* now do the interpolation */
668 if (*nsrc) /* don't go off end of data */
669 dst[0] = dst[1] = C168(I(apd->last[0].s, R16(src), r));
670 else
671 dst[0] = dst[1] = C168(apd->last[0].s);
672 dst += 2;
673 apd->dstPos += apd->dstIncr;
674 (*ndst)--;
679 static void cvtMM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
680 unsigned char* dst, LPDWORD ndst)
682 double r;
683 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
685 while (*nsrc != 0 && *ndst != 0) {
686 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
687 if (*nsrc == 0) return;
688 apd->last[0].s = R16(src); src += 2;
689 apd->srcPos++;
690 (*nsrc)--;
692 /* now do the interpolation */
693 if (*nsrc) /* don't go off end of data */
694 *dst++ = C168(I(apd->last[0].s, R16(src), r));
695 else
696 *dst++ = C168(apd->last[0].s);
697 apd->dstPos += apd->dstIncr;
698 (*ndst)--;
702 static void cvtSS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
703 unsigned char* dst, LPDWORD ndst)
705 double r;
706 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
708 while (*nsrc != 0 && *ndst != 0) {
709 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
710 if (*nsrc == 0) return;
711 apd->last[0].s = R16(src); src += 2;
712 apd->last[1].s = R16(src); src += 2;
713 apd->srcPos++;
714 (*nsrc)--;
716 /* now do the interpolation */
717 if (*nsrc) /* don't go off end of data */
718 W16(dst, I(apd->last[0].s, R16(src), r));
719 else
720 W16(dst, apd->last[0].s);
721 dst += 2;
722 if (*nsrc) /* don't go off end of data */
723 W16(dst, I(apd->last[1].s, R16(src+2), r));
724 else
725 W16(dst, apd->last[1].s);
726 dst += 2;
727 apd->dstPos += apd->dstIncr;
728 (*ndst)--;
732 static void cvtSM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
733 unsigned char* dst, LPDWORD ndst)
735 double r;
736 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
738 while (*nsrc != 0 && *ndst != 0) {
739 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
740 if (*nsrc == 0) return;
741 apd->last[0].s = R16(src); src += 2;
742 apd->last[1].s = R16(src); src += 2;
743 apd->srcPos++;
744 (*nsrc)--;
746 /* now do the interpolation */
747 if (*nsrc) /* don't go off end of data */
748 W16(dst, I(M16(apd->last[0].s, apd->last[1].s),
749 M16(R16(src), R16(src+2)), r));
750 else
751 W16(dst, M16(apd->last[0].s, apd->last[1].s));
752 dst += 2;
753 apd->dstPos += apd->dstIncr;
754 (*ndst)--;
758 static void cvtMS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
759 unsigned char* dst, LPDWORD ndst)
761 double r;
762 short v;
763 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
765 while (*nsrc != 0 && *ndst != 0) {
766 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
767 if (*nsrc == 0) return;
768 apd->last[0].s = R16(src); src += 2;
769 apd->srcPos++;
770 (*nsrc)--;
772 /* now do the interpolation */
773 if (*nsrc) /* don't go off end of data */
774 v = I(apd->last[0].s, R16(src), r);
775 else
776 v = apd->last[0].s;
777 W16(dst, v); dst += 2;
778 W16(dst, v); dst += 2;
779 apd->dstPos += apd->dstIncr;
780 (*ndst)--;
784 static void cvtMM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
785 unsigned char* dst, LPDWORD ndst)
787 double r;
788 TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
790 while (*nsrc != 0 && *ndst != 0) {
791 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
792 if (*nsrc == 0) return;
793 apd->last[0].s = R16(src); src += 2;
794 apd->srcPos++;
795 (*nsrc)--;
797 /* now do the interpolation */
798 if (*nsrc) /* don't go off end of data */
799 W16(dst, I(apd->last[0].s, R16(src), r));
800 else
801 W16(dst, apd->last[0].s);
802 dst += 2;
803 apd->dstPos += apd->dstIncr;
804 (*ndst)--;
808 static void (*PCM_ConvertChangeRate[16])(AcmPcmData* apd,
809 const unsigned char* src, LPDWORD nsrc,
810 unsigned char* dst, LPDWORD ndst) = {
811 cvtSS88C, cvtSM88C, cvtMS88C, cvtMM88C,
812 cvtSS816C, cvtSM816C, cvtMS816C, cvtMM816C,
813 cvtSS168C, cvtSM168C, cvtMS168C, cvtMM168C,
814 cvtSS1616C, cvtSM1616C, cvtMS1616C, cvtMM1616C,
817 /***********************************************************************
818 * PCM_DriverDetails
821 static LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add)
823 TRACE("(%p)\n", add);
825 add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
826 add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
827 add->wMid = 0xFF;
828 add->wPid = 0x00;
829 add->vdwACM = 0x01000000;
830 add->vdwDriver = 0x01000000;
831 add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
832 add->cFormatTags = 1;
833 add->cFilterTags = 0;
834 add->hicon = NULL;
835 MultiByteToWideChar( CP_ACP, 0, "WINE-PCM", -1,
836 add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
837 MultiByteToWideChar( CP_ACP, 0, "Wine PCM converter", -1,
838 add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
839 MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1,
840 add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) );
841 MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1,
842 add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) );
843 add->szFeatures[0] = 0;
845 return MMSYSERR_NOERROR;
848 /***********************************************************************
849 * PCM_FormatTagDetails
852 static LRESULT PCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
854 TRACE("(%p, %08lx)\n", aftd, dwQuery);
856 switch (dwQuery) {
857 case ACM_FORMATTAGDETAILSF_INDEX:
858 if (aftd->dwFormatTagIndex != 0) {
859 WARN("not possible\n");
860 return ACMERR_NOTPOSSIBLE;
862 break;
863 case ACM_FORMATTAGDETAILSF_FORMATTAG:
864 if (aftd->dwFormatTag != WAVE_FORMAT_PCM) {
865 WARN("not possible\n");
866 return ACMERR_NOTPOSSIBLE;
868 break;
869 case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
870 if (aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN &&
871 aftd->dwFormatTag != WAVE_FORMAT_PCM) {
872 WARN("not possible\n");
873 return ACMERR_NOTPOSSIBLE;
875 break;
876 default:
877 WARN("Unsupported query %08lx\n", dwQuery);
878 return MMSYSERR_NOTSUPPORTED;
881 aftd->dwFormatTagIndex = 0;
882 aftd->dwFormatTag = WAVE_FORMAT_PCM;
883 aftd->cbFormatSize = sizeof(PCMWAVEFORMAT);
884 aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
885 aftd->cStandardFormats = NUM_PCM_FORMATS;
886 aftd->szFormatTag[0] = 0;
888 return MMSYSERR_NOERROR;
891 /***********************************************************************
892 * PCM_FormatDetails
895 static LRESULT PCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
897 TRACE("(%p, %08lx)\n", afd, dwQuery);
899 switch (dwQuery) {
900 case ACM_FORMATDETAILSF_FORMAT:
901 if (PCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) {
902 return ACMERR_NOTPOSSIBLE;
903 WARN("not possible\n");
905 break;
906 case ACM_FORMATDETAILSF_INDEX:
907 assert(afd->dwFormatIndex < NUM_PCM_FORMATS);
908 afd->pwfx->wFormatTag = WAVE_FORMAT_PCM;
909 afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels;
910 afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate;
911 afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits;
912 /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not
913 * accessible afd->pwfx->cbSize = 0;
915 afd->pwfx->nBlockAlign =
916 (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
917 afd->pwfx->nAvgBytesPerSec =
918 afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
919 break;
920 default:
921 WARN("Unsupported query %08lx\n", dwQuery);
922 return MMSYSERR_NOTSUPPORTED;
925 afd->dwFormatTag = WAVE_FORMAT_PCM;
926 afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
927 afd->szFormat[0] = 0; /* let MSACM format this for us... */
928 afd->cbwfx = sizeof(PCMWAVEFORMAT);
930 return MMSYSERR_NOERROR;
933 /***********************************************************************
934 * PCM_FormatSuggest
937 static LRESULT PCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
939 TRACE("(%p)\n", adfs);
941 /* some tests ... */
942 if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) ||
943 adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
944 PCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) {
945 WARN("not possible\n");
946 return ACMERR_NOTPOSSIBLE;
949 /* is no suggestion for destination, then copy source value */
950 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS)) {
951 adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
953 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC)) {
954 adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
956 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE)) {
957 adfs->pwfxDst->wBitsPerSample = adfs->pwfxSrc->wBitsPerSample;
959 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)) {
960 if (adfs->pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) {
961 WARN("not possible\n");
962 return ACMERR_NOTPOSSIBLE;
964 adfs->pwfxDst->wFormatTag = adfs->pwfxSrc->wFormatTag;
966 /* check if result is ok */
967 if (PCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) {
968 WARN("not possible\n");
969 return ACMERR_NOTPOSSIBLE;
972 /* recompute other values */
973 adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8;
974 adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign;
976 return MMSYSERR_NOERROR;
979 /***********************************************************************
980 * PCM_Reset
983 static void PCM_Reset(AcmPcmData* apd, int srcNumBits)
985 TRACE("(%p, %d)\n", apd, srcNumBits);
987 apd->srcPos = 0;
988 apd->dstPos = 0;
989 /* initialize with neutral value */
990 if (srcNumBits == 16) {
991 apd->last[0].s = 0;
992 apd->last[1].s = 0;
993 } else {
994 apd->last[0].b = (BYTE)0x80;
995 apd->last[1].b = (BYTE)0x80;
999 /***********************************************************************
1000 * PCM_StreamOpen
1003 static LRESULT PCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
1005 AcmPcmData* apd;
1006 int idx = 0;
1008 TRACE("(%p)\n", adsi);
1010 assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
1012 if (PCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF ||
1013 PCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF) {
1014 WARN("not possible\n");
1015 return ACMERR_NOTPOSSIBLE;
1018 apd = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmPcmData));
1019 if (apd == 0) {
1020 WARN("no memory\n");
1021 return MMSYSERR_NOMEM;
1024 adsi->dwDriver = (DWORD)apd;
1025 adsi->fdwDriver = 0;
1027 if (adsi->pwfxSrc->wBitsPerSample == 16) idx += 8;
1028 if (adsi->pwfxDst->wBitsPerSample == 16) idx += 4;
1029 if (adsi->pwfxSrc->nChannels == 1) idx += 2;
1030 if (adsi->pwfxDst->nChannels == 1) idx += 1;
1032 if (adsi->pwfxSrc->nSamplesPerSec == adsi->pwfxDst->nSamplesPerSec) {
1033 apd->cvt.cvtKeepRate = PCM_ConvertKeepRate[idx];
1034 } else {
1035 adsi->fdwDriver |= PCM_RESAMPLE;
1036 apd->dstIncr = (double)(adsi->pwfxSrc->nSamplesPerSec) /
1037 (double)(adsi->pwfxDst->nSamplesPerSec);
1038 PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
1039 apd->cvt.cvtChangeRate = PCM_ConvertChangeRate[idx];
1042 return MMSYSERR_NOERROR;
1045 /***********************************************************************
1046 * PCM_StreamClose
1049 static LRESULT PCM_StreamClose(PACMDRVSTREAMINSTANCE adsi)
1051 TRACE("(%p)\n", adsi);
1053 HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
1054 return MMSYSERR_NOERROR;
1057 /***********************************************************************
1058 * PCM_round
1061 static inline DWORD PCM_round(DWORD a, DWORD b, DWORD c)
1063 assert(c);
1064 /* to be sure, always return an entire number of c... */
1065 return ((double)a * (double)b + (double)c - 1) / (double)c;
1068 /***********************************************************************
1069 * PCM_StreamSize
1072 static LRESULT PCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss)
1074 DWORD srcMask = ~(adsi->pwfxSrc->nBlockAlign - 1);
1075 DWORD dstMask = ~(adsi->pwfxDst->nBlockAlign - 1);
1077 TRACE("(%p, %p)\n", adsi, adss);
1079 switch (adss->fdwSize) {
1080 case ACM_STREAMSIZEF_DESTINATION:
1081 /* cbDstLength => cbSrcLength */
1082 adss->cbSrcLength = PCM_round(adss->cbDstLength & dstMask,
1083 adsi->pwfxSrc->nAvgBytesPerSec,
1084 adsi->pwfxDst->nAvgBytesPerSec) & srcMask;
1085 break;
1086 case ACM_STREAMSIZEF_SOURCE:
1087 /* cbSrcLength => cbDstLength */
1088 adss->cbDstLength = PCM_round(adss->cbSrcLength & srcMask,
1089 adsi->pwfxDst->nAvgBytesPerSec,
1090 adsi->pwfxSrc->nAvgBytesPerSec) & dstMask;
1091 break;
1092 default:
1093 WARN("Unsupported query %08lx\n", adss->fdwSize);
1094 return MMSYSERR_NOTSUPPORTED;
1096 return MMSYSERR_NOERROR;
1099 /***********************************************************************
1100 * PCM_StreamConvert
1103 static LRESULT PCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
1105 AcmPcmData* apd = (AcmPcmData*)adsi->dwDriver;
1106 DWORD nsrc = NUM_OF(adsh->cbSrcLength, adsi->pwfxSrc->nBlockAlign);
1107 DWORD ndst = NUM_OF(adsh->cbDstLength, adsi->pwfxDst->nBlockAlign);
1109 TRACE("(%p, %p)\n", adsi, adsh);
1111 TRACE("nsrc=%ld,adsh->cbSrcLength=%ld\n", nsrc, adsh->cbSrcLength);
1112 TRACE("ndst=%ld,adsh->cbDstLength=%ld\n", ndst, adsh->cbDstLength);
1113 TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
1114 adsi->pwfxSrc->wFormatTag, adsi->pwfxSrc->nChannels, adsi->pwfxSrc->nSamplesPerSec, adsi->pwfxSrc->nAvgBytesPerSec,
1115 adsi->pwfxSrc->nBlockAlign, adsi->pwfxSrc->wBitsPerSample, adsi->pwfxSrc->cbSize);
1116 TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
1117 adsi->pwfxDst->wFormatTag, adsi->pwfxDst->nChannels, adsi->pwfxDst->nSamplesPerSec, adsi->pwfxDst->nAvgBytesPerSec,
1118 adsi->pwfxDst->nBlockAlign, adsi->pwfxDst->wBitsPerSample, adsi->pwfxDst->cbSize);
1120 if (adsh->fdwConvert &
1121 ~(ACM_STREAMCONVERTF_BLOCKALIGN|
1122 ACM_STREAMCONVERTF_END|
1123 ACM_STREAMCONVERTF_START)) {
1124 FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert);
1126 /* ACM_STREAMCONVERTF_BLOCKALIGN
1127 * currently all conversions are block aligned, so do nothing for this flag
1128 * ACM_STREAMCONVERTF_END
1129 * no pending data, so do nothing for this flag
1131 if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START) &&
1132 (adsi->fdwDriver & PCM_RESAMPLE)) {
1133 PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
1136 /* do the job */
1137 if (adsi->fdwDriver & PCM_RESAMPLE) {
1138 DWORD nsrc2 = nsrc;
1139 DWORD ndst2 = ndst;
1141 apd->cvt.cvtChangeRate(apd, adsh->pbSrc, &nsrc2, adsh->pbDst, &ndst2);
1142 nsrc -= nsrc2;
1143 ndst -= ndst2;
1144 } else {
1145 if (nsrc < ndst) ndst = nsrc; else nsrc = ndst;
1147 /* nsrc is now equal to ndst */
1148 apd->cvt.cvtKeepRate(adsh->pbSrc, nsrc, adsh->pbDst);
1151 adsh->cbSrcLengthUsed = nsrc * adsi->pwfxSrc->nBlockAlign;
1152 adsh->cbDstLengthUsed = ndst * adsi->pwfxDst->nBlockAlign;
1154 return MMSYSERR_NOERROR;
1157 /**************************************************************************
1158 * DriverProc (MSACM32.@)
1160 LRESULT CALLBACK PCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg,
1161 LPARAM dwParam1, LPARAM dwParam2)
1163 TRACE("(%08lx %08lx %u %08lx %08lx);\n",
1164 dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2);
1166 switch (wMsg) {
1167 case DRV_LOAD: return 1;
1168 case DRV_FREE: return 1;
1169 case DRV_OPEN: return PCM_drvOpen((LPSTR)dwParam1, (PACMDRVOPENDESCW)dwParam2);
1170 case DRV_CLOSE: return PCM_drvClose(dwDevID);
1171 case DRV_ENABLE: return 1;
1172 case DRV_DISABLE: return 1;
1173 case DRV_QUERYCONFIGURE: return 1;
1174 case DRV_CONFIGURE: MessageBoxA(0, "MSACM PCM filter !", "Wine Driver", MB_OK); return 1;
1175 case DRV_INSTALL: return DRVCNF_RESTART;
1176 case DRV_REMOVE: return DRVCNF_RESTART;
1178 case ACMDM_DRIVER_NOTIFY:
1179 /* no caching from other ACM drivers is done so far */
1180 return MMSYSERR_NOERROR;
1182 case ACMDM_DRIVER_DETAILS:
1183 return PCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1);
1185 case ACMDM_FORMATTAG_DETAILS:
1186 return PCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2);
1188 case ACMDM_FORMAT_DETAILS:
1189 return PCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2);
1191 case ACMDM_FORMAT_SUGGEST:
1192 return PCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1);
1194 case ACMDM_STREAM_OPEN:
1195 return PCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1);
1197 case ACMDM_STREAM_CLOSE:
1198 return PCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1);
1200 case ACMDM_STREAM_SIZE:
1201 return PCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2);
1203 case ACMDM_STREAM_CONVERT:
1204 return PCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2);
1206 case ACMDM_HARDWARE_WAVE_CAPS_INPUT:
1207 case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT:
1208 /* this converter is not a hardware driver */
1209 case ACMDM_FILTERTAG_DETAILS:
1210 case ACMDM_FILTER_DETAILS:
1211 /* this converter is not a filter */
1212 case ACMDM_STREAM_RESET:
1213 /* only needed for asynchronous driver... we aren't, so just say it */
1214 case ACMDM_STREAM_PREPARE:
1215 case ACMDM_STREAM_UNPREPARE:
1216 /* nothing special to do here... so don't do anything */
1217 return MMSYSERR_NOTSUPPORTED;
1219 default:
1220 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1222 return 0;