Various test fixes for XP/msvc.
[wine/multimedia.git] / dlls / msacm / pcmconverter.c
blob87d1f62a57c6e383ade8199079a62c09306e60b4
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 return (adod == NULL) ||
60 (adod->fccType == ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC &&
61 adod->fccComp == ACMDRIVERDETAILS_FCCCOMP_UNDEFINED);
64 /***********************************************************************
65 * PCM_drvClose
67 static DWORD PCM_drvClose(DWORD dwDevID)
69 return 1;
72 #define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
73 #define NUM_OF(a,b) (((a)+(b)-1)/(b))
75 /* flags for fdwDriver */
76 #define PCM_RESAMPLE 1
78 /* data used while converting */
79 typedef struct tagAcmPcmData {
80 /* conversion routine, depending if rate conversion is required */
81 union {
82 void (*cvtKeepRate)(const unsigned char*, int, unsigned char*);
83 void (*cvtChangeRate)(struct tagAcmPcmData*, const unsigned char*,
84 LPDWORD, unsigned char*, LPDWORD);
85 } cvt;
86 /* the following fields are used only with rate conversion) */
87 DWORD srcPos; /* position in source stream */
88 double dstPos; /* position in destination stream */
89 double dstIncr; /* value to increment dst stream when src stream
90 is incremented by 1 */
91 /* last source stream value read */
92 union {
93 unsigned char b; /* 8 bit value */
94 short s; /* 16 bit value */
95 } last[2]; /* two channels max (stereo) */
96 } AcmPcmData;
98 /* table to list all supported formats... those are the basic ones. this
99 * also helps given a unique index to each of the supported formats
101 static struct {
102 int nChannels;
103 int nBits;
104 int rate;
105 } PCM_Formats[] = {
106 {1, 8, 8000}, {2, 8, 8000}, {1, 16, 8000}, {2, 16, 8000},
107 {1, 8, 11025}, {2, 8, 11025}, {1, 16, 11025}, {2, 16, 11025},
108 {1, 8, 22050}, {2, 8, 22050}, {1, 16, 22050}, {2, 16, 22050},
109 {1, 8, 44100}, {2, 8, 44100}, {1, 16, 44100}, {2, 16, 44100},
110 {1, 8, 48000}, {2, 8, 48000}, {1, 16, 48000}, {2, 16, 48000},
111 {1, 8, 96000}, {2, 8, 96000}, {1, 16, 96000}, {2, 16, 96000}
114 /***********************************************************************
115 * PCM_GetFormatIndex
117 static DWORD PCM_GetFormatIndex(LPWAVEFORMATEX wfx)
119 int i;
121 for (i = 0; i < NUM_PCM_FORMATS; i++) {
122 if (wfx->nChannels == PCM_Formats[i].nChannels &&
123 wfx->nSamplesPerSec == PCM_Formats[i].rate &&
124 wfx->wBitsPerSample == PCM_Formats[i].nBits)
125 return i;
127 return 0xFFFFFFFF;
130 /* PCM Conversions:
132 * parameters:
133 * + 8 bit unsigned vs 16 bit signed
134 * + mono vs stereo (1 or 2 channels)
135 * + sampling rate (8.0, 11.025, 22.05, 44.1 kHz are defined, but algo
136 * shall work in all cases)
138 * mono => stereo: copy the same sample on Left & Right channels
139 * stereo =) mono: use the average value of samples from Left & Right channels
140 * resampling; we lookup for each destination sample the two source adjacent
141 * samples were src <= dst < src+1 (dst is increased by a fractional
142 * value which is equivalent to the increment by one on src); then we
143 * use a linear interpolation between src and src+1
146 /***********************************************************************
147 * C816
149 * Converts a 8 bit sample to a 16 bit one
151 static inline short C816(unsigned char b)
153 return (short)((b+(b << 8))-32768);
156 /***********************************************************************
157 * C168
159 * Converts a 16 bit sample to a 8 bit one (data loss !!)
161 static inline unsigned char C168(short s)
163 return HIBYTE(s) ^ (unsigned char)0x80;
166 /***********************************************************************
167 * R16
169 * Read a 16 bit sample (correctly handles endianess)
171 static inline short R16(const unsigned char* src)
173 return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
176 /***********************************************************************
177 * W16
179 * Write a 16 bit sample (correctly handles endianess)
181 static inline void W16(unsigned char* dst, short s)
183 dst[0] = LOBYTE(s);
184 dst[1] = HIBYTE(s);
187 /***********************************************************************
188 * M16
190 * Convert the (l,r) 16 bit stereo sample into a 16 bit mono
191 * (takes the mid-point of the two values)
193 static inline short M16(short l, short r)
195 return (l + r) / 2;
198 /***********************************************************************
199 * M8
201 * Convert the (l,r) 8 bit stereo sample into a 8 bit mono
202 * (takes the mid-point of the two values)
204 static inline unsigned char M8(unsigned char a, unsigned char b)
206 return (unsigned char)((a + b) / 2);
209 /* the conversion routines without rate conversion are labelled cvt<X><Y><N><M>K
210 * where :
211 * <X> is the (M)ono/(S)tereo configuration of input channel
212 * <Y> is the (M)ono/(S)tereo configuration of output channel
213 * <N> is the number of bits of input channel (8 or 16)
214 * <M> is the number of bits of output channel (8 or 16)
216 * in the parameters, ns is always the number of samples, so the size of input
217 * buffer (resp output buffer) is ns * (<X> == 'Mono' ? 1:2) * (<N> == 8 ? 1:2)
220 static void cvtMM88K(const unsigned char* src, int ns, unsigned char* dst)
222 memcpy(dst, src, ns);
225 static void cvtSS88K(const unsigned char* src, int ns, unsigned char* dst)
227 memcpy(dst, src, ns * 2);
230 static void cvtMM1616K(const unsigned char* src, int ns, unsigned char* dst)
232 memcpy(dst, src, ns * 2);
235 static void cvtSS1616K(const unsigned char* src, int ns, unsigned char* dst)
237 memcpy(dst, src, ns * 4);
240 static void cvtMS88K(const unsigned char* src, int ns, unsigned char* dst)
242 while (ns--) {
243 *dst++ = *src;
244 *dst++ = *src++;
248 static void cvtMS816K(const unsigned char* src, int ns, unsigned char* dst)
250 short v;
252 while (ns--) {
253 v = C816(*src++);
254 W16(dst, v); dst += 2;
255 W16(dst, v); dst += 2;
259 static void cvtMS168K(const unsigned char* src, int ns, unsigned char* dst)
261 unsigned char v;
263 while (ns--) {
264 v = C168(R16(src)); src += 2;
265 *dst++ = v;
266 *dst++ = v;
270 static void cvtMS1616K(const unsigned char* src, int ns, unsigned char* dst)
272 short v;
274 while (ns--) {
275 v = R16(src); src += 2;
276 W16(dst, v); dst += 2;
277 W16(dst, v); dst += 2;
281 static void cvtSM88K(const unsigned char* src, int ns, unsigned char* dst)
283 while (ns--) {
284 *dst++ = M8(src[0], src[1]);
285 src += 2;
289 static void cvtSM816K(const unsigned char* src, int ns, unsigned char* dst)
291 short v;
293 while (ns--) {
294 v = M16(C816(src[0]), C816(src[1]));
295 src += 2;
296 W16(dst, v); dst += 2;
300 static void cvtSM168K(const unsigned char* src, int ns, unsigned char* dst)
302 while (ns--) {
303 *dst++ = C168(M16(R16(src), R16(src + 2)));
304 src += 4;
308 static void cvtSM1616K(const unsigned char* src, int ns, unsigned char* dst)
310 while (ns--) {
311 W16(dst, M16(R16(src),R16(src+2))); dst += 2;
312 src += 4;
316 static void cvtMM816K(const unsigned char* src, int ns, unsigned char* dst)
318 while (ns--) {
319 W16(dst, C816(*src++)); dst += 2;
323 static void cvtSS816K(const unsigned char* src, int ns, unsigned char* dst)
325 while (ns--) {
326 W16(dst, C816(*src++)); dst += 2;
327 W16(dst, C816(*src++)); dst += 2;
331 static void cvtMM168K(const unsigned char* src, int ns, unsigned char* dst)
333 while (ns--) {
334 *dst++ = C168(R16(src)); src += 2;
338 static void cvtSS168K(const unsigned char* src, int ns, unsigned char* dst)
340 while (ns--) {
341 *dst++ = C168(R16(src)); src += 2;
342 *dst++ = C168(R16(src)); src += 2;
346 static void (*PCM_ConvertKeepRate[16])(const unsigned char*, int, unsigned char*) = {
347 cvtSS88K, cvtSM88K, cvtMS88K, cvtMM88K,
348 cvtSS816K, cvtSM816K, cvtMS816K, cvtMM816K,
349 cvtSS168K, cvtSM168K, cvtMS168K, cvtMM168K,
350 cvtSS1616K, cvtSM1616K, cvtMS1616K, cvtMM1616K,
353 /***********************************************************************
356 * Interpolate the value at r (r in ]0, 1]) between the two points v1 and v2
357 * Linear interpolation is used
359 static inline double I(double v1, double v2, double r)
361 if (0.0 >= r || r > 1.0) FIXME("r!! %f\n", r);
362 return (1.0 - r) * v1 + r * v2;
365 static void cvtSS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
366 unsigned char* dst, LPDWORD ndst)
368 double r;
370 while (*nsrc != 0 && *ndst != 0) {
371 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
372 if (*nsrc == 0) return;
373 apd->last[0].b = *src++;
374 apd->last[1].b = *src++;
375 apd->srcPos++;
376 (*nsrc)--;
378 /* now do the interpolation */
379 *dst++ = I(apd->last[0].b, src[0], r);
380 *dst++ = I(apd->last[1].b, src[1], r);
381 apd->dstPos += apd->dstIncr;
382 (*ndst)--;
386 /* the conversion routines with rate conversion are labelled cvt<X><Y><N><M>C
387 * where :
388 * <X> is the (M)ono/(S)tereo configuration of input channel
389 * <Y> is the (M)ono/(S)tereo configuration of output channel
390 * <N> is the number of bits of input channel (8 or 16)
391 * <M> is the number of bits of output channel (8 or 16)
394 static void cvtSM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
395 unsigned char* dst, LPDWORD ndst)
397 double r;
399 while (*nsrc != 0 && *ndst != 0) {
400 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
401 if (*nsrc == 0) return;
402 apd->last[0].b = *src++;
403 apd->last[1].b = *src++;
404 apd->srcPos++;
405 (*nsrc)--;
407 /* now do the interpolation */
408 *dst++ = I(M8(apd->last[0].b, apd->last[1].b), M8(src[0], src[1]), r);
409 apd->dstPos += apd->dstIncr;
410 (*ndst)--;
414 static void cvtMS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
415 unsigned char* dst, LPDWORD ndst)
417 double r;
419 while (*nsrc != 0 && *ndst != 0) {
420 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
421 if (*nsrc == 0) return;
422 apd->last[0].b = *src++;
423 apd->srcPos++;
424 (*nsrc)--;
426 /* now do the interpolation */
427 dst[0] = dst[1] = I(apd->last[0].b, src[0], r);
428 dst += 2;
429 apd->dstPos += apd->dstIncr;
430 (*ndst)--;
434 static void cvtMM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
435 unsigned char* dst, LPDWORD ndst)
437 double r;
439 while (*nsrc != 0 && *ndst != 0) {
440 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
441 if (*nsrc == 0) return;
442 apd->last[0].b = *src++;
443 apd->srcPos++;
444 (*nsrc)--;
446 /* now do the interpolation */
447 *dst++ = I(apd->last[0].b, src[0], r);
448 apd->dstPos += apd->dstIncr;
449 (*ndst)--;
453 static void cvtSS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
454 unsigned char* dst, LPDWORD ndst)
456 double r;
458 while (*nsrc != 0 && *ndst != 0) {
459 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
460 if (*nsrc == 0) return;
461 apd->last[0].b = *src++;
462 apd->last[1].b = *src++;
463 apd->srcPos++;
464 (*nsrc)--;
466 /* now do the interpolation */
467 W16(dst, I(C816(apd->last[0].b), C816(src[0]), r)); dst += 2;
468 W16(dst, I(C816(apd->last[1].b), C816(src[1]), r)); dst += 2;
469 apd->dstPos += apd->dstIncr;
470 (*ndst)--;
474 static void cvtSM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
475 unsigned char* dst, LPDWORD ndst)
477 double r;
479 while (*nsrc != 0 && *ndst != 0) {
480 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
481 if (*nsrc == 0) return;
482 apd->last[0].b = *src++;
483 apd->last[1].b = *src++;
484 apd->srcPos++;
485 (*nsrc)--;
487 /* now do the interpolation */
488 W16(dst, I(M16(C816(apd->last[0].b), C816(apd->last[1].b)),
489 M16(C816(src[0]), C816(src[1])), r));
490 dst += 2;
491 apd->dstPos += apd->dstIncr;
492 (*ndst)--;
496 static void cvtMS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
497 unsigned char* dst, LPDWORD ndst)
499 double r;
500 short v;
502 while (*nsrc != 0 && *ndst != 0) {
503 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
504 if (*nsrc == 0) return;
505 apd->last[0].b = *src++;
506 apd->srcPos++;
507 (*nsrc)--;
509 /* now do the interpolation */
510 v = I(C816(apd->last[0].b), C816(src[0]), r);
511 W16(dst, v); dst += 2;
512 W16(dst, v); dst += 2;
513 apd->dstPos += apd->dstIncr;
514 (*ndst)--;
518 static void cvtMM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
519 unsigned char* dst, LPDWORD ndst)
521 double r;
523 while (*nsrc != 0 && *ndst != 0) {
524 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
525 if (*nsrc == 0) return;
526 apd->last[0].b = *src++;
527 apd->srcPos++;
528 (*nsrc)--;
530 /* now do the interpolation */
531 W16(dst, I(C816(apd->last[0].b), C816(src[0]), r));
532 dst += 2;
533 apd->dstPos += apd->dstIncr;
534 (*ndst)--;
538 static void cvtSS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
539 unsigned char* dst, LPDWORD ndst)
541 double r;
543 while (*nsrc != 0 && *ndst != 0) {
544 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
545 if (*nsrc == 0) return;
546 apd->last[0].s = R16(src); src += 2;
547 apd->last[1].s = R16(src); src += 2;
548 apd->srcPos++;
549 (*nsrc)--;
551 /* now do the interpolation */
552 *dst++ = C168(I(apd->last[0].s, R16(src) , r));
553 *dst++ = C168(I(apd->last[1].s, R16(src+2), r));
554 apd->dstPos += apd->dstIncr;
555 (*ndst)--;
559 static void cvtSM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
560 unsigned char* dst, LPDWORD ndst)
562 double r;
564 while (*nsrc != 0 && *ndst != 0) {
565 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
566 if (*nsrc == 0) return;
567 apd->last[0].s = R16(src); src += 2;
568 apd->last[1].s = R16(src); src += 2;
569 apd->srcPos++;
570 (*nsrc)--;
572 /* now do the interpolation */
573 *dst++ = C168(I(M16(apd->last[0].s, apd->last[1].s),
574 M16(R16(src), R16(src + 2)), r));
575 apd->dstPos += apd->dstIncr;
576 (*ndst)--;
581 static void cvtMS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
582 unsigned char* dst, LPDWORD ndst)
584 double r;
586 while (*nsrc != 0 && *ndst != 0) {
587 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
588 if (*nsrc == 0) return;
589 apd->last[0].s = R16(src); src += 2;
590 apd->srcPos++;
591 (*nsrc)--;
593 /* now do the interpolation */
594 dst[0] = dst[1] = C168(I(apd->last[0].s, R16(src), r)); dst += 2;
595 apd->dstPos += apd->dstIncr;
596 (*ndst)--;
601 static void cvtMM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
602 unsigned char* dst, LPDWORD ndst)
604 double r;
606 while (*nsrc != 0 && *ndst != 0) {
607 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
608 if (*nsrc == 0) return;
609 apd->last[0].s = R16(src); src += 2;
610 apd->srcPos++;
611 (*nsrc)--;
613 /* now do the interpolation */
614 *dst++ = C168(I(apd->last[0].s, R16(src), r));
615 apd->dstPos += apd->dstIncr;
616 (*ndst)--;
620 static void cvtSS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
621 unsigned char* dst, LPDWORD ndst)
623 double r;
625 while (*nsrc != 0 && *ndst != 0) {
626 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
627 if (*nsrc == 0) return;
628 apd->last[0].s = R16(src); src += 2;
629 apd->last[1].s = R16(src); src += 2;
630 apd->srcPos++;
631 (*nsrc)--;
633 /* now do the interpolation */
634 W16(dst, I(apd->last[0].s, R16(src) , r)); dst += 2;
635 W16(dst, I(apd->last[1].s, R16(src+2), r)); dst += 2;
636 apd->dstPos += apd->dstIncr;
637 (*ndst)--;
641 static void cvtSM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
642 unsigned char* dst, LPDWORD ndst)
644 double r;
646 while (*nsrc != 0 && *ndst != 0) {
647 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
648 if (*nsrc == 0) return;
649 apd->last[0].s = R16(src); src += 2;
650 apd->last[1].s = R16(src); src += 2;
651 apd->srcPos++;
652 (*nsrc)--;
654 /* now do the interpolation */
655 W16(dst, I(M16(apd->last[0].s, apd->last[1].s),
656 M16(R16(src), R16(src+2)), r));
657 dst += 2;
658 apd->dstPos += apd->dstIncr;
659 (*ndst)--;
663 static void cvtMS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
664 unsigned char* dst, LPDWORD ndst)
666 double r;
667 short v;
669 while (*nsrc != 0 && *ndst != 0) {
670 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
671 if (*nsrc == 0) return;
672 apd->last[0].s = R16(src); src += 2;
673 apd->srcPos++;
674 (*nsrc)--;
676 /* now do the interpolation */
677 v = I(apd->last[0].s, R16(src), r);
678 W16(dst, v); dst += 2;
679 W16(dst, v); dst += 2;
680 apd->dstPos += apd->dstIncr;
681 (*ndst)--;
685 static void cvtMM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
686 unsigned char* dst, LPDWORD ndst)
688 double r;
690 while (*nsrc != 0 && *ndst != 0) {
691 while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
692 if (*nsrc == 0) return;
693 apd->last[0].s = R16(src); src += 2;
694 apd->srcPos++;
695 (*nsrc)--;
697 /* now do the interpolation */
698 W16(dst, I(apd->last[0].s, R16(src), r)); dst += 2;
699 apd->dstPos += apd->dstIncr;
700 (*ndst)--;
704 static void (*PCM_ConvertChangeRate[16])(AcmPcmData* apd,
705 const unsigned char* src, LPDWORD nsrc,
706 unsigned char* dst, LPDWORD ndst) = {
707 cvtSS88C, cvtSM88C, cvtMS88C, cvtMM88C,
708 cvtSS816C, cvtSM816C, cvtMS816C, cvtMM816C,
709 cvtSS168C, cvtSM168C, cvtMS168C, cvtMM168C,
710 cvtSS1616C, cvtSM1616C, cvtMS1616C, cvtMM1616C,
713 /***********************************************************************
714 * PCM_DriverDetails
717 static LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add)
719 add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
720 add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
721 add->wMid = 0xFF;
722 add->wPid = 0x00;
723 add->vdwACM = 0x01000000;
724 add->vdwDriver = 0x01000000;
725 add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
726 add->cFormatTags = 1;
727 add->cFilterTags = 0;
728 add->hicon = NULL;
729 MultiByteToWideChar( CP_ACP, 0, "WINE-PCM", -1,
730 add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
731 MultiByteToWideChar( CP_ACP, 0, "Wine PCM converter", -1,
732 add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
733 MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1,
734 add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) );
735 MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1,
736 add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) );
737 add->szFeatures[0] = 0;
739 return MMSYSERR_NOERROR;
742 /***********************************************************************
743 * PCM_FormatTagDetails
746 static LRESULT PCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
748 switch (dwQuery) {
749 case ACM_FORMATTAGDETAILSF_INDEX:
750 if (aftd->dwFormatTagIndex != 0) return ACMERR_NOTPOSSIBLE;
751 break;
752 case ACM_FORMATTAGDETAILSF_FORMATTAG:
753 if (aftd->dwFormatTag != WAVE_FORMAT_PCM) return ACMERR_NOTPOSSIBLE;
754 break;
755 case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
756 if (aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN &&
757 aftd->dwFormatTag != WAVE_FORMAT_PCM)
758 return ACMERR_NOTPOSSIBLE;
759 break;
760 default:
761 WARN("Unsupported query %08lx\n", dwQuery);
762 return MMSYSERR_NOTSUPPORTED;
765 aftd->dwFormatTagIndex = 0;
766 aftd->dwFormatTag = WAVE_FORMAT_PCM;
767 aftd->cbFormatSize = sizeof(PCMWAVEFORMAT);
768 aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
769 aftd->cStandardFormats = NUM_PCM_FORMATS;
770 aftd->szFormatTag[0] = 0;
772 return MMSYSERR_NOERROR;
775 /***********************************************************************
776 * PCM_FormatDetails
779 static LRESULT PCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
781 switch (dwQuery) {
782 case ACM_FORMATDETAILSF_FORMAT:
783 if (PCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
784 break;
785 case ACM_FORMATDETAILSF_INDEX:
786 assert(afd->dwFormatIndex < NUM_PCM_FORMATS);
787 afd->pwfx->wFormatTag = WAVE_FORMAT_PCM;
788 afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels;
789 afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate;
790 afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits;
791 /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not
792 * accessible afd->pwfx->cbSize = 0;
794 afd->pwfx->nBlockAlign =
795 (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
796 afd->pwfx->nAvgBytesPerSec =
797 afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
798 break;
799 default:
800 WARN("Unsupported query %08lx\n", dwQuery);
801 return MMSYSERR_NOTSUPPORTED;
804 afd->dwFormatTag = WAVE_FORMAT_PCM;
805 afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
806 afd->szFormat[0] = 0; /* let MSACM format this for us... */
807 afd->cbwfx = sizeof(PCMWAVEFORMAT);
809 return MMSYSERR_NOERROR;
812 /***********************************************************************
813 * PCM_FormatSuggest
816 static LRESULT PCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
818 /* some tests ... */
819 if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) ||
820 adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
821 PCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
823 /* is no suggestion for destination, then copy source value */
824 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS)) {
825 adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
827 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC)) {
828 adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
830 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE)) {
831 adfs->pwfxDst->wBitsPerSample = adfs->pwfxSrc->wBitsPerSample;
833 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)) {
834 if (adfs->pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) return ACMERR_NOTPOSSIBLE;
835 adfs->pwfxDst->wFormatTag = adfs->pwfxSrc->wFormatTag;
837 /* check if result is ok */
838 if (PCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
840 /* recompute other values */
841 adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8;
842 adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign;
844 return MMSYSERR_NOERROR;
847 /***********************************************************************
848 * PCM_Reset
851 static void PCM_Reset(AcmPcmData* apd, int srcNumBits)
853 apd->srcPos = 0;
854 apd->dstPos = 0;
855 /* initialize with neutral value */
856 if (srcNumBits == 16) {
857 apd->last[0].s = 0;
858 apd->last[1].s = 0;
859 } else {
860 apd->last[0].b = (BYTE)0x80;
861 apd->last[1].b = (BYTE)0x80;
865 /***********************************************************************
866 * PCM_StreamOpen
869 static LRESULT PCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
871 AcmPcmData* apd;
872 int idx = 0;
874 assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
876 if (PCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF ||
877 PCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF)
878 return ACMERR_NOTPOSSIBLE;
880 apd = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmPcmData));
881 if (apd == 0) return MMSYSERR_NOMEM;
883 adsi->dwDriver = (DWORD)apd;
884 adsi->fdwDriver = 0;
886 if (adsi->pwfxSrc->wBitsPerSample == 16) idx += 8;
887 if (adsi->pwfxDst->wBitsPerSample == 16) idx += 4;
888 if (adsi->pwfxSrc->nChannels == 1) idx += 2;
889 if (adsi->pwfxDst->nChannels == 1) idx += 1;
891 if (adsi->pwfxSrc->nSamplesPerSec == adsi->pwfxDst->nSamplesPerSec) {
892 apd->cvt.cvtKeepRate = PCM_ConvertKeepRate[idx];
893 } else {
894 adsi->fdwDriver |= PCM_RESAMPLE;
895 apd->dstIncr = (double)(adsi->pwfxSrc->nSamplesPerSec) /
896 (double)(adsi->pwfxDst->nSamplesPerSec);
897 PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
898 apd->cvt.cvtChangeRate = PCM_ConvertChangeRate[idx];
901 return MMSYSERR_NOERROR;
904 /***********************************************************************
905 * PCM_StreamClose
908 static LRESULT PCM_StreamClose(PACMDRVSTREAMINSTANCE adsi)
910 HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
911 return MMSYSERR_NOERROR;
914 /***********************************************************************
915 * PCM_round
918 static inline DWORD PCM_round(DWORD a, DWORD b, DWORD c)
920 assert(c);
921 /* to be sure, always return an entire number of c... */
922 return ((double)a * (double)b + (double)c - 1) / (double)c;
925 /***********************************************************************
926 * PCM_StreamSize
929 static LRESULT PCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss)
931 DWORD srcMask = ~(adsi->pwfxSrc->nBlockAlign - 1);
932 DWORD dstMask = ~(adsi->pwfxDst->nBlockAlign - 1);
934 switch (adss->fdwSize) {
935 case ACM_STREAMSIZEF_DESTINATION:
936 /* cbDstLength => cbSrcLength */
937 adss->cbSrcLength = PCM_round(adss->cbDstLength & dstMask,
938 adsi->pwfxSrc->nAvgBytesPerSec,
939 adsi->pwfxDst->nAvgBytesPerSec) & srcMask;
940 break;
941 case ACM_STREAMSIZEF_SOURCE:
942 /* cbSrcLength => cbDstLength */
943 adss->cbDstLength = PCM_round(adss->cbSrcLength & srcMask,
944 adsi->pwfxDst->nAvgBytesPerSec,
945 adsi->pwfxSrc->nAvgBytesPerSec) & dstMask;
946 break;
947 default:
948 WARN("Unsupported query %08lx\n", adss->fdwSize);
949 return MMSYSERR_NOTSUPPORTED;
951 return MMSYSERR_NOERROR;
954 /***********************************************************************
955 * PCM_StreamConvert
958 static LRESULT PCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
960 AcmPcmData* apd = (AcmPcmData*)adsi->dwDriver;
961 DWORD nsrc = NUM_OF(adsh->cbSrcLength, adsi->pwfxSrc->nBlockAlign);
962 DWORD ndst = NUM_OF(adsh->cbDstLength, adsi->pwfxDst->nBlockAlign);
964 if (adsh->fdwConvert &
965 ~(ACM_STREAMCONVERTF_BLOCKALIGN|
966 ACM_STREAMCONVERTF_END|
967 ACM_STREAMCONVERTF_START)) {
968 FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert);
970 /* ACM_STREAMCONVERTF_BLOCKALIGN
971 * currently all conversions are block aligned, so do nothing for this flag
972 * ACM_STREAMCONVERTF_END
973 * no pending data, so do nothing for this flag
975 if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START) &&
976 (adsi->fdwDriver & PCM_RESAMPLE)) {
977 PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
980 /* do the job */
981 if (adsi->fdwDriver & PCM_RESAMPLE) {
982 DWORD nsrc2 = nsrc;
983 DWORD ndst2 = ndst;
985 apd->cvt.cvtChangeRate(apd, adsh->pbSrc, &nsrc2, adsh->pbDst, &ndst2);
986 nsrc -= nsrc2;
987 ndst -= ndst2;
988 } else {
989 if (nsrc < ndst) ndst = nsrc; else nsrc = ndst;
991 /* nsrc is now equal to ndst */
992 apd->cvt.cvtKeepRate(adsh->pbSrc, nsrc, adsh->pbDst);
995 adsh->cbSrcLengthUsed = nsrc * adsi->pwfxSrc->nBlockAlign;
996 adsh->cbDstLengthUsed = ndst * adsi->pwfxDst->nBlockAlign;
998 return MMSYSERR_NOERROR;
1001 /**************************************************************************
1002 * DriverProc (MSACM32.@)
1004 LRESULT CALLBACK PCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg,
1005 LPARAM dwParam1, LPARAM dwParam2)
1007 TRACE("(%08lx %08lx %u %08lx %08lx);\n",
1008 dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2);
1010 switch (wMsg) {
1011 case DRV_LOAD: return 1;
1012 case DRV_FREE: return 1;
1013 case DRV_OPEN: return PCM_drvOpen((LPSTR)dwParam1, (PACMDRVOPENDESCW)dwParam2);
1014 case DRV_CLOSE: return PCM_drvClose(dwDevID);
1015 case DRV_ENABLE: return 1;
1016 case DRV_DISABLE: return 1;
1017 case DRV_QUERYCONFIGURE: return 1;
1018 case DRV_CONFIGURE: MessageBoxA(0, "MSACM PCM filter !", "Wine Driver", MB_OK); return 1;
1019 case DRV_INSTALL: return DRVCNF_RESTART;
1020 case DRV_REMOVE: return DRVCNF_RESTART;
1022 case ACMDM_DRIVER_NOTIFY:
1023 /* no caching from other ACM drivers is done so far */
1024 return MMSYSERR_NOERROR;
1026 case ACMDM_DRIVER_DETAILS:
1027 return PCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1);
1029 case ACMDM_FORMATTAG_DETAILS:
1030 return PCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2);
1032 case ACMDM_FORMAT_DETAILS:
1033 return PCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2);
1035 case ACMDM_FORMAT_SUGGEST:
1036 return PCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1);
1038 case ACMDM_STREAM_OPEN:
1039 return PCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1);
1041 case ACMDM_STREAM_CLOSE:
1042 return PCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1);
1044 case ACMDM_STREAM_SIZE:
1045 return PCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2);
1047 case ACMDM_STREAM_CONVERT:
1048 return PCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2);
1050 case ACMDM_HARDWARE_WAVE_CAPS_INPUT:
1051 case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT:
1052 /* this converter is not a hardware driver */
1053 case ACMDM_FILTERTAG_DETAILS:
1054 case ACMDM_FILTER_DETAILS:
1055 /* this converter is not a filter */
1056 case ACMDM_STREAM_RESET:
1057 /* only needed for asynchronous driver... we aren't, so just say it */
1058 case ACMDM_STREAM_PREPARE:
1059 case ACMDM_STREAM_UNPREPARE:
1060 /* nothing special to do here... so don't do anything */
1061 return MMSYSERR_NOTSUPPORTED;
1063 default:
1064 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1066 return 0;