d3d8: Use the surface implementation pointer in reset_enum_callback().
[wine/multimedia.git] / dlls / imaadp32.acm / imaadp32.c
blobf7b6ab130402e97a40790795585711f3c649c882
1 /*
2 * IMA ADPCM handling
4 * Copyright (C) 2001,2002 Eric Pouech
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winnls.h"
30 #include "mmsystem.h"
31 #include "mmreg.h"
32 #include "msacm.h"
33 #include "msacmdrv.h"
34 #include "wine/debug.h"
36 /* see http://www.pcisys.net/~melanson/codecs/adpcm.txt for the details */
38 WINE_DEFAULT_DEBUG_CHANNEL(adpcm);
40 /***********************************************************************
41 * ADPCM_drvClose
43 static LRESULT ADPCM_drvClose(DWORD_PTR dwDevID)
45 return 1;
48 typedef struct tagAcmAdpcmData
50 void (*convert)(PACMDRVSTREAMINSTANCE adsi,
51 const unsigned char*, LPDWORD, unsigned char*, LPDWORD);
52 /* IMA encoding only */
53 BYTE stepIndexL;
54 BYTE stepIndexR;
55 /* short sample; */
56 } AcmAdpcmData;
58 /* table to list all supported formats... those are the basic ones. this
59 * also helps given a unique index to each of the supported formats
61 typedef struct
63 int nChannels;
64 int nBits;
65 int rate;
66 } Format;
68 static const Format PCM_Formats[] =
70 {1, 8, 8000}, {2, 8, 8000}, {1, 16, 8000}, {2, 16, 8000},
71 {1, 8, 11025}, {2, 8, 11025}, {1, 16, 11025}, {2, 16, 11025},
72 {1, 8, 22050}, {2, 8, 22050}, {1, 16, 22050}, {2, 16, 22050},
73 {1, 8, 44100}, {2, 8, 44100}, {1, 16, 44100}, {2, 16, 44100},
76 static const Format ADPCM_Formats[] =
78 {1, 4, 8000}, {2, 4, 8000}, {1, 4, 11025}, {2, 4, 11025},
79 {1, 4, 22050}, {2, 4, 22050}, {1, 4, 44100}, {2, 4, 44100},
82 #define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
83 #define NUM_ADPCM_FORMATS (sizeof(ADPCM_Formats) / sizeof(ADPCM_Formats[0]))
85 /***********************************************************************
86 * ADPCM_GetFormatIndex
88 static DWORD ADPCM_GetFormatIndex(const WAVEFORMATEX *wfx)
90 int i, hi;
91 const Format* fmts;
93 switch (wfx->wFormatTag)
95 case WAVE_FORMAT_PCM:
96 hi = NUM_PCM_FORMATS;
97 fmts = PCM_Formats;
98 break;
99 case WAVE_FORMAT_IMA_ADPCM:
100 hi = NUM_ADPCM_FORMATS;
101 fmts = ADPCM_Formats;
102 break;
103 default:
104 return 0xFFFFFFFF;
107 for (i = 0; i < hi; i++)
109 if (wfx->nChannels == fmts[i].nChannels &&
110 wfx->nSamplesPerSec == fmts[i].rate &&
111 wfx->wBitsPerSample == fmts[i].nBits)
112 return i;
115 switch (wfx->wFormatTag)
117 case WAVE_FORMAT_PCM:
118 if(3 > wfx->nChannels &&
119 wfx->nChannels > 0 &&
120 wfx->nAvgBytesPerSec == 2 * wfx->nSamplesPerSec * wfx->nChannels &&
121 wfx->nBlockAlign == 2 * wfx->nChannels &&
122 wfx->wBitsPerSample == 16)
123 return hi;
124 break;
125 case WAVE_FORMAT_IMA_ADPCM:
126 if(3 > wfx->nChannels &&
127 wfx->nChannels > 0 &&
128 wfx->wBitsPerSample == 4 &&
129 wfx->cbSize == 2)
130 return hi;
131 break;
134 return 0xFFFFFFFF;
137 static void init_wfx_ima_adpcm(IMAADPCMWAVEFORMAT* awfx/*, DWORD nba*/)
139 register WAVEFORMATEX* pwfx = &awfx->wfx;
141 /* we assume wFormatTag, nChannels, nSamplesPerSec and wBitsPerSample
142 * have been initialized... */
144 if (pwfx->wFormatTag != WAVE_FORMAT_IMA_ADPCM) {FIXME("wrong FT\n"); return;}
145 if (ADPCM_GetFormatIndex(pwfx) == 0xFFFFFFFF) {FIXME("wrong fmt\n"); return;}
147 switch (pwfx->nSamplesPerSec)
149 case 8000: pwfx->nBlockAlign = 256 * pwfx->nChannels; break;
150 case 11025: pwfx->nBlockAlign = 256 * pwfx->nChannels; break;
151 case 22050: pwfx->nBlockAlign = 512 * pwfx->nChannels; break;
152 case 44100: pwfx->nBlockAlign = 1024 * pwfx->nChannels; break;
153 default: /*pwfx->nBlockAlign = nba;*/ break;
155 pwfx->cbSize = sizeof(WORD);
157 awfx->wSamplesPerBlock = (pwfx->nBlockAlign - (4 * pwfx->nChannels) * 2) / pwfx->nChannels + 1;
158 pwfx->nAvgBytesPerSec = (pwfx->nSamplesPerSec * pwfx->nBlockAlign) / awfx->wSamplesPerBlock;
161 /***********************************************************************
162 * R16
164 * Read a 16 bit sample (correctly handles endianness)
166 static inline short R16(const unsigned char* src)
168 return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
171 /***********************************************************************
172 * W16
174 * Write a 16 bit sample (correctly handles endianness)
176 static inline void W16(unsigned char* dst, short s)
178 dst[0] = LOBYTE(s);
179 dst[1] = HIBYTE(s);
182 /* IMA (or DVI) APDCM codec routines */
184 static const unsigned IMA_StepTable[89] =
186 7, 8, 9, 10, 11, 12, 13, 14,
187 16, 17, 19, 21, 23, 25, 28, 31,
188 34, 37, 41, 45, 50, 55, 60, 66,
189 73, 80, 88, 97, 107, 118, 130, 143,
190 157, 173, 190, 209, 230, 253, 279, 307,
191 337, 371, 408, 449, 494, 544, 598, 658,
192 724, 796, 876, 963, 1060, 1166, 1282, 1411,
193 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
194 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
195 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
196 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
197 32767
200 static const int IMA_IndexTable[16] =
202 -1, -1, -1, -1, 2, 4, 6, 8,
203 -1, -1, -1, -1, 2, 4, 6, 8
206 static inline void clamp_step_index(int* stepIndex)
208 if (*stepIndex < 0 ) *stepIndex = 0;
209 if (*stepIndex > 88) *stepIndex = 88;
212 static inline void clamp_sample(int* sample)
214 if (*sample < -32768) *sample = -32768;
215 if (*sample > 32767) *sample = 32767;
218 static inline void process_nibble(unsigned char code, int* stepIndex, int* sample)
220 unsigned step;
221 int diff;
223 code &= 0x0F;
225 step = IMA_StepTable[*stepIndex];
226 diff = step >> 3;
227 if (code & 1) diff += step >> 2;
228 if (code & 2) diff += step >> 1;
229 if (code & 4) diff += step;
230 if (code & 8) *sample -= diff;
231 else *sample += diff;
232 clamp_sample(sample);
233 *stepIndex += IMA_IndexTable[code];
234 clamp_step_index(stepIndex);
237 static inline unsigned char generate_nibble(int in, int* stepIndex, int* sample)
239 int effdiff, diff = in - *sample;
240 unsigned step;
241 unsigned char code;
243 if (diff < 0)
245 diff = -diff;
246 code = 8;
248 else
250 code = 0;
253 step = IMA_StepTable[*stepIndex];
254 effdiff = (step >> 3);
255 if (diff >= step)
257 code |= 4;
258 diff -= step;
259 effdiff += step;
261 step >>= 1;
262 if (diff >= step)
264 code |= 2;
265 diff -= step;
266 effdiff += step;
268 step >>= 1;
269 if (diff >= step)
271 code |= 1;
272 effdiff += step;
274 if (code & 8) *sample -= effdiff;
275 else *sample += effdiff;
276 clamp_sample(sample);
277 *stepIndex += IMA_IndexTable[code];
278 clamp_step_index(stepIndex);
279 return code;
282 static void cvtSSima16K(PACMDRVSTREAMINSTANCE adsi,
283 const unsigned char* src, LPDWORD nsrc,
284 unsigned char* dst, LPDWORD ndst)
286 int i;
287 int sampleL, sampleR;
288 int stepIndexL, stepIndexR;
289 int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock;
290 int nsamp;
291 /* compute the number of entire blocks we can decode...
292 * it's the min of the number of entire blocks in source buffer and the number
293 * of entire blocks in destination buffer
295 DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
296 *ndst / (nsamp_blk * 2 * 2));
298 *nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
299 *ndst = nblock * (nsamp_blk * 2 * 2);
301 nsamp_blk--; /* remove the sample in block header */
302 for (; nblock > 0; nblock--)
304 const unsigned char* in_src = src;
306 /* handle headers first */
307 sampleL = R16(src);
308 stepIndexL = (unsigned)*(src + 2);
309 clamp_step_index(&stepIndexL);
310 src += 4;
311 W16(dst, sampleL); dst += 2;
313 sampleR = R16(src);
314 stepIndexR = (unsigned)*(src + 2);
315 clamp_step_index(&stepIndexR);
316 src += 4;
317 W16(dst, sampleR); dst += 2;
319 for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 8)
321 for (i = 0; i < 4; i++)
323 process_nibble(*src, &stepIndexL, &sampleL);
324 W16(dst + (2 * i + 0) * 4 + 0, sampleL);
325 process_nibble(*src++ >> 4, &stepIndexL, &sampleL);
326 W16(dst + (2 * i + 1) * 4 + 0, sampleL);
328 for (i = 0; i < 4; i++)
330 process_nibble(*src , &stepIndexR, &sampleR);
331 W16(dst + (2 * i + 0) * 4 + 2, sampleR);
332 process_nibble(*src++ >>4, &stepIndexR, &sampleR);
333 W16(dst + (2 * i + 1) * 4 + 2, sampleR);
335 dst += 32;
337 /* we have now to realign the source pointer on block */
338 src = in_src + adsi->pwfxSrc->nBlockAlign;
342 static void cvtMMima16K(PACMDRVSTREAMINSTANCE adsi,
343 const unsigned char* src, LPDWORD nsrc,
344 unsigned char* dst, LPDWORD ndst)
346 int sample;
347 int stepIndex;
348 int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock;
349 int nsamp;
350 /* compute the number of entire blocks we can decode...
351 * it's the min of the number of entire blocks in source buffer and the number
352 * of entire blocks in destination buffer
354 DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
355 *ndst / (nsamp_blk * 2));
357 *nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
358 *ndst = nblock * nsamp_blk * 2;
360 nsamp_blk--; /* remove the sample in block header */
361 for (; nblock > 0; nblock--)
363 const unsigned char* in_src = src;
365 /* handle header first */
366 sample = R16(src);
367 stepIndex = (unsigned)*(src + 2);
368 clamp_step_index(&stepIndex);
369 src += 4;
370 W16(dst, sample); dst += 2;
372 for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2)
374 process_nibble(*src, &stepIndex, &sample);
375 W16(dst, sample); dst += 2;
376 process_nibble(*src++ >> 4, &stepIndex, &sample);
377 W16(dst, sample); dst += 2;
379 /* we have now to realign the source pointer on block */
380 src = in_src + adsi->pwfxSrc->nBlockAlign;
384 static void cvtSS16imaK(PACMDRVSTREAMINSTANCE adsi,
385 const unsigned char* src, LPDWORD nsrc,
386 unsigned char* dst, LPDWORD ndst)
388 int stepIndexL, stepIndexR;
389 int sampleL, sampleR;
390 BYTE code1, code2;
391 int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock;
392 int i, nsamp;
393 /* compute the number of entire blocks we can decode...
394 * it's the min of the number of entire blocks in source buffer and the number
395 * of entire blocks in destination buffer
397 DWORD nblock = min(*nsrc / (nsamp_blk * 2 * 2),
398 *ndst / adsi->pwfxDst->nBlockAlign);
400 *nsrc = nblock * (nsamp_blk * 2 * 2);
401 *ndst = nblock * adsi->pwfxDst->nBlockAlign;
403 stepIndexL = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL;
404 stepIndexR = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexR;
406 nsamp_blk--; /* so that we won't count the sample in header while filling the block */
408 for (; nblock > 0; nblock--)
410 unsigned char* in_dst = dst;
412 /* generate header */
413 sampleL = R16(src); src += 2;
414 W16(dst, sampleL); dst += 2;
415 *dst = (unsigned char)(unsigned)stepIndexL;
416 dst += 2;
418 sampleR = R16(src); src += 2;
419 W16(dst, sampleR); dst += 2;
420 *dst = (unsigned char)(unsigned)stepIndexR;
421 dst += 2;
423 for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 8)
425 for (i = 0; i < 4; i++)
427 code1 = generate_nibble(R16(src + (2 * i + 0) * 2 + 0),
428 &stepIndexL, &sampleL);
429 code2 = generate_nibble(R16(src + (2 * i + 1) * 2 + 0),
430 &stepIndexL, &sampleL);
431 *dst++ = (code1 << 4) | code2;
433 for (i = 0; i < 4; i++)
435 code1 = generate_nibble(R16(src + (2 * i + 0) * 2 + 1),
436 &stepIndexR, &sampleR);
437 code2 = generate_nibble(R16(src + (2 * i + 1) * 2 + 1),
438 &stepIndexR, &sampleR);
439 *dst++ = (code1 << 4) | code2;
441 src += 32;
443 dst = in_dst + adsi->pwfxDst->nBlockAlign;
445 ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL = stepIndexL;
446 ((AcmAdpcmData*)adsi->dwDriver)->stepIndexR = stepIndexR;
449 static void cvtMM16imaK(PACMDRVSTREAMINSTANCE adsi,
450 const unsigned char* src, LPDWORD nsrc,
451 unsigned char* dst, LPDWORD ndst)
453 int stepIndex;
454 int sample;
455 BYTE code1, code2;
456 int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock;
457 int nsamp;
458 /* compute the number of entire blocks we can decode...
459 * it's the min of the number of entire blocks in source buffer and the number
460 * of entire blocks in destination buffer
462 DWORD nblock = min(*nsrc / (nsamp_blk * 2),
463 *ndst / adsi->pwfxDst->nBlockAlign);
465 *nsrc = nblock * (nsamp_blk * 2);
466 *ndst = nblock * adsi->pwfxDst->nBlockAlign;
468 stepIndex = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL;
469 nsamp_blk--; /* so that we won't count the sample in header while filling the block */
471 for (; nblock > 0; nblock--)
473 unsigned char* in_dst = dst;
475 /* generate header */
476 /* FIXME: what about the last effective sample from previous block ??? */
477 /* perhaps something like:
478 * sample += R16(src);
479 * clamp_sample(sample);
480 * and with :
481 * + saving the sample in adsi->dwDriver when all blocks are done
482 + + reset should set the field in adsi->dwDriver to 0 too
484 sample = R16(src); src += 2;
485 W16(dst, sample); dst += 2;
486 *dst = (unsigned char)(unsigned)stepIndex;
487 dst += 2;
489 for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2)
491 code1 = generate_nibble(R16(src), &stepIndex, &sample);
492 src += 2;
493 code2 = generate_nibble(R16(src), &stepIndex, &sample);
494 src += 2;
495 *dst++ = (code1 << 4) | code2;
497 dst = in_dst + adsi->pwfxDst->nBlockAlign;
499 ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL = stepIndex;
502 /***********************************************************************
503 * ADPCM_DriverDetails
506 static LRESULT ADPCM_DriverDetails(PACMDRIVERDETAILSW add)
508 add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
509 add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
510 add->wMid = 0x1;
511 add->wPid = 0x22;
512 add->vdwACM = 0x3320000;
513 add->vdwDriver = 0x04000000;
514 add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
515 add->cFormatTags = 2; /* PCM, IMA ADPCM */
516 add->cFilterTags = 0;
517 add->hicon = NULL;
518 MultiByteToWideChar( CP_ACP, 0, "Microsoft IMA ADPCM", -1,
519 add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
520 MultiByteToWideChar( CP_ACP, 0, "Microsoft IMA ADPCM CODEC", -1,
521 add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
522 MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1,
523 add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) );
524 MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1,
525 add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) );
526 add->szFeatures[0] = 0;
528 return MMSYSERR_NOERROR;
531 /***********************************************************************
532 * ADPCM_FormatTagDetails
535 static LRESULT ADPCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
537 static const WCHAR szPcm[]={'P','C','M',0};
538 static const WCHAR szImaAdPcm[]={'I','M','A',' ','A','D','P','C','M',0};
540 switch (dwQuery)
542 case ACM_FORMATTAGDETAILSF_INDEX:
543 if (aftd->dwFormatTagIndex >= 2) return ACMERR_NOTPOSSIBLE;
544 break;
545 case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
546 if (aftd->dwFormatTag == WAVE_FORMAT_UNKNOWN)
548 aftd->dwFormatTagIndex = 1; /* WAVE_FORMAT_IMA_ADPCM is bigger than PCM */
549 break;
551 /* fall thru */
552 case ACM_FORMATTAGDETAILSF_FORMATTAG:
553 switch (aftd->dwFormatTag)
555 case WAVE_FORMAT_PCM: aftd->dwFormatTagIndex = 0; break;
556 case WAVE_FORMAT_IMA_ADPCM: aftd->dwFormatTagIndex = 1; break;
557 default: return ACMERR_NOTPOSSIBLE;
559 break;
560 default:
561 WARN("Unsupported query %08x\n", dwQuery);
562 return MMSYSERR_NOTSUPPORTED;
565 aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
566 switch (aftd->dwFormatTagIndex)
568 case 0:
569 aftd->dwFormatTag = WAVE_FORMAT_PCM;
570 aftd->cbFormatSize = sizeof(PCMWAVEFORMAT);
571 aftd->cStandardFormats = NUM_PCM_FORMATS;
572 lstrcpyW(aftd->szFormatTag, szPcm);
573 break;
574 case 1:
575 aftd->dwFormatTag = WAVE_FORMAT_IMA_ADPCM;
576 aftd->cbFormatSize = sizeof(IMAADPCMWAVEFORMAT);
577 aftd->cStandardFormats = NUM_ADPCM_FORMATS;
578 lstrcpyW(aftd->szFormatTag, szImaAdPcm);
579 break;
581 return MMSYSERR_NOERROR;
584 /***********************************************************************
585 * ADPCM_FormatDetails
588 static LRESULT ADPCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
590 switch (dwQuery)
592 case ACM_FORMATDETAILSF_FORMAT:
593 if (ADPCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
594 break;
595 case ACM_FORMATDETAILSF_INDEX:
596 afd->pwfx->wFormatTag = afd->dwFormatTag;
597 switch (afd->dwFormatTag)
599 case WAVE_FORMAT_PCM:
600 if (afd->dwFormatIndex >= NUM_PCM_FORMATS) return ACMERR_NOTPOSSIBLE;
601 afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels;
602 afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate;
603 afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits;
604 /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not accessible
605 * afd->pwfx->cbSize = 0;
607 afd->pwfx->nBlockAlign =
608 (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
609 afd->pwfx->nAvgBytesPerSec =
610 afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
611 break;
612 case WAVE_FORMAT_IMA_ADPCM:
613 if (afd->dwFormatIndex >= NUM_ADPCM_FORMATS) return ACMERR_NOTPOSSIBLE;
614 afd->pwfx->nChannels = ADPCM_Formats[afd->dwFormatIndex].nChannels;
615 afd->pwfx->nSamplesPerSec = ADPCM_Formats[afd->dwFormatIndex].rate;
616 afd->pwfx->wBitsPerSample = ADPCM_Formats[afd->dwFormatIndex].nBits;
617 afd->pwfx->nBlockAlign = 1024;
618 /* we got 4 bits per sample */
619 afd->pwfx->nAvgBytesPerSec =
620 (afd->pwfx->nSamplesPerSec * 4) / 8;
621 if (afd->cbwfx >= sizeof(WAVEFORMATEX))
622 afd->pwfx->cbSize = sizeof(WORD);
623 if (afd->cbwfx >= sizeof(IMAADPCMWAVEFORMAT))
624 ((IMAADPCMWAVEFORMAT*)afd->pwfx)->wSamplesPerBlock = (1024 - 4 * afd->pwfx->nChannels) * (2 / afd->pwfx->nChannels) + 1;
625 break;
626 default:
627 WARN("Unsupported tag %08x\n", afd->dwFormatTag);
628 return MMSYSERR_INVALPARAM;
630 break;
631 default:
632 WARN("Unsupported query %08x\n", dwQuery);
633 return MMSYSERR_NOTSUPPORTED;
635 afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
636 afd->szFormat[0] = 0; /* let MSACM format this for us... */
638 return MMSYSERR_NOERROR;
641 /***********************************************************************
642 * ADPCM_FormatSuggest
645 static LRESULT ADPCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
647 /* some tests ... */
648 if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) ||
649 adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
650 adfs->pwfxSrc->wFormatTag == adfs->pwfxDst->wFormatTag ||
651 ADPCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
653 /* If no suggestion for destination, then copy source value */
654 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS))
655 adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
656 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC))
657 adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
659 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE))
661 if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
662 adfs->pwfxDst->wBitsPerSample = 4;
663 else
664 adfs->pwfxDst->wBitsPerSample = 16;
666 if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG))
668 if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
669 adfs->pwfxDst->wFormatTag = WAVE_FORMAT_IMA_ADPCM;
670 else
671 adfs->pwfxDst->wFormatTag = WAVE_FORMAT_PCM;
674 /* recompute other values */
675 switch (adfs->pwfxDst->wFormatTag)
677 case WAVE_FORMAT_PCM:
678 if (adfs->cbwfxSrc < sizeof(IMAADPCMWAVEFORMAT)) return ACMERR_NOTPOSSIBLE;
679 adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8;
680 adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign;
681 /* check if result is ok */
682 if (ADPCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
683 break;
684 case WAVE_FORMAT_IMA_ADPCM:
685 if (adfs->cbwfxDst < sizeof(IMAADPCMWAVEFORMAT)) return ACMERR_NOTPOSSIBLE;
686 init_wfx_ima_adpcm((IMAADPCMWAVEFORMAT*)adfs->pwfxDst);
687 /* FIXME: not handling header overhead */
688 TRACE("setting spb=%u\n", ((IMAADPCMWAVEFORMAT*)adfs->pwfxDst)->wSamplesPerBlock);
689 /* check if result is ok */
690 if (ADPCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
691 break;
692 default:
693 return ACMERR_NOTPOSSIBLE;
696 return MMSYSERR_NOERROR;
699 /***********************************************************************
700 * ADPCM_Reset
703 static void ADPCM_Reset(PACMDRVSTREAMINSTANCE adsi, AcmAdpcmData* aad)
705 aad->stepIndexL = aad->stepIndexR = 0;
708 /***********************************************************************
709 * ADPCM_StreamOpen
712 static LRESULT ADPCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
714 AcmAdpcmData* aad;
715 unsigned nspb;
717 assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
719 if (ADPCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF ||
720 ADPCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF)
721 return ACMERR_NOTPOSSIBLE;
723 aad = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmAdpcmData));
724 if (aad == 0) return MMSYSERR_NOMEM;
726 adsi->dwDriver = (DWORD_PTR)aad;
728 if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
729 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
731 goto theEnd;
733 else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM &&
734 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
736 /* resampling or mono <=> stereo not available
737 * ADPCM algo only define 16 bit per sample output
739 if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec ||
740 adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels ||
741 adsi->pwfxDst->wBitsPerSample != 16)
742 goto theEnd;
744 nspb = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock;
745 TRACE("spb=%u\n", nspb);
747 /* we check that in a block, after the header, samples are present on
748 * 4-sample packet pattern
749 * we also check that the block alignment is bigger than the expected size
751 if (((nspb - 1) & 3) != 0) goto theEnd;
752 if ((((nspb - 1) / 2) + 4) * adsi->pwfxSrc->nChannels < adsi->pwfxSrc->nBlockAlign)
753 goto theEnd;
755 /* adpcm decoding... */
756 if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 2)
757 aad->convert = cvtSSima16K;
758 if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 1)
759 aad->convert = cvtMMima16K;
761 else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
762 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM)
764 if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec ||
765 adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels ||
766 adsi->pwfxSrc->wBitsPerSample != 16)
767 goto theEnd;
769 nspb = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock;
770 TRACE("spb=%u\n", nspb);
772 /* we check that in a block, after the header, samples are present on
773 * 4-sample packet pattern
774 * we also check that the block alignment is bigger than the expected size
776 if (((nspb - 1) & 3) != 0) goto theEnd;
777 if ((((nspb - 1) / 2) + 4) * adsi->pwfxDst->nChannels < adsi->pwfxDst->nBlockAlign)
778 goto theEnd;
780 /* adpcm coding... */
781 if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 2)
782 aad->convert = cvtSS16imaK;
783 if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 1)
784 aad->convert = cvtMM16imaK;
786 else goto theEnd;
787 ADPCM_Reset(adsi, aad);
789 return MMSYSERR_NOERROR;
791 theEnd:
792 HeapFree(GetProcessHeap(), 0, aad);
793 adsi->dwDriver = 0L;
794 return MMSYSERR_NOTSUPPORTED;
797 /***********************************************************************
798 * ADPCM_StreamClose
801 static LRESULT ADPCM_StreamClose(PACMDRVSTREAMINSTANCE adsi)
803 HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
804 return MMSYSERR_NOERROR;
807 /***********************************************************************
808 * ADPCM_StreamSize
811 static LRESULT ADPCM_StreamSize(const ACMDRVSTREAMINSTANCE *adsi, PACMDRVSTREAMSIZE adss)
813 DWORD nblocks;
815 switch (adss->fdwSize)
817 case ACM_STREAMSIZEF_DESTINATION:
818 /* cbDstLength => cbSrcLength */
819 if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
820 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM)
822 nblocks = adss->cbDstLength / adsi->pwfxDst->nBlockAlign;
823 if (nblocks == 0)
824 return ACMERR_NOTPOSSIBLE;
825 adss->cbSrcLength = nblocks * adsi->pwfxSrc->nBlockAlign * ((IMAADPCMWAVEFORMAT*)adsi->pwfxDst)->wSamplesPerBlock;
827 else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM &&
828 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
830 nblocks = adss->cbDstLength / (adsi->pwfxDst->nBlockAlign * ((IMAADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock);
831 if (nblocks == 0)
832 return ACMERR_NOTPOSSIBLE;
833 adss->cbSrcLength = nblocks * adsi->pwfxSrc->nBlockAlign;
835 else
837 return MMSYSERR_NOTSUPPORTED;
839 break;
840 case ACM_STREAMSIZEF_SOURCE:
841 /* cbSrcLength => cbDstLength */
842 if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
843 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM)
845 nblocks = adss->cbSrcLength / (adsi->pwfxSrc->nBlockAlign * ((IMAADPCMWAVEFORMAT*)adsi->pwfxDst)->wSamplesPerBlock);
846 if (nblocks == 0)
847 return ACMERR_NOTPOSSIBLE;
848 if (adss->cbSrcLength % (adsi->pwfxSrc->nBlockAlign * ((IMAADPCMWAVEFORMAT*)adsi->pwfxDst)->wSamplesPerBlock))
849 /* Round block count up. */
850 nblocks++;
851 adss->cbDstLength = nblocks * adsi->pwfxDst->nBlockAlign;
853 else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM &&
854 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
856 nblocks = adss->cbSrcLength / adsi->pwfxSrc->nBlockAlign;
857 if (nblocks == 0)
858 return ACMERR_NOTPOSSIBLE;
859 if (adss->cbSrcLength % adsi->pwfxSrc->nBlockAlign)
860 /* Round block count up. */
861 nblocks++;
862 adss->cbDstLength = nblocks * adsi->pwfxDst->nBlockAlign * ((IMAADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock;
864 else
866 return MMSYSERR_NOTSUPPORTED;
868 break;
869 default:
870 WARN("Unsupported query %08x\n", adss->fdwSize);
871 return MMSYSERR_NOTSUPPORTED;
873 return MMSYSERR_NOERROR;
876 /***********************************************************************
877 * ADPCM_StreamConvert
880 static LRESULT ADPCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
882 AcmAdpcmData* aad = (AcmAdpcmData*)adsi->dwDriver;
883 DWORD nsrc = adsh->cbSrcLength;
884 DWORD ndst = adsh->cbDstLength;
886 if (adsh->fdwConvert &
887 ~(ACM_STREAMCONVERTF_BLOCKALIGN|
888 ACM_STREAMCONVERTF_END|
889 ACM_STREAMCONVERTF_START))
891 FIXME("Unsupported fdwConvert (%08x), ignoring it\n", adsh->fdwConvert);
893 /* ACM_STREAMCONVERTF_BLOCKALIGN
894 * currently all conversions are block aligned, so do nothing for this flag
895 * ACM_STREAMCONVERTF_END
896 * no pending data, so do nothing for this flag
898 if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START))
900 ADPCM_Reset(adsi, aad);
903 aad->convert(adsi, adsh->pbSrc, &nsrc, adsh->pbDst, &ndst);
904 adsh->cbSrcLengthUsed = nsrc;
905 adsh->cbDstLengthUsed = ndst;
907 return MMSYSERR_NOERROR;
910 /**************************************************************************
911 * ADPCM_DriverProc [exported]
913 LRESULT CALLBACK ADPCM_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
914 LPARAM dwParam1, LPARAM dwParam2)
916 TRACE("(%08lx %p %04x %08lx %08lx);\n",
917 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
919 switch (wMsg)
921 case DRV_LOAD: return 1;
922 case DRV_FREE: return 1;
923 case DRV_OPEN: return 1;
924 case DRV_CLOSE: return ADPCM_drvClose(dwDevID);
925 case DRV_ENABLE: return 1;
926 case DRV_DISABLE: return 1;
927 case DRV_QUERYCONFIGURE: return 1;
928 case DRV_CONFIGURE: MessageBoxA(0, "MSACM IMA ADPCM filter !", "Wine Driver", MB_OK); return 1;
929 case DRV_INSTALL: return DRVCNF_RESTART;
930 case DRV_REMOVE: return DRVCNF_RESTART;
932 case ACMDM_DRIVER_NOTIFY:
933 /* no caching from other ACM drivers is done so far */
934 return MMSYSERR_NOERROR;
936 case ACMDM_DRIVER_DETAILS:
937 return ADPCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1);
939 case ACMDM_FORMATTAG_DETAILS:
940 return ADPCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2);
942 case ACMDM_FORMAT_DETAILS:
943 return ADPCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2);
945 case ACMDM_FORMAT_SUGGEST:
946 return ADPCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1);
948 case ACMDM_STREAM_OPEN:
949 return ADPCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1);
951 case ACMDM_STREAM_CLOSE:
952 return ADPCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1);
954 case ACMDM_STREAM_SIZE:
955 return ADPCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2);
957 case ACMDM_STREAM_CONVERT:
958 return ADPCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2);
960 case ACMDM_HARDWARE_WAVE_CAPS_INPUT:
961 case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT:
962 /* this converter is not a hardware driver */
963 case ACMDM_FILTERTAG_DETAILS:
964 case ACMDM_FILTER_DETAILS:
965 /* this converter is not a filter */
966 case ACMDM_STREAM_RESET:
967 /* only needed for asynchronous driver... we aren't, so just say it */
968 return MMSYSERR_NOTSUPPORTED;
969 case ACMDM_STREAM_PREPARE:
970 case ACMDM_STREAM_UNPREPARE:
971 /* nothing special to do here... so don't do anything */
972 return MMSYSERR_NOERROR;
974 default:
975 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);