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
24 #include <wine/port.h>
32 #elif defined(HAVE_GSM_H)
45 #include "wine/library.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(gsm
);
52 static void *libgsm_handle
;
53 #define FUNCPTR(f) static typeof(f) * p##f
60 #define LOAD_FUNCPTR(f) \
61 if((p##f = wine_dlsym(libgsm_handle, #f, NULL, 0)) == NULL) { \
62 wine_dlclose(libgsm_handle, NULL, 0); \
63 libgsm_handle = NULL; \
67 /***********************************************************************
70 static BOOL
GSM_drvLoad(void)
74 libgsm_handle
= wine_dlopen(SONAME_LIBGSM
, RTLD_NOW
, error
, sizeof(error
));
77 LOAD_FUNCPTR(gsm_create
);
78 LOAD_FUNCPTR(gsm_destroy
);
79 LOAD_FUNCPTR(gsm_option
);
80 LOAD_FUNCPTR(gsm_encode
);
81 LOAD_FUNCPTR(gsm_decode
);
86 ERR("Couldn't load " SONAME_LIBGSM
": %s\n", error
);
91 /***********************************************************************
94 static LRESULT
GSM_drvFree(void)
97 wine_dlclose(libgsm_handle
, NULL
, 0);
103 static LRESULT
GSM_drvFree(void)
110 /***********************************************************************
114 static LRESULT
GSM_DriverDetails(PACMDRIVERDETAILSW add
)
116 add
->fccType
= ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC
;
117 add
->fccComp
= ACMDRIVERDETAILS_FCCCOMP_UNDEFINED
;
118 /* Details found from probing native msgsm32.acm */
119 add
->wMid
= MM_MICROSOFT
;
120 add
->wPid
= MM_MSFT_ACM_GSM610
;
121 add
->vdwACM
= 0x3320000;
122 add
->vdwDriver
= 0x4000000;
123 add
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
124 add
->cFormatTags
= 2;
125 add
->cFilterTags
= 0;
127 MultiByteToWideChar( CP_ACP
, 0, "Microsoft GSM 6.10", -1,
128 add
->szShortName
, sizeof(add
->szShortName
)/sizeof(WCHAR
) );
129 MultiByteToWideChar( CP_ACP
, 0, "Wine GSM 6.10 libgsm codec", -1,
130 add
->szLongName
, sizeof(add
->szLongName
)/sizeof(WCHAR
) );
131 MultiByteToWideChar( CP_ACP
, 0, "Brought to you by the Wine team...", -1,
132 add
->szCopyright
, sizeof(add
->szCopyright
)/sizeof(WCHAR
) );
133 MultiByteToWideChar( CP_ACP
, 0, "Refer to LICENSE file", -1,
134 add
->szLicensing
, sizeof(add
->szLicensing
)/sizeof(WCHAR
) );
135 add
->szFeatures
[0] = 0;
136 return MMSYSERR_NOERROR
;
139 /* Validate a WAVEFORMATEX structure */
140 static BOOL
GSM_FormatValidate(const WAVEFORMATEX
*wfx
)
142 if (wfx
->nChannels
!= 1)
145 switch (wfx
->wFormatTag
)
147 case WAVE_FORMAT_PCM
:
148 if (wfx
->wBitsPerSample
!= 16)
150 WARN("PCM wBitsPerSample %u\n", wfx
->wBitsPerSample
);
153 if (wfx
->nBlockAlign
!= 2)
155 WARN("PCM nBlockAlign %u\n", wfx
->nBlockAlign
);
158 if (wfx
->nAvgBytesPerSec
!= wfx
->nBlockAlign
* wfx
->nSamplesPerSec
)
160 WARN("PCM nAvgBytesPerSec %u/%u\n",
161 wfx
->nAvgBytesPerSec
,
162 wfx
->nBlockAlign
* wfx
->nSamplesPerSec
);
166 case WAVE_FORMAT_GSM610
:
167 if (wfx
->cbSize
< sizeof(WORD
))
169 WARN("GSM cbSize %u\n", wfx
->cbSize
);
172 if (wfx
->wBitsPerSample
!= 0)
174 WARN("GSM wBitsPerSample %u\n", wfx
->wBitsPerSample
);
177 if (wfx
->nBlockAlign
!= 65)
179 WARN("GSM nBlockAlign %u\n", wfx
->nBlockAlign
);
182 if (((const GSM610WAVEFORMAT
*)wfx
)->wSamplesPerBlock
!= 320)
184 WARN("GSM wSamplesPerBlock %u\n",
185 ((const GSM610WAVEFORMAT
*)wfx
)->wSamplesPerBlock
);
188 if (wfx
->nAvgBytesPerSec
!= wfx
->nSamplesPerSec
* 65 / 320)
190 WARN("GSM nAvgBytesPerSec %d / %d\n",
191 wfx
->nAvgBytesPerSec
, wfx
->nSamplesPerSec
* 65 / 320);
201 static const DWORD gsm_rates
[] = { 8000, 11025, 22050, 44100, 48000, 96000 };
203 /***********************************************************************
204 * GSM_FormatTagDetails
207 static LRESULT
GSM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd
, DWORD dwQuery
)
209 static const WCHAR szPcm
[]={'P','C','M',0};
210 static const WCHAR szGsm
[]={'G','S','M',' ','6','.','1','0',0};
214 case ACM_FORMATTAGDETAILSF_INDEX
:
215 if (aftd
->dwFormatTagIndex
> 1) return ACMERR_NOTPOSSIBLE
;
217 case ACM_FORMATTAGDETAILSF_LARGESTSIZE
:
218 if (aftd
->dwFormatTag
== WAVE_FORMAT_UNKNOWN
)
220 aftd
->dwFormatTagIndex
= 1;
224 case ACM_FORMATTAGDETAILSF_FORMATTAG
:
225 switch (aftd
->dwFormatTag
)
227 case WAVE_FORMAT_PCM
: aftd
->dwFormatTagIndex
= 0; break;
228 case WAVE_FORMAT_GSM610
: aftd
->dwFormatTagIndex
= 1; break;
229 default: return ACMERR_NOTPOSSIBLE
;
233 WARN("Unsupported query %08x\n", dwQuery
);
234 return MMSYSERR_NOTSUPPORTED
;
237 aftd
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
238 switch (aftd
->dwFormatTagIndex
)
241 aftd
->dwFormatTag
= WAVE_FORMAT_PCM
;
242 aftd
->cbFormatSize
= sizeof(PCMWAVEFORMAT
);
243 aftd
->cStandardFormats
= ARRAY_SIZE(gsm_rates
);
244 lstrcpyW(aftd
->szFormatTag
, szPcm
);
247 aftd
->dwFormatTag
= WAVE_FORMAT_GSM610
;
248 aftd
->cbFormatSize
= sizeof(GSM610WAVEFORMAT
);
249 aftd
->cStandardFormats
= ARRAY_SIZE(gsm_rates
);
250 lstrcpyW(aftd
->szFormatTag
, szGsm
);
253 return MMSYSERR_NOERROR
;
256 /***********************************************************************
260 static LRESULT
GSM_FormatDetails(PACMFORMATDETAILSW afd
, DWORD dwQuery
)
264 case ACM_FORMATDETAILSF_FORMAT
:
265 if (!GSM_FormatValidate(afd
->pwfx
)) return ACMERR_NOTPOSSIBLE
;
267 case ACM_FORMATDETAILSF_INDEX
:
268 afd
->pwfx
->wFormatTag
= afd
->dwFormatTag
;
269 switch (afd
->dwFormatTag
)
271 case WAVE_FORMAT_PCM
:
272 if (afd
->dwFormatIndex
>= ARRAY_SIZE(gsm_rates
)) return ACMERR_NOTPOSSIBLE
;
273 afd
->pwfx
->nChannels
= 1;
274 afd
->pwfx
->nSamplesPerSec
= gsm_rates
[afd
->dwFormatIndex
];
275 afd
->pwfx
->wBitsPerSample
= 16;
276 afd
->pwfx
->nBlockAlign
= 2;
277 afd
->pwfx
->nAvgBytesPerSec
= afd
->pwfx
->nSamplesPerSec
* afd
->pwfx
->nBlockAlign
;
279 case WAVE_FORMAT_GSM610
:
280 if (afd
->dwFormatIndex
>= ARRAY_SIZE(gsm_rates
)) return ACMERR_NOTPOSSIBLE
;
281 afd
->pwfx
->nChannels
= 1;
282 afd
->pwfx
->nSamplesPerSec
= gsm_rates
[afd
->dwFormatIndex
];
283 afd
->pwfx
->wBitsPerSample
= 0;
284 afd
->pwfx
->nBlockAlign
= 65;
285 afd
->pwfx
->nAvgBytesPerSec
= afd
->pwfx
->nSamplesPerSec
* 65 / 320;
286 afd
->pwfx
->cbSize
= sizeof(WORD
);
287 ((GSM610WAVEFORMAT
*)afd
->pwfx
)->wSamplesPerBlock
= 320;
290 WARN("Unsupported tag %08x\n", afd
->dwFormatTag
);
291 return MMSYSERR_INVALPARAM
;
295 WARN("Unsupported query %08x\n", dwQuery
);
296 return MMSYSERR_NOTSUPPORTED
;
298 afd
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
299 afd
->szFormat
[0] = 0; /* let MSACM format this for us... */
301 return MMSYSERR_NOERROR
;
304 /***********************************************************************
308 static LRESULT
GSM_FormatSuggest(PACMDRVFORMATSUGGEST adfs
)
311 if (adfs
->cbwfxSrc
< sizeof(PCMWAVEFORMAT
) ||
312 adfs
->cbwfxDst
< sizeof(PCMWAVEFORMAT
) ||
313 !GSM_FormatValidate(adfs
->pwfxSrc
)) return ACMERR_NOTPOSSIBLE
;
314 /* FIXME: should do those tests against the real size (according to format tag */
316 /* If no suggestion for destination, then copy source value */
317 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_NCHANNELS
))
318 adfs
->pwfxDst
->nChannels
= adfs
->pwfxSrc
->nChannels
;
319 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_NSAMPLESPERSEC
))
320 adfs
->pwfxDst
->nSamplesPerSec
= adfs
->pwfxSrc
->nSamplesPerSec
;
322 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_WBITSPERSAMPLE
))
324 if (adfs
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
)
325 adfs
->pwfxDst
->wBitsPerSample
= 0;
327 adfs
->pwfxDst
->wBitsPerSample
= 16;
329 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_WFORMATTAG
))
331 switch (adfs
->pwfxSrc
->wFormatTag
)
333 case WAVE_FORMAT_PCM
: adfs
->pwfxDst
->wFormatTag
= WAVE_FORMAT_GSM610
; break;
334 case WAVE_FORMAT_GSM610
: adfs
->pwfxDst
->wFormatTag
= WAVE_FORMAT_PCM
; break;
338 /* recompute other values */
339 switch (adfs
->pwfxDst
->wFormatTag
)
341 case WAVE_FORMAT_PCM
:
342 adfs
->pwfxDst
->nBlockAlign
= 2;
343 adfs
->pwfxDst
->nAvgBytesPerSec
= adfs
->pwfxDst
->nSamplesPerSec
* 2;
345 case WAVE_FORMAT_GSM610
:
346 if (adfs
->pwfxDst
->cbSize
< sizeof(WORD
))
347 return ACMERR_NOTPOSSIBLE
;
348 adfs
->pwfxDst
->nBlockAlign
= 65;
349 adfs
->pwfxDst
->nAvgBytesPerSec
= adfs
->pwfxDst
->nSamplesPerSec
* 65 / 320;
350 ((GSM610WAVEFORMAT
*)adfs
->pwfxDst
)->wSamplesPerBlock
= 320;
353 return ACMERR_NOTPOSSIBLE
;
356 /* check if result is ok */
357 if (!GSM_FormatValidate(adfs
->pwfxDst
)) return ACMERR_NOTPOSSIBLE
;
358 return MMSYSERR_NOERROR
;
362 /***********************************************************************
366 static LRESULT
GSM_StreamOpen(PACMDRVSTREAMINSTANCE adsi
)
370 if (!GSM_FormatValidate(adsi
->pwfxSrc
) || !GSM_FormatValidate(adsi
->pwfxDst
))
371 return MMSYSERR_NOTSUPPORTED
;
373 if (adsi
->pwfxSrc
->nSamplesPerSec
!= adsi
->pwfxDst
->nSamplesPerSec
)
374 return MMSYSERR_NOTSUPPORTED
;
376 if (!GSM_drvLoad()) return MMSYSERR_NOTSUPPORTED
;
380 return MMSYSERR_NOMEM
;
381 if (pgsm_option(r
, GSM_OPT_WAV49
, &used
) < 0)
383 FIXME("Your libgsm library doesn't support GSM_OPT_WAV49\n");
384 FIXME("Please recompile libgsm with WAV49 support\n");
386 return MMSYSERR_NOTSUPPORTED
;
388 adsi
->dwDriver
= (DWORD_PTR
)r
;
389 return MMSYSERR_NOERROR
;
392 /***********************************************************************
396 static LRESULT
GSM_StreamClose(PACMDRVSTREAMINSTANCE adsi
)
398 pgsm_destroy((gsm
)adsi
->dwDriver
);
399 return MMSYSERR_NOERROR
;
402 /***********************************************************************
406 static LRESULT
GSM_StreamSize(const ACMDRVSTREAMINSTANCE
*adsi
, PACMDRVSTREAMSIZE adss
)
408 switch (adss
->fdwSize
)
410 case ACM_STREAMSIZEF_DESTINATION
:
411 /* cbDstLength => cbSrcLength */
412 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
&&
413 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_GSM610
)
415 adss
->cbSrcLength
= adss
->cbDstLength
/ 65 * 640;
417 else if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
&&
418 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_PCM
)
420 adss
->cbSrcLength
= adss
->cbDstLength
/ 640 * 65;
424 return MMSYSERR_NOTSUPPORTED
;
426 return MMSYSERR_NOERROR
;
427 case ACM_STREAMSIZEF_SOURCE
:
428 /* cbSrcLength => cbDstLength */
429 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
&&
430 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_GSM610
)
432 adss
->cbDstLength
= (adss
->cbSrcLength
+ 639) / 640 * 65;
434 else if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
&&
435 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_PCM
)
437 adss
->cbDstLength
= adss
->cbSrcLength
/ 65 * 640;
441 return MMSYSERR_NOTSUPPORTED
;
443 return MMSYSERR_NOERROR
;
445 WARN("Unsupported query %08x\n", adss
->fdwSize
);
446 return MMSYSERR_NOTSUPPORTED
;
450 /***********************************************************************
454 static LRESULT
GSM_StreamConvert(PACMDRVSTREAMINSTANCE adsi
, PACMDRVSTREAMHEADER adsh
)
456 gsm r
= (gsm
)adsi
->dwDriver
;
459 BYTE
*src
= adsh
->pbSrc
;
460 BYTE
*dst
= adsh
->pbDst
;
463 if (adsh
->fdwConvert
&
464 ~(ACM_STREAMCONVERTF_BLOCKALIGN
|
465 ACM_STREAMCONVERTF_END
|
466 ACM_STREAMCONVERTF_START
))
468 FIXME("Unsupported fdwConvert (%08x), ignoring it\n", adsh
->fdwConvert
);
471 /* Reset the index to 0, just to be sure */
472 pgsm_option(r
, GSM_OPT_FRAME_INDEX
, &odd
);
474 /* The native ms codec writes 65 bytes, and this requires 2 libgsm calls.
475 * First 32 bytes are written, or 33 bytes read
476 * Second 33 bytes are written, or 32 bytes read
480 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
)
482 if (adsh
->cbSrcLength
/ 65 * 640 > adsh
->cbDstLength
)
484 return ACMERR_NOTPOSSIBLE
;
487 while (nsrc
+ 65 <= adsh
->cbSrcLength
)
490 if (pgsm_decode(r
, src
+ nsrc
, (gsm_signal
*)(dst
+ ndst
)) < 0)
491 FIXME("Couldn't decode data\n");
495 if (pgsm_decode(r
, src
+ nsrc
, (gsm_signal
*)(dst
+ ndst
)) < 0)
496 FIXME("Couldn't decode data\n");
503 /* Testing a little seems to reveal that despite being able to fit
504 * inside the buffer if ACM_STREAMCONVERTF_BLOCKALIGN is set
507 if ((adsh
->cbSrcLength
+ 639) / 640 * 65 > adsh
->cbDstLength
)
509 return ACMERR_NOTPOSSIBLE
;
512 /* The packing algorithm writes 32 bytes, then 33 bytes,
513 * and it seems to pad to align to 65 bytes always
514 * adding extra data where necessary
516 while (nsrc
+ 640 <= adsh
->cbSrcLength
)
519 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
522 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
527 /* If ACM_STREAMCONVERTF_BLOCKALIGN isn't set pad with zeros */
528 if (!(adsh
->fdwConvert
& ACM_STREAMCONVERTF_BLOCKALIGN
) &&
529 nsrc
< adsh
->cbSrcLength
)
532 int todo
= adsh
->cbSrcLength
- nsrc
;
536 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
541 memcpy(emptiness
, src
+nsrc
, todo
);
542 memset(emptiness
+ todo
, 0, 320 - todo
);
543 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
548 memcpy(emptiness
, src
+nsrc
, todo
);
549 memset(emptiness
+ todo
, 0, 320 - todo
);
550 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
553 memset(emptiness
, 0, todo
);
554 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
557 nsrc
= adsh
->cbSrcLength
;
561 adsh
->cbSrcLengthUsed
= nsrc
;
562 adsh
->cbDstLengthUsed
= ndst
;
563 TRACE("%d(%d) -> %d(%d)\n", nsrc
, adsh
->cbSrcLength
, ndst
, adsh
->cbDstLength
);
564 return MMSYSERR_NOERROR
;
569 /**************************************************************************
570 * GSM_DriverProc [exported]
572 LRESULT CALLBACK
GSM_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
573 LPARAM dwParam1
, LPARAM dwParam2
)
575 TRACE("(%08lx %p %04x %08lx %08lx);\n",
576 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
580 case DRV_LOAD
: return 1;
581 case DRV_FREE
: return GSM_drvFree();
582 case DRV_OPEN
: return 1;
583 case DRV_CLOSE
: return 1;
584 case DRV_ENABLE
: return 1;
585 case DRV_DISABLE
: return 1;
586 case DRV_QUERYCONFIGURE
: return 1;
587 case DRV_CONFIGURE
: MessageBoxA(0, "GSM 06.10 codec", "Wine Driver", MB_OK
); return 1;
588 case DRV_INSTALL
: return DRVCNF_RESTART
;
589 case DRV_REMOVE
: return DRVCNF_RESTART
;
591 case ACMDM_DRIVER_NOTIFY
:
592 /* no caching from other ACM drivers is done so far */
593 return MMSYSERR_NOERROR
;
595 case ACMDM_DRIVER_DETAILS
:
596 return GSM_DriverDetails((PACMDRIVERDETAILSW
)dwParam1
);
598 case ACMDM_FORMATTAG_DETAILS
:
599 return GSM_FormatTagDetails((PACMFORMATTAGDETAILSW
)dwParam1
, dwParam2
);
601 case ACMDM_FORMAT_DETAILS
:
602 return GSM_FormatDetails((PACMFORMATDETAILSW
)dwParam1
, dwParam2
);
604 case ACMDM_FORMAT_SUGGEST
:
605 return GSM_FormatSuggest((PACMDRVFORMATSUGGEST
)dwParam1
);
608 case ACMDM_STREAM_OPEN
:
609 return GSM_StreamOpen((PACMDRVSTREAMINSTANCE
)dwParam1
);
611 case ACMDM_STREAM_CLOSE
:
612 return GSM_StreamClose((PACMDRVSTREAMINSTANCE
)dwParam1
);
614 case ACMDM_STREAM_SIZE
:
615 return GSM_StreamSize((PACMDRVSTREAMINSTANCE
)dwParam1
, (PACMDRVSTREAMSIZE
)dwParam2
);
617 case ACMDM_STREAM_CONVERT
:
618 return GSM_StreamConvert((PACMDRVSTREAMINSTANCE
)dwParam1
, (PACMDRVSTREAMHEADER
)dwParam2
);
620 case ACMDM_STREAM_OPEN
: WARN("libgsm support not compiled in!\n");
621 case ACMDM_STREAM_CLOSE
:
622 case ACMDM_STREAM_SIZE
:
623 case ACMDM_STREAM_CONVERT
:
624 return MMSYSERR_NOTSUPPORTED
;
627 case ACMDM_HARDWARE_WAVE_CAPS_INPUT
:
628 case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT
:
629 /* this converter is not a hardware driver */
630 case ACMDM_FILTERTAG_DETAILS
:
631 case ACMDM_FILTER_DETAILS
:
632 /* this converter is not a filter */
633 case ACMDM_STREAM_RESET
:
634 /* only needed for asynchronous driver... we aren't, so just say it */
635 return MMSYSERR_NOTSUPPORTED
;
636 case ACMDM_STREAM_PREPARE
:
637 case ACMDM_STREAM_UNPREPARE
:
638 /* nothing special to do here... so don't do anything */
639 return MMSYSERR_NOERROR
;
642 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);