2 * GSM 06.10 codec handling
3 * Copyright (C) 2009 Maarten Lankhorst
6 * Copyright (C) 2002 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define WIN32_NO_STATUS
26 //#include <wine/port.h>
34 #elif defined(HAVE_GSM_H)
43 //#include "mmsystem.h"
47 //#include "wine/library.h"
48 #include <wine/debug.h>
50 WINE_DEFAULT_DEBUG_CHANNEL(gsm
);
54 static void *libgsm_handle
;
55 #define FUNCPTR(f) static typeof(f) * p##f
62 #define LOAD_FUNCPTR(f) \
63 if((p##f = wine_dlsym(libgsm_handle, #f, NULL, 0)) == NULL) { \
64 wine_dlclose(libgsm_handle, NULL, 0); \
65 libgsm_handle = NULL; \
69 /***********************************************************************
72 static BOOL
GSM_drvLoad(void)
76 libgsm_handle
= wine_dlopen(SONAME_LIBGSM
, RTLD_NOW
, error
, sizeof(error
));
79 LOAD_FUNCPTR(gsm_create
);
80 LOAD_FUNCPTR(gsm_destroy
);
81 LOAD_FUNCPTR(gsm_option
);
82 LOAD_FUNCPTR(gsm_encode
);
83 LOAD_FUNCPTR(gsm_decode
);
88 ERR("Couldn't load " SONAME_LIBGSM
": %s\n", error
);
93 /***********************************************************************
96 static LRESULT
GSM_drvFree(void)
99 wine_dlclose(libgsm_handle
, NULL
, 0);
105 static LRESULT
GSM_drvFree(void)
112 /***********************************************************************
116 static LRESULT
GSM_DriverDetails(PACMDRIVERDETAILSW add
)
118 add
->fccType
= ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC
;
119 add
->fccComp
= ACMDRIVERDETAILS_FCCCOMP_UNDEFINED
;
120 /* Details found from probing native msgsm32.acm */
121 add
->wMid
= MM_MICROSOFT
;
123 add
->vdwACM
= 0x3320000;
124 add
->vdwDriver
= 0x4000000;
125 add
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
126 add
->cFormatTags
= 2;
127 add
->cFilterTags
= 0;
129 MultiByteToWideChar( CP_ACP
, 0, "Wine GSM 6.10", -1,
130 add
->szShortName
, sizeof(add
->szShortName
)/sizeof(WCHAR
) );
131 MultiByteToWideChar( CP_ACP
, 0, "Wine GSM 6.10 libgsm codec", -1,
132 add
->szLongName
, sizeof(add
->szLongName
)/sizeof(WCHAR
) );
133 MultiByteToWideChar( CP_ACP
, 0, "Brought to you by the Wine team...", -1,
134 add
->szCopyright
, sizeof(add
->szCopyright
)/sizeof(WCHAR
) );
135 MultiByteToWideChar( CP_ACP
, 0, "Refer to LICENSE file", -1,
136 add
->szLicensing
, sizeof(add
->szLicensing
)/sizeof(WCHAR
) );
137 add
->szFeatures
[0] = 0;
138 return MMSYSERR_NOERROR
;
141 /* Validate a WAVEFORMATEX structure */
142 static BOOL
GSM_FormatValidate(const WAVEFORMATEX
*wfx
)
144 if (wfx
->nChannels
!= 1)
147 switch (wfx
->wFormatTag
)
149 case WAVE_FORMAT_PCM
:
150 if (wfx
->wBitsPerSample
!= 16)
152 WARN("PCM wBitsPerSample %u\n", wfx
->wBitsPerSample
);
155 if (wfx
->nBlockAlign
!= 2)
157 WARN("PCM nBlockAlign %u\n", wfx
->nBlockAlign
);
160 if (wfx
->nAvgBytesPerSec
!= wfx
->nBlockAlign
* wfx
->nSamplesPerSec
)
162 WARN("PCM nAvgBytesPerSec %u/%u\n",
163 wfx
->nAvgBytesPerSec
,
164 wfx
->nBlockAlign
* wfx
->nSamplesPerSec
);
168 case WAVE_FORMAT_GSM610
:
169 if (wfx
->cbSize
< sizeof(WORD
))
171 WARN("GSM cbSize %u\n", wfx
->cbSize
);
174 if (wfx
->wBitsPerSample
!= 0)
176 WARN("GSM wBitsPerSample %u\n", wfx
->wBitsPerSample
);
179 if (wfx
->nBlockAlign
!= 65)
181 WARN("GSM nBlockAlign %u\n", wfx
->nBlockAlign
);
184 if (((const GSM610WAVEFORMAT
*)wfx
)->wSamplesPerBlock
!= 320)
186 WARN("GSM wSamplesPerBlock %u\n",
187 ((const GSM610WAVEFORMAT
*)wfx
)->wSamplesPerBlock
);
190 if (wfx
->nAvgBytesPerSec
!= wfx
->nSamplesPerSec
* 65 / 320)
192 WARN("GSM nAvgBytesPerSec %d / %d\n",
193 wfx
->nAvgBytesPerSec
, wfx
->nSamplesPerSec
* 65 / 320);
203 static const DWORD gsm_rates
[] = { 8000, 11025, 22050, 44100, 48000, 96000 };
204 #define NUM_RATES (sizeof(gsm_rates)/sizeof(*gsm_rates))
206 /***********************************************************************
207 * GSM_FormatTagDetails
210 static LRESULT
GSM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd
, DWORD dwQuery
)
212 static const WCHAR szPcm
[]={'P','C','M',0};
213 static const WCHAR szGsm
[]={'G','S','M',' ','6','.','1','0',0};
217 case ACM_FORMATTAGDETAILSF_INDEX
:
218 if (aftd
->dwFormatTagIndex
> 1) return ACMERR_NOTPOSSIBLE
;
220 case ACM_FORMATTAGDETAILSF_LARGESTSIZE
:
221 if (aftd
->dwFormatTag
== WAVE_FORMAT_UNKNOWN
)
223 aftd
->dwFormatTagIndex
= 1;
227 case ACM_FORMATTAGDETAILSF_FORMATTAG
:
228 switch (aftd
->dwFormatTag
)
230 case WAVE_FORMAT_PCM
: aftd
->dwFormatTagIndex
= 0; break;
231 case WAVE_FORMAT_GSM610
: aftd
->dwFormatTagIndex
= 1; break;
232 default: return ACMERR_NOTPOSSIBLE
;
236 WARN("Unsupported query %08x\n", dwQuery
);
237 return MMSYSERR_NOTSUPPORTED
;
240 aftd
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
241 switch (aftd
->dwFormatTagIndex
)
244 aftd
->dwFormatTag
= WAVE_FORMAT_PCM
;
245 aftd
->cbFormatSize
= sizeof(PCMWAVEFORMAT
);
246 aftd
->cStandardFormats
= NUM_RATES
;
247 lstrcpyW(aftd
->szFormatTag
, szPcm
);
250 aftd
->dwFormatTag
= WAVE_FORMAT_GSM610
;
251 aftd
->cbFormatSize
= sizeof(GSM610WAVEFORMAT
);
252 aftd
->cStandardFormats
= NUM_RATES
;
253 lstrcpyW(aftd
->szFormatTag
, szGsm
);
256 return MMSYSERR_NOERROR
;
259 /***********************************************************************
263 static LRESULT
GSM_FormatDetails(PACMFORMATDETAILSW afd
, DWORD dwQuery
)
267 case ACM_FORMATDETAILSF_FORMAT
:
268 if (!GSM_FormatValidate(afd
->pwfx
)) return ACMERR_NOTPOSSIBLE
;
270 case ACM_FORMATDETAILSF_INDEX
:
271 afd
->pwfx
->wFormatTag
= afd
->dwFormatTag
;
272 switch (afd
->dwFormatTag
)
274 case WAVE_FORMAT_PCM
:
275 if (afd
->dwFormatIndex
>= NUM_RATES
) return ACMERR_NOTPOSSIBLE
;
276 afd
->pwfx
->nChannels
= 1;
277 afd
->pwfx
->nSamplesPerSec
= gsm_rates
[afd
->dwFormatIndex
];
278 afd
->pwfx
->wBitsPerSample
= 16;
279 afd
->pwfx
->nBlockAlign
= 2;
280 afd
->pwfx
->nAvgBytesPerSec
= afd
->pwfx
->nSamplesPerSec
* afd
->pwfx
->nBlockAlign
;
282 case WAVE_FORMAT_GSM610
:
283 if (afd
->dwFormatIndex
>= NUM_RATES
) return ACMERR_NOTPOSSIBLE
;
284 afd
->pwfx
->nChannels
= 1;
285 afd
->pwfx
->nSamplesPerSec
= gsm_rates
[afd
->dwFormatIndex
];
286 afd
->pwfx
->wBitsPerSample
= 0;
287 afd
->pwfx
->nBlockAlign
= 65;
288 afd
->pwfx
->nAvgBytesPerSec
= afd
->pwfx
->nSamplesPerSec
* 65 / 320;
289 afd
->pwfx
->cbSize
= sizeof(WORD
);
290 ((GSM610WAVEFORMAT
*)afd
->pwfx
)->wSamplesPerBlock
= 320;
293 WARN("Unsupported tag %08x\n", afd
->dwFormatTag
);
294 return MMSYSERR_INVALPARAM
;
298 WARN("Unsupported query %08x\n", dwQuery
);
299 return MMSYSERR_NOTSUPPORTED
;
301 afd
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
302 afd
->szFormat
[0] = 0; /* let MSACM format this for us... */
304 return MMSYSERR_NOERROR
;
307 /***********************************************************************
311 static LRESULT
GSM_FormatSuggest(PACMDRVFORMATSUGGEST adfs
)
314 if (adfs
->cbwfxSrc
< sizeof(PCMWAVEFORMAT
) ||
315 adfs
->cbwfxDst
< sizeof(PCMWAVEFORMAT
) ||
316 !GSM_FormatValidate(adfs
->pwfxSrc
)) return ACMERR_NOTPOSSIBLE
;
317 /* FIXME: should do those tests against the real size (according to format tag */
319 /* If no suggestion for destination, then copy source value */
320 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_NCHANNELS
))
321 adfs
->pwfxDst
->nChannels
= adfs
->pwfxSrc
->nChannels
;
322 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_NSAMPLESPERSEC
))
323 adfs
->pwfxDst
->nSamplesPerSec
= adfs
->pwfxSrc
->nSamplesPerSec
;
325 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_WBITSPERSAMPLE
))
327 if (adfs
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
)
328 adfs
->pwfxDst
->wBitsPerSample
= 0;
330 adfs
->pwfxDst
->wBitsPerSample
= 16;
332 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_WFORMATTAG
))
334 switch (adfs
->pwfxSrc
->wFormatTag
)
336 case WAVE_FORMAT_PCM
: adfs
->pwfxDst
->wFormatTag
= WAVE_FORMAT_GSM610
; break;
337 case WAVE_FORMAT_GSM610
: adfs
->pwfxDst
->wFormatTag
= WAVE_FORMAT_PCM
; break;
341 /* recompute other values */
342 switch (adfs
->pwfxDst
->wFormatTag
)
344 case WAVE_FORMAT_PCM
:
345 adfs
->pwfxDst
->nBlockAlign
= 2;
346 adfs
->pwfxDst
->nAvgBytesPerSec
= adfs
->pwfxDst
->nSamplesPerSec
* 2;
348 case WAVE_FORMAT_GSM610
:
349 if (adfs
->pwfxDst
->cbSize
< sizeof(WORD
))
350 return ACMERR_NOTPOSSIBLE
;
351 adfs
->pwfxDst
->nBlockAlign
= 65;
352 adfs
->pwfxDst
->nAvgBytesPerSec
= adfs
->pwfxDst
->nSamplesPerSec
* 65 / 320;
353 ((GSM610WAVEFORMAT
*)adfs
->pwfxDst
)->wSamplesPerBlock
= 320;
356 return ACMERR_NOTPOSSIBLE
;
359 /* check if result is ok */
360 if (!GSM_FormatValidate(adfs
->pwfxDst
)) return ACMERR_NOTPOSSIBLE
;
361 return MMSYSERR_NOERROR
;
365 /***********************************************************************
369 static LRESULT
GSM_StreamOpen(PACMDRVSTREAMINSTANCE adsi
)
373 if (!GSM_FormatValidate(adsi
->pwfxSrc
) || !GSM_FormatValidate(adsi
->pwfxDst
))
374 return MMSYSERR_NOTSUPPORTED
;
376 if (adsi
->pwfxSrc
->nSamplesPerSec
!= adsi
->pwfxDst
->nSamplesPerSec
)
377 return MMSYSERR_NOTSUPPORTED
;
379 if (!GSM_drvLoad()) return MMSYSERR_NOTSUPPORTED
;
383 return MMSYSERR_NOMEM
;
384 if (pgsm_option(r
, GSM_OPT_WAV49
, &used
) < 0)
386 FIXME("Your libgsm library doesn't support GSM_OPT_WAV49\n");
387 FIXME("Please recompile libgsm with WAV49 support\n");
389 return MMSYSERR_NOTSUPPORTED
;
391 adsi
->dwDriver
= (DWORD_PTR
)r
;
392 return MMSYSERR_NOERROR
;
395 /***********************************************************************
399 static LRESULT
GSM_StreamClose(PACMDRVSTREAMINSTANCE adsi
)
401 pgsm_destroy((gsm
)adsi
->dwDriver
);
402 return MMSYSERR_NOERROR
;
405 /***********************************************************************
409 static LRESULT
GSM_StreamSize(const ACMDRVSTREAMINSTANCE
*adsi
, PACMDRVSTREAMSIZE adss
)
411 switch (adss
->fdwSize
)
413 case ACM_STREAMSIZEF_DESTINATION
:
414 /* cbDstLength => cbSrcLength */
415 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
&&
416 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_GSM610
)
418 adss
->cbSrcLength
= adss
->cbDstLength
/ 65 * 640;
420 else if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
&&
421 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_PCM
)
423 adss
->cbSrcLength
= adss
->cbDstLength
/ 640 * 65;
427 return MMSYSERR_NOTSUPPORTED
;
429 return MMSYSERR_NOERROR
;
430 case ACM_STREAMSIZEF_SOURCE
:
431 /* cbSrcLength => cbDstLength */
432 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
&&
433 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_GSM610
)
435 adss
->cbDstLength
= (adss
->cbSrcLength
+ 639) / 640 * 65;
437 else if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
&&
438 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_PCM
)
440 adss
->cbDstLength
= adss
->cbSrcLength
/ 65 * 640;
444 return MMSYSERR_NOTSUPPORTED
;
446 return MMSYSERR_NOERROR
;
448 WARN("Unsupported query %08x\n", adss
->fdwSize
);
449 return MMSYSERR_NOTSUPPORTED
;
453 /***********************************************************************
457 static LRESULT
GSM_StreamConvert(PACMDRVSTREAMINSTANCE adsi
, PACMDRVSTREAMHEADER adsh
)
459 gsm r
= (gsm
)adsi
->dwDriver
;
462 BYTE
*src
= adsh
->pbSrc
;
463 BYTE
*dst
= adsh
->pbDst
;
466 if (adsh
->fdwConvert
&
467 ~(ACM_STREAMCONVERTF_BLOCKALIGN
|
468 ACM_STREAMCONVERTF_END
|
469 ACM_STREAMCONVERTF_START
))
471 FIXME("Unsupported fdwConvert (%08x), ignoring it\n", adsh
->fdwConvert
);
474 /* Reset the index to 0, just to be sure */
475 pgsm_option(r
, GSM_OPT_FRAME_INDEX
, &odd
);
477 /* The native ms codec writes 65 bytes, and this requires 2 libgsm calls.
478 * First 32 bytes are written, or 33 bytes read
479 * Second 33 bytes are written, or 32 bytes read
483 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
)
485 if (adsh
->cbSrcLength
/ 65 * 640 > adsh
->cbDstLength
)
487 return ACMERR_NOTPOSSIBLE
;
490 while (nsrc
+ 65 <= adsh
->cbSrcLength
)
493 if (pgsm_decode(r
, src
+ nsrc
, (gsm_signal
*)(dst
+ ndst
)) < 0)
494 FIXME("Couldn't decode data\n");
498 if (pgsm_decode(r
, src
+ nsrc
, (gsm_signal
*)(dst
+ ndst
)) < 0)
499 FIXME("Couldn't decode data\n");
506 /* Testing a little seems to reveal that despite being able to fit
507 * inside the buffer if ACM_STREAMCONVERTF_BLOCKALIGN is set
510 if ((adsh
->cbSrcLength
+ 639) / 640 * 65 > adsh
->cbDstLength
)
512 return ACMERR_NOTPOSSIBLE
;
515 /* The packing algorithm writes 32 bytes, then 33 bytes,
516 * and it seems to pad to align to 65 bytes always
517 * adding extra data where necessary
519 while (nsrc
+ 640 <= adsh
->cbSrcLength
)
522 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
525 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
530 /* If ACM_STREAMCONVERTF_BLOCKALIGN isn't set pad with zeros */
531 if (!(adsh
->fdwConvert
& ACM_STREAMCONVERTF_BLOCKALIGN
) &&
532 nsrc
< adsh
->cbSrcLength
)
535 int todo
= adsh
->cbSrcLength
- nsrc
;
539 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
544 memcpy(emptiness
, src
+nsrc
, todo
);
545 memset(emptiness
+ todo
, 0, 320 - todo
);
546 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
551 memcpy(emptiness
, src
+nsrc
, todo
);
552 memset(emptiness
+ todo
, 0, 320 - todo
);
553 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
556 memset(emptiness
, 0, todo
);
557 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
560 nsrc
= adsh
->cbSrcLength
;
564 adsh
->cbSrcLengthUsed
= nsrc
;
565 adsh
->cbDstLengthUsed
= ndst
;
566 TRACE("%d(%d) -> %d(%d)\n", nsrc
, adsh
->cbSrcLength
, ndst
, adsh
->cbDstLength
);
567 return MMSYSERR_NOERROR
;
572 /**************************************************************************
573 * GSM_DriverProc [exported]
575 LRESULT CALLBACK
GSM_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
576 LPARAM dwParam1
, LPARAM dwParam2
)
578 TRACE("(%08lx %p %04x %08lx %08lx);\n",
579 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
583 case DRV_LOAD
: return 1;
584 case DRV_FREE
: return GSM_drvFree();
585 case DRV_OPEN
: return 1;
586 case DRV_CLOSE
: return 1;
587 case DRV_ENABLE
: return 1;
588 case DRV_DISABLE
: return 1;
589 case DRV_QUERYCONFIGURE
: return 1;
590 case DRV_CONFIGURE
: MessageBoxA(0, "GSM 06.10 codec", "Wine Driver", MB_OK
); return 1;
591 case DRV_INSTALL
: return DRVCNF_RESTART
;
592 case DRV_REMOVE
: return DRVCNF_RESTART
;
594 case ACMDM_DRIVER_NOTIFY
:
595 /* no caching from other ACM drivers is done so far */
596 return MMSYSERR_NOERROR
;
598 case ACMDM_DRIVER_DETAILS
:
599 return GSM_DriverDetails((PACMDRIVERDETAILSW
)dwParam1
);
601 case ACMDM_FORMATTAG_DETAILS
:
602 return GSM_FormatTagDetails((PACMFORMATTAGDETAILSW
)dwParam1
, dwParam2
);
604 case ACMDM_FORMAT_DETAILS
:
605 return GSM_FormatDetails((PACMFORMATDETAILSW
)dwParam1
, dwParam2
);
607 case ACMDM_FORMAT_SUGGEST
:
608 return GSM_FormatSuggest((PACMDRVFORMATSUGGEST
)dwParam1
);
611 case ACMDM_STREAM_OPEN
:
612 return GSM_StreamOpen((PACMDRVSTREAMINSTANCE
)dwParam1
);
614 case ACMDM_STREAM_CLOSE
:
615 return GSM_StreamClose((PACMDRVSTREAMINSTANCE
)dwParam1
);
617 case ACMDM_STREAM_SIZE
:
618 return GSM_StreamSize((PACMDRVSTREAMINSTANCE
)dwParam1
, (PACMDRVSTREAMSIZE
)dwParam2
);
620 case ACMDM_STREAM_CONVERT
:
621 return GSM_StreamConvert((PACMDRVSTREAMINSTANCE
)dwParam1
, (PACMDRVSTREAMHEADER
)dwParam2
);
623 case ACMDM_STREAM_OPEN
: WARN("libgsm support not compiled in!\n");
624 case ACMDM_STREAM_CLOSE
:
625 case ACMDM_STREAM_SIZE
:
626 case ACMDM_STREAM_CONVERT
:
627 return MMSYSERR_NOTSUPPORTED
;
630 case ACMDM_HARDWARE_WAVE_CAPS_INPUT
:
631 case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT
:
632 /* this converter is not a hardware driver */
633 case ACMDM_FILTERTAG_DETAILS
:
634 case ACMDM_FILTER_DETAILS
:
635 /* this converter is not a filter */
636 case ACMDM_STREAM_RESET
:
637 /* only needed for asynchronous driver... we aren't, so just say it */
638 return MMSYSERR_NOTSUPPORTED
;
639 case ACMDM_STREAM_PREPARE
:
640 case ACMDM_STREAM_UNPREPARE
:
641 /* nothing special to do here... so don't do anything */
642 return MMSYSERR_NOERROR
;
645 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);