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 LRESULT
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
;
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, "Wine 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 DWORD
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 (((GSM610WAVEFORMAT
*)wfx
)->wSamplesPerBlock
!= 320)
184 WARN("GSM wSamplesPerBlock %u\n",
185 ((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 };
202 #define NUM_RATES (sizeof(gsm_rates)/sizeof(*gsm_rates))
204 /***********************************************************************
205 * GSM_FormatTagDetails
208 static LRESULT
GSM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd
, DWORD dwQuery
)
210 static const WCHAR szPcm
[]={'P','C','M',0};
211 static const WCHAR szGsm
[]={'G','S','M',' ','6','.','1','0',0};
215 case ACM_FORMATTAGDETAILSF_INDEX
:
216 if (aftd
->dwFormatTagIndex
> 1) return ACMERR_NOTPOSSIBLE
;
218 case ACM_FORMATTAGDETAILSF_LARGESTSIZE
:
219 if (aftd
->dwFormatTag
== WAVE_FORMAT_UNKNOWN
)
221 aftd
->dwFormatTagIndex
= 1;
225 case ACM_FORMATTAGDETAILSF_FORMATTAG
:
226 switch (aftd
->dwFormatTag
)
228 case WAVE_FORMAT_PCM
: aftd
->dwFormatTagIndex
= 0; break;
229 case WAVE_FORMAT_GSM610
: aftd
->dwFormatTagIndex
= 1; break;
230 default: return ACMERR_NOTPOSSIBLE
;
234 WARN("Unsupported query %08x\n", dwQuery
);
235 return MMSYSERR_NOTSUPPORTED
;
238 aftd
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
239 switch (aftd
->dwFormatTagIndex
)
242 aftd
->dwFormatTag
= WAVE_FORMAT_PCM
;
243 aftd
->cbFormatSize
= sizeof(PCMWAVEFORMAT
);
244 aftd
->cStandardFormats
= NUM_RATES
;
245 lstrcpyW(aftd
->szFormatTag
, szPcm
);
248 aftd
->dwFormatTag
= WAVE_FORMAT_GSM610
;
249 aftd
->cbFormatSize
= sizeof(GSM610WAVEFORMAT
);
250 aftd
->cStandardFormats
= NUM_RATES
;
251 lstrcpyW(aftd
->szFormatTag
, szGsm
);
254 return MMSYSERR_NOERROR
;
257 /***********************************************************************
261 static LRESULT
GSM_FormatDetails(PACMFORMATDETAILSW afd
, DWORD dwQuery
)
265 case ACM_FORMATDETAILSF_FORMAT
:
266 if (!GSM_FormatValidate(afd
->pwfx
)) return ACMERR_NOTPOSSIBLE
;
268 case ACM_FORMATDETAILSF_INDEX
:
269 afd
->pwfx
->wFormatTag
= afd
->dwFormatTag
;
270 switch (afd
->dwFormatTag
)
272 case WAVE_FORMAT_PCM
:
273 if (afd
->dwFormatIndex
>= NUM_RATES
) return ACMERR_NOTPOSSIBLE
;
274 afd
->pwfx
->nChannels
= 1;
275 afd
->pwfx
->nSamplesPerSec
= gsm_rates
[afd
->dwFormatIndex
];
276 afd
->pwfx
->wBitsPerSample
= 16;
277 afd
->pwfx
->nBlockAlign
= 2;
278 afd
->pwfx
->nAvgBytesPerSec
= afd
->pwfx
->nSamplesPerSec
* afd
->pwfx
->nBlockAlign
;
280 case WAVE_FORMAT_GSM610
:
281 if (afd
->dwFormatIndex
>= NUM_RATES
) return ACMERR_NOTPOSSIBLE
;
282 afd
->pwfx
->nChannels
= 1;
283 afd
->pwfx
->nSamplesPerSec
= gsm_rates
[afd
->dwFormatIndex
];
284 afd
->pwfx
->wBitsPerSample
= 0;
285 afd
->pwfx
->nBlockAlign
= 65;
286 afd
->pwfx
->nAvgBytesPerSec
= afd
->pwfx
->nSamplesPerSec
* 65 / 320;
287 afd
->pwfx
->cbSize
= sizeof(WORD
);
288 ((GSM610WAVEFORMAT
*)afd
->pwfx
)->wSamplesPerBlock
= 320;
291 WARN("Unsupported tag %08x\n", afd
->dwFormatTag
);
292 return MMSYSERR_INVALPARAM
;
296 WARN("Unsupported query %08x\n", dwQuery
);
297 return MMSYSERR_NOTSUPPORTED
;
299 afd
->fdwSupport
= ACMDRIVERDETAILS_SUPPORTF_CODEC
;
300 afd
->szFormat
[0] = 0; /* let MSACM format this for us... */
302 return MMSYSERR_NOERROR
;
305 /***********************************************************************
309 static LRESULT
GSM_FormatSuggest(PACMDRVFORMATSUGGEST adfs
)
312 if (adfs
->cbwfxSrc
< sizeof(PCMWAVEFORMAT
) ||
313 adfs
->cbwfxDst
< sizeof(PCMWAVEFORMAT
) ||
314 !GSM_FormatValidate(adfs
->pwfxSrc
)) return ACMERR_NOTPOSSIBLE
;
315 /* FIXME: should do those tests against the real size (according to format tag */
317 /* If no suggestion for destination, then copy source value */
318 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_NCHANNELS
))
319 adfs
->pwfxDst
->nChannels
= adfs
->pwfxSrc
->nChannels
;
320 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_NSAMPLESPERSEC
))
321 adfs
->pwfxDst
->nSamplesPerSec
= adfs
->pwfxSrc
->nSamplesPerSec
;
323 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_WBITSPERSAMPLE
))
325 if (adfs
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
)
326 adfs
->pwfxDst
->wBitsPerSample
= 0;
328 adfs
->pwfxDst
->wBitsPerSample
= 16;
330 if (!(adfs
->fdwSuggest
& ACM_FORMATSUGGESTF_WFORMATTAG
))
332 switch (adfs
->pwfxSrc
->wFormatTag
)
334 case WAVE_FORMAT_PCM
: adfs
->pwfxDst
->wFormatTag
= WAVE_FORMAT_GSM610
; break;
335 case WAVE_FORMAT_GSM610
: adfs
->pwfxDst
->wFormatTag
= WAVE_FORMAT_PCM
; break;
339 /* recompute other values */
340 switch (adfs
->pwfxDst
->wFormatTag
)
342 case WAVE_FORMAT_PCM
:
343 adfs
->pwfxDst
->nBlockAlign
= 2;
344 adfs
->pwfxDst
->nAvgBytesPerSec
= adfs
->pwfxDst
->nSamplesPerSec
* 2;
346 case WAVE_FORMAT_GSM610
:
347 if (adfs
->pwfxDst
->cbSize
< sizeof(WORD
))
348 return ACMERR_NOTPOSSIBLE
;
349 adfs
->pwfxDst
->nBlockAlign
= 65;
350 adfs
->pwfxDst
->nAvgBytesPerSec
= adfs
->pwfxDst
->nSamplesPerSec
* 65 / 320;
351 ((GSM610WAVEFORMAT
*)adfs
->pwfxDst
)->wSamplesPerBlock
= 320;
354 return ACMERR_NOTPOSSIBLE
;
357 /* check if result is ok */
358 if (!GSM_FormatValidate(adfs
->pwfxDst
)) return ACMERR_NOTPOSSIBLE
;
359 return MMSYSERR_NOERROR
;
363 /***********************************************************************
367 static LRESULT
GSM_StreamOpen(PACMDRVSTREAMINSTANCE adsi
)
371 if (!GSM_FormatValidate(adsi
->pwfxSrc
) || !GSM_FormatValidate(adsi
->pwfxDst
))
372 return MMSYSERR_NOTSUPPORTED
;
374 if (adsi
->pwfxSrc
->nSamplesPerSec
!= adsi
->pwfxDst
->nSamplesPerSec
)
375 return MMSYSERR_NOTSUPPORTED
;
377 if (!GSM_drvLoad()) return MMSYSERR_NOTSUPPORTED
;
381 return MMSYSERR_NOMEM
;
382 if (pgsm_option(r
, GSM_OPT_WAV49
, &used
) < 0)
384 FIXME("Your libgsm library is doesn't support GSM_OPT_WAV49\n");
385 FIXME("Please recompile libgsm with WAV49 support\n");
387 return MMSYSERR_NOTSUPPORTED
;
389 adsi
->dwDriver
= (DWORD_PTR
)r
;
390 return MMSYSERR_NOERROR
;
393 /***********************************************************************
397 static LRESULT
GSM_StreamClose(PACMDRVSTREAMINSTANCE adsi
)
399 pgsm_destroy((gsm
)adsi
->dwDriver
);
400 return MMSYSERR_NOERROR
;
403 /***********************************************************************
407 static LRESULT
GSM_StreamSize(const ACMDRVSTREAMINSTANCE
*adsi
, PACMDRVSTREAMSIZE adss
)
409 switch (adss
->fdwSize
)
411 case ACM_STREAMSIZEF_DESTINATION
:
412 /* cbDstLength => cbSrcLength */
413 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
&&
414 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_GSM610
)
416 adss
->cbSrcLength
= adss
->cbDstLength
/ 65 * 640;
418 else if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
&&
419 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_PCM
)
421 adss
->cbSrcLength
= adss
->cbDstLength
/ 640 * 65;
425 return MMSYSERR_NOTSUPPORTED
;
427 return MMSYSERR_NOERROR
;
428 case ACM_STREAMSIZEF_SOURCE
:
429 /* cbSrcLength => cbDstLength */
430 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_PCM
&&
431 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_GSM610
)
433 adss
->cbDstLength
= (adss
->cbSrcLength
+ 639) / 640 * 65;
435 else if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
&&
436 adsi
->pwfxDst
->wFormatTag
== WAVE_FORMAT_PCM
)
438 adss
->cbDstLength
= adss
->cbSrcLength
/ 65 * 640;
442 return MMSYSERR_NOTSUPPORTED
;
444 return MMSYSERR_NOERROR
;
446 WARN("Unsupported query %08x\n", adss
->fdwSize
);
447 return MMSYSERR_NOTSUPPORTED
;
451 /***********************************************************************
455 static LRESULT
GSM_StreamConvert(PACMDRVSTREAMINSTANCE adsi
, PACMDRVSTREAMHEADER adsh
)
457 gsm r
= (gsm
)adsi
->dwDriver
;
460 BYTE
*src
= adsh
->pbSrc
;
461 BYTE
*dst
= adsh
->pbDst
;
464 if (adsh
->fdwConvert
&
465 ~(ACM_STREAMCONVERTF_BLOCKALIGN
|
466 ACM_STREAMCONVERTF_END
|
467 ACM_STREAMCONVERTF_START
))
469 FIXME("Unsupported fdwConvert (%08x), ignoring it\n", adsh
->fdwConvert
);
472 /* Reset the index to 0, just to be sure */
473 pgsm_option(r
, GSM_OPT_FRAME_INDEX
, &odd
);
475 /* The native ms codec writes 65 bytes, and this requires 2 libgsm calls.
476 * First 32 bytes are written, or 33 bytes read
477 * Second 33 bytes are written, or 32 bytes read
481 if (adsi
->pwfxSrc
->wFormatTag
== WAVE_FORMAT_GSM610
)
483 if (adsh
->cbSrcLength
/ 65 * 640 > adsh
->cbDstLength
)
485 return ACMERR_NOTPOSSIBLE
;
488 while (nsrc
+ 65 <= adsh
->cbSrcLength
)
491 if (pgsm_decode(r
, src
+ nsrc
, (gsm_signal
*)(dst
+ ndst
)) < 0)
492 FIXME("Couldn't decode data\n");
496 if (pgsm_decode(r
, src
+ nsrc
, (gsm_signal
*)(dst
+ ndst
)) < 0)
497 FIXME("Couldn't decode data\n");
504 /* Testing a little seems to reveal that despite being able to fit
505 * inside the buffer if ACM_STREAMCONVERTF_BLOCKALIGN is set
508 if ((adsh
->cbSrcLength
+ 639) / 640 * 65 > adsh
->cbDstLength
)
510 return ACMERR_NOTPOSSIBLE
;
513 /* The packing algorythm writes 32 bytes, then 33 bytes,
514 * and it seems to pad to align to 65 bytes always
515 * adding extra data where necessary
517 while (nsrc
+ 640 <= adsh
->cbSrcLength
)
520 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
523 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
528 /* If ACM_STREAMCONVERTF_BLOCKALIGN isn't set pad with zeros */
529 if (!(adsh
->fdwConvert
& ACM_STREAMCONVERTF_BLOCKALIGN
) &&
530 nsrc
< adsh
->cbSrcLength
)
533 int todo
= adsh
->cbSrcLength
- nsrc
;
537 pgsm_encode(r
, (gsm_signal
*)(src
+nsrc
), dst
+ndst
);
542 memcpy(emptiness
, src
+nsrc
, todo
);
543 memset(emptiness
+ todo
, 0, 320 - todo
);
544 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
549 memcpy(emptiness
, src
+nsrc
, todo
);
550 memset(emptiness
+ todo
, 0, 320 - todo
);
551 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
554 memset(emptiness
, 0, todo
);
555 pgsm_encode(r
, (gsm_signal
*)emptiness
, dst
+ndst
);
558 nsrc
= adsh
->cbSrcLength
;
562 adsh
->cbSrcLengthUsed
= nsrc
;
563 adsh
->cbDstLengthUsed
= ndst
;
564 TRACE("%d(%d) -> %d(%d)\n", nsrc
, adsh
->cbSrcLength
, ndst
, adsh
->cbDstLength
);
565 return MMSYSERR_NOERROR
;
570 /**************************************************************************
571 * GSM_DriverProc [exported]
573 LRESULT CALLBACK
GSM_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
574 LPARAM dwParam1
, LPARAM dwParam2
)
576 TRACE("(%08lx %p %04x %08lx %08lx);\n",
577 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
581 case DRV_LOAD
: return 1;
582 case DRV_FREE
: return GSM_drvFree();
583 case DRV_OPEN
: return 1;
584 case DRV_CLOSE
: return 1;
585 case DRV_ENABLE
: return 1;
586 case DRV_DISABLE
: return 1;
587 case DRV_QUERYCONFIGURE
: return 1;
588 case DRV_CONFIGURE
: MessageBoxA(0, "GSM 06.10 codec", "Wine Driver", MB_OK
); return 1;
589 case DRV_INSTALL
: return DRVCNF_RESTART
;
590 case DRV_REMOVE
: return DRVCNF_RESTART
;
592 case ACMDM_DRIVER_NOTIFY
:
593 /* no caching from other ACM drivers is done so far */
594 return MMSYSERR_NOERROR
;
596 case ACMDM_DRIVER_DETAILS
:
597 return GSM_DriverDetails((PACMDRIVERDETAILSW
)dwParam1
);
599 case ACMDM_FORMATTAG_DETAILS
:
600 return GSM_FormatTagDetails((PACMFORMATTAGDETAILSW
)dwParam1
, dwParam2
);
602 case ACMDM_FORMAT_DETAILS
:
603 return GSM_FormatDetails((PACMFORMATDETAILSW
)dwParam1
, dwParam2
);
605 case ACMDM_FORMAT_SUGGEST
:
606 return GSM_FormatSuggest((PACMDRVFORMATSUGGEST
)dwParam1
);
609 case ACMDM_STREAM_OPEN
:
610 return GSM_StreamOpen((PACMDRVSTREAMINSTANCE
)dwParam1
);
612 case ACMDM_STREAM_CLOSE
:
613 return GSM_StreamClose((PACMDRVSTREAMINSTANCE
)dwParam1
);
615 case ACMDM_STREAM_SIZE
:
616 return GSM_StreamSize((PACMDRVSTREAMINSTANCE
)dwParam1
, (PACMDRVSTREAMSIZE
)dwParam2
);
618 case ACMDM_STREAM_CONVERT
:
619 return GSM_StreamConvert((PACMDRVSTREAMINSTANCE
)dwParam1
, (PACMDRVSTREAMHEADER
)dwParam2
);
621 case ACMDM_STREAM_OPEN
: ERR("libgsm support not compiled in!\n");
622 case ACMDM_STREAM_CLOSE
:
623 case ACMDM_STREAM_SIZE
:
624 case ACMDM_STREAM_CONVERT
:
625 return MMSYSERR_NOTSUPPORTED
;
628 case ACMDM_HARDWARE_WAVE_CAPS_INPUT
:
629 case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT
:
630 /* this converter is not a hardware driver */
631 case ACMDM_FILTERTAG_DETAILS
:
632 case ACMDM_FILTER_DETAILS
:
633 /* this converter is not a filter */
634 case ACMDM_STREAM_RESET
:
635 /* only needed for asynchronous driver... we aren't, so just say it */
636 return MMSYSERR_NOTSUPPORTED
;
637 case ACMDM_STREAM_PREPARE
:
638 case ACMDM_STREAM_UNPREPARE
:
639 /* nothing special to do here... so don't do anything */
640 return MMSYSERR_NOERROR
;
643 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);