stream.c 15.5 KB
Newer Older
1 2
/* -*- tab-width: 8; c-basic-offset: 4 -*- */

3 4 5 6
/*
 *      MSACM32 library
 *
 *      Copyright 1998  Patrik Stridvall
7
 *		  1999	Eric Pouech
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 23 24 25 26 27 28
 */

/* TODO
 * 	+ asynchronous conversion is not implemented
 *	+ callback/notification
 *	* acmStreamMessage
 *	+ properly close ACM streams
29 30
 */

31
#include <stdarg.h>
32
#include <string.h>
33
#include "windef.h"
34
#include "winbase.h"
35
#include "winerror.h"
36
#include "wine/debug.h"
37
#include "mmsystem.h"
38
#define NOBITMAP
39
#include "mmreg.h"
40 41
#include "msacm.h"
#include "msacmdrv.h"
42
#include "wineacm.h"
43

44
WINE_DEFAULT_DEBUG_CHANNEL(msacm);
45

46 47
static PWINE_ACMSTREAM	ACM_GetStream(HACMSTREAM has)
{
48 49
    TRACE("(%p)\n", has);

50 51
    return (PWINE_ACMSTREAM)has;
}
52

53 54 55 56 57 58 59 60 61
static BOOL ACM_ValidatePointers(PACMDRVSTREAMHEADER padsh)
{
    /* check that pointers have not been modified */
    return !(padsh->pbPreparedSrc != padsh->pbSrc ||
             padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
             padsh->pbPreparedDst != padsh->pbDst ||
             padsh->cbPreparedDstLength < padsh->cbDstLength);
}

62
/***********************************************************************
63
 *           acmStreamClose (MSACM32.@)
64
 */
65
MMRESULT WINAPI acmStreamClose(HACMSTREAM has, DWORD fdwClose)
66
{
67 68
    PWINE_ACMSTREAM	was;
    MMRESULT		ret;
69

70
    TRACE("(%p, %d)\n", has, fdwClose);
71

72
    if ((was = ACM_GetStream(has)) == NULL) {
73
        WARN("invalid handle\n");
74 75
	return MMSYSERR_INVALHANDLE;
    }
76
    ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_CLOSE, (LPARAM)&was->drvInst, 0);
77
    if (ret == MMSYSERR_NOERROR) {
78
	if (was->hAcmDriver)
79
	    acmDriverClose(was->hAcmDriver, 0L);
80 81 82 83
	HeapFree(MSACM_hHeap, 0, was);
    }
    TRACE("=> (%d)\n", ret);
    return ret;
84 85 86
}

/***********************************************************************
87
 *           acmStreamConvert (MSACM32.@)
88
 */
89
MMRESULT WINAPI acmStreamConvert(HACMSTREAM has, PACMSTREAMHEADER pash,
90
				 DWORD fdwConvert)
91
{
92 93 94 95
    PWINE_ACMSTREAM	was;
    MMRESULT		ret = MMSYSERR_NOERROR;
    PACMDRVSTREAMHEADER	padsh;

96
    TRACE("(%p, %p, %d)\n", has, pash, fdwConvert);
97

98 99
    if ((was = ACM_GetStream(has)) == NULL) {
        WARN("invalid handle\n");
100
	return MMSYSERR_INVALHANDLE;
101 102 103
    }
    if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
        WARN("invalid parameter\n");
104
	return MMSYSERR_INVALPARAM;
105 106 107
    }
    if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) {
        WARN("unprepared header\n");
108
	return ACMERR_UNPREPARED;
109
    }
110

111 112 113
    pash->cbSrcLengthUsed = 0;
    pash->cbDstLengthUsed = 0;

114 115 116 117 118 119
    /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
     * size. some fields are private to msacm internals, and are exposed
     * in ACMSTREAMHEADER in the dwReservedDriver array
     */
    padsh = (PACMDRVSTREAMHEADER)pash;

120
    if (!ACM_ValidatePointers(padsh)) {
121
        WARN("invalid parameter\n");
122
        return MMSYSERR_INVALPARAM;
123
    }
124 125 126

    padsh->fdwConvert = fdwConvert;

127
    ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_CONVERT, (LPARAM)&was->drvInst, (LPARAM)padsh);
128 129 130 131 132
    if (ret == MMSYSERR_NOERROR) {
	padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_DONE;
    }
    TRACE("=> (%d)\n", ret);
    return ret;
133 134 135
}

/***********************************************************************
136
 *           acmStreamMessage (MSACM32.@)
137
 */
138
MMRESULT WINAPI acmStreamMessage(HACMSTREAM has, UINT uMsg, LPARAM lParam1,
139
				 LPARAM lParam2)
140
{
141
    FIXME("(%p, %u, %ld, %ld): stub\n", has, uMsg, lParam1, lParam2);
142 143
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return MMSYSERR_ERROR;
144 145 146
}

/***********************************************************************
147
 *           acmStreamOpen (MSACM32.@)
148
 */
149 150 151 152
MMRESULT WINAPI acmStreamOpen(PHACMSTREAM phas, HACMDRIVER had,
                              PWAVEFORMATEX pwfxSrc, PWAVEFORMATEX pwfxDst,
                              PWAVEFILTER pwfltr, DWORD_PTR dwCallback,
                              DWORD_PTR dwInstance, DWORD fdwOpen)
153
{
154 155 156 157 158
    PWINE_ACMSTREAM	was;
    PWINE_ACMDRIVER	wad;
    MMRESULT		ret;
    int			wfxSrcSize;
    int			wfxDstSize;
159
    WAVEFORMATEX	wfxSrc, wfxDst;
160

161
    TRACE("(%p, %p, %p, %p, %p, %ld, %ld, %d)\n",
162 163
	  phas, had, pwfxSrc, pwfxDst, pwfltr, dwCallback, dwInstance, fdwOpen);

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    /* NOTE: pwfxSrc and/or pwfxDst can point to a structure smaller than
     * WAVEFORMATEX so don't use them directly when not sure */
    if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) {
        memcpy(&wfxSrc, pwfxSrc, sizeof(PCMWAVEFORMAT));
        wfxSrc.wBitsPerSample = pwfxSrc->wBitsPerSample;
        wfxSrc.cbSize = 0;
        pwfxSrc = &wfxSrc;
    }

    if (pwfxDst->wFormatTag == WAVE_FORMAT_PCM) {
        memcpy(&wfxDst, pwfxDst, sizeof(PCMWAVEFORMAT));
        wfxDst.wBitsPerSample = pwfxDst->wBitsPerSample;
        wfxDst.cbSize = 0;
        pwfxDst = &wfxDst;
    }

180
    TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%u, nAvgBytesPerSec=%u, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
181
	  pwfxSrc->wFormatTag, pwfxSrc->nChannels, pwfxSrc->nSamplesPerSec, pwfxSrc->nAvgBytesPerSec,
182 183
	  pwfxSrc->nBlockAlign, pwfxSrc->wBitsPerSample, pwfxSrc->cbSize);

184
    TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%u, nAvgBytesPerSec=%u, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
185
	  pwfxDst->wFormatTag, pwfxDst->nChannels, pwfxDst->nSamplesPerSec, pwfxDst->nAvgBytesPerSec,
186 187
	  pwfxDst->nBlockAlign, pwfxDst->wBitsPerSample, pwfxDst->cbSize);

188 189 190 191 192 193
    /* (WS) In query mode, phas should be NULL. If it is not, then instead
     * of returning an error we are making sure it is NULL, preventing some
     * applications that pass garbage for phas from crashing.
     */
    if (fdwOpen & ACM_STREAMOPENF_QUERY) phas = NULL;

194 195 196 197
    if (pwfltr && (pwfxSrc->wFormatTag != pwfxDst->wFormatTag)) {
        WARN("invalid parameter\n");
        return MMSYSERR_INVALPARAM;
    }
Eric Pouech's avatar
Eric Pouech committed
198 199 200 201

    wfxSrcSize = wfxDstSize = sizeof(WAVEFORMATEX);
    if (pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) wfxSrcSize += pwfxSrc->cbSize;
    if (pwfxDst->wFormatTag != WAVE_FORMAT_PCM) wfxDstSize += pwfxDst->cbSize;
202

203
    was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize +
204
		    ((pwfltr) ? sizeof(WAVEFILTER) : 0));
205 206
    if (was == NULL) {
        WARN("no memory\n");
207
	return MMSYSERR_NOMEM;
208
    }
209

210 211 212 213 214 215 216 217 218 219 220
    was->drvInst.cbStruct = sizeof(was->drvInst);
    was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was));
    memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize);
    was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize);
    memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize);
    if (pwfltr) {
	was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize);
	memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER));
    } else {
	was->drvInst.pwfltr = NULL;
    }
221
    was->drvInst.dwCallback = dwCallback;
222 223
    was->drvInst.dwInstance = dwInstance;
    was->drvInst.fdwOpen = fdwOpen;
224 225
    was->drvInst.fdwDriver = 0L;
    was->drvInst.dwDriver = 0L;
226 227
    /* real value will be stored once ACMDM_STREAM_OPEN succeeds */
    was->drvInst.has = 0L;
228

229 230 231 232 233
    if (had) {
	if (!(wad = MSACM_GetDriver(had))) {
	    ret = MMSYSERR_INVALPARAM;
	    goto errCleanUp;
	}
234

235
	was->obj.dwType = WINE_ACMOBJ_STREAM;
236 237
	was->obj.pACMDriverID = wad->obj.pACMDriverID;
	was->pDrv = wad;
238 239
	was->hAcmDriver = 0; /* not to close it in acmStreamClose */

240
	ret = MSACM_Message((HACMDRIVER)wad, ACMDM_STREAM_OPEN, (LPARAM)&was->drvInst, 0L);
241 242 243 244
	if (ret != MMSYSERR_NOERROR)
	    goto errCleanUp;
    } else {
	PWINE_ACMDRIVERID wadi;
245

246 247
	ret = ACMERR_NOTPOSSIBLE;
	for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) {
248
	    if ((wadi->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
249 250 251
		!MSACM_FindFormatTagInCache(wadi, pwfxSrc->wFormatTag, NULL) ||
		!MSACM_FindFormatTagInCache(wadi, pwfxDst->wFormatTag, NULL))
		continue;
252
	    ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L);
253 254 255 256 257 258 259
	    if (ret != MMSYSERR_NOERROR)
		continue;
	    if ((wad = MSACM_GetDriver(had)) != 0) {
		was->obj.dwType = WINE_ACMOBJ_STREAM;
		was->obj.pACMDriverID = wad->obj.pACMDriverID;
		was->pDrv = wad;
		was->hAcmDriver = had;
260

261
		ret = MSACM_Message((HACMDRIVER)wad, ACMDM_STREAM_OPEN, (LPARAM)&was->drvInst, 0L);
262
		TRACE("%s => %08x\n", debugstr_w(wadi->pszDriverAlias), ret);
263 264
		if (ret == MMSYSERR_NOERROR) {
		    if (fdwOpen & ACM_STREAMOPENF_QUERY) {
265
			MSACM_Message((HACMDRIVER)wad, ACMDM_STREAM_CLOSE, (LPARAM)&was->drvInst, 0);
266
			acmDriverClose(had, 0L);
267
		    }
268
		    break;
269 270
		}
	    }
271 272
	    /* no match, close this acm driver and try next one */
	    acmDriverClose(had, 0L);
273 274 275 276 277 278 279
	}
	if (ret != MMSYSERR_NOERROR) {
	    ret = ACMERR_NOTPOSSIBLE;
	    goto errCleanUp;
	}
    }
    ret = MMSYSERR_NOERROR;
280
    was->drvInst.has = (HACMSTREAM)was;
281 282 283 284
    if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) {
	if (phas)
	    *phas = (HACMSTREAM)was;
	TRACE("=> (%d)\n", ret);
285
	return ret;
286
    }
287
errCleanUp:
288
    if (phas)
289
	*phas = NULL;
290 291 292
    HeapFree(MSACM_hHeap, 0, was);
    TRACE("=> (%d)\n", ret);
    return ret;
293 294 295 296
}


/***********************************************************************
297
 *           acmStreamPrepareHeader (MSACM32.@)
298
 */
299
MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
300
				       DWORD fdwPrepare)
301
{
302 303 304 305
    PWINE_ACMSTREAM	was;
    MMRESULT		ret = MMSYSERR_NOERROR;
    PACMDRVSTREAMHEADER	padsh;

306
    TRACE("(%p, %p, %d)\n", has, pash, fdwPrepare);
307

308 309
    if ((was = ACM_GetStream(has)) == NULL) {
        WARN("invalid handle\n");
310
        return MMSYSERR_INVALHANDLE;
311 312 313
    }
    if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
        WARN("invalid parameter\n");
314 315 316 317 318
        return MMSYSERR_INVALPARAM;
    }
    if (fdwPrepare) {
        WARN("invalid use of reserved parameter\n");
        return MMSYSERR_INVALFLAG;
319
    }
320 321 322
    if ((was->drvInst.pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM ||
         was->drvInst.pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) &&
        pash->cbSrcLength < was->drvInst.pwfxSrc->nBlockAlign) {
323 324 325 326
        WARN("source smaller than block align (%d < %d)\n",
             pash->cbSrcLength, was->drvInst.pwfxSrc->nBlockAlign);
        return pash->cbSrcLength ? ACMERR_NOTPOSSIBLE : MMSYSERR_INVALPARAM;
    }
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

    /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
     * size. some fields are private to msacm internals, and are exposed
     * in ACMSTREAMHEADER in the dwReservedDriver array
     */
    padsh = (PACMDRVSTREAMHEADER)pash;

    padsh->fdwConvert = fdwPrepare;
    padsh->padshNext = NULL;
    padsh->fdwDriver = padsh->dwDriver = 0L;

    padsh->fdwPrepared = 0;
    padsh->dwPrepared = 0;
    padsh->pbPreparedSrc = 0;
    padsh->cbPreparedSrcLength = 0;
    padsh->pbPreparedDst = 0;
    padsh->cbPreparedDstLength = 0;

345
    ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_PREPARE, (LPARAM)&was->drvInst, (LPARAM)padsh);
346 347
    if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
	ret = MMSYSERR_NOERROR;
348
	padsh->fdwStatus &= ~ACMSTREAMHEADER_STATUSF_INQUEUE;
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED;
	padsh->fdwPrepared = padsh->fdwStatus;
	padsh->dwPrepared = 0;
	padsh->pbPreparedSrc = padsh->pbSrc;
	padsh->cbPreparedSrcLength = padsh->cbSrcLength;
	padsh->pbPreparedDst = padsh->pbDst;
	padsh->cbPreparedDstLength = padsh->cbDstLength;
    } else {
	padsh->fdwPrepared = 0;
	padsh->dwPrepared = 0;
	padsh->pbPreparedSrc = 0;
	padsh->cbPreparedSrcLength = 0;
	padsh->pbPreparedDst = 0;
	padsh->cbPreparedDstLength = 0;
    }
    TRACE("=> (%d)\n", ret);
    return ret;
366 367 368
}

/***********************************************************************
369
 *           acmStreamReset (MSACM32.@)
370
 */
371
MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset)
372
{
373 374 375
    PWINE_ACMSTREAM	was;
    MMRESULT		ret = MMSYSERR_NOERROR;

376
    TRACE("(%p, %d)\n", has, fdwReset);
377 378

    if (fdwReset) {
379
        WARN("invalid flag\n");
380 381
	ret = MMSYSERR_INVALFLAG;
    } else if ((was = ACM_GetStream(has)) == NULL) {
382
        WARN("invalid handle\n");
383 384
	return MMSYSERR_INVALHANDLE;
    } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) {
385
	ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_RESET, (LPARAM)&was->drvInst, 0);
386 387 388
    }
    TRACE("=> (%d)\n", ret);
    return ret;
389 390 391
}

/***********************************************************************
392
 *           acmStreamSize (MSACM32.@)
393
 */
394
MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput,
395
			      LPDWORD pdwOutputBytes, DWORD fdwSize)
396
{
397 398 399
    PWINE_ACMSTREAM	was;
    ACMDRVSTREAMSIZE	adss;
    MMRESULT		ret;
400

401
    TRACE("(%p, %d, %p, %d)\n", has, cbInput, pdwOutputBytes, fdwSize);
402

403
    if ((was = ACM_GetStream(has)) == NULL) {
404
        WARN("invalid handle\n");
405 406 407
	return MMSYSERR_INVALHANDLE;
    }
    if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) {
408
        WARN("invalid flag\n");
409 410 411 412
	return MMSYSERR_INVALFLAG;
    }

    *pdwOutputBytes = 0L;
413

414 415 416 417 418 419 420 421 422
    switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
    case ACM_STREAMSIZEF_DESTINATION:
	adss.cbDstLength = cbInput;
	adss.cbSrcLength = 0;
	break;
    case ACM_STREAMSIZEF_SOURCE:
	adss.cbSrcLength = cbInput;
	adss.cbDstLength = 0;
	break;
423
    default:
424
        WARN("invalid flag\n");
425 426
	return MMSYSERR_INVALFLAG;
    }
427

428 429
    adss.cbStruct = sizeof(adss);
    adss.fdwSize = fdwSize;
430
    ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_SIZE,
431
                            (LPARAM)&was->drvInst, (LPARAM)&adss);
432 433 434 435 436 437 438 439 440 441
    if (ret == MMSYSERR_NOERROR) {
	switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
	case ACM_STREAMSIZEF_DESTINATION:
	    *pdwOutputBytes = adss.cbSrcLength;
	    break;
	case ACM_STREAMSIZEF_SOURCE:
	    *pdwOutputBytes = adss.cbDstLength;
	    break;
	}
    }
442
    TRACE("=> (%d) [%u]\n", ret, *pdwOutputBytes);
443
    return ret;
444 445 446
}

/***********************************************************************
447
 *           acmStreamUnprepareHeader (MSACM32.@)
448
 */
449
MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
450
					 DWORD fdwUnprepare)
451
{
452 453 454 455
    PWINE_ACMSTREAM	was;
    MMRESULT		ret = MMSYSERR_NOERROR;
    PACMDRVSTREAMHEADER	padsh;

456
    TRACE("(%p, %p, %d)\n", has, pash, fdwUnprepare);
457

458 459
    if ((was = ACM_GetStream(has)) == NULL) {
        WARN("invalid handle\n");
460
	return MMSYSERR_INVALHANDLE;
461 462 463
    }
    if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
        WARN("invalid parameter\n");
464
	return MMSYSERR_INVALPARAM;
465 466 467
    }
    if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) {
        WARN("unprepared header\n");
468
	return ACMERR_UNPREPARED;
469
    }
470 471 472 473 474 475 476

    /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
     * size. some fields are private to msacm internals, and are exposed
     * in ACMSTREAMHEADER in the dwReservedDriver array
     */
    padsh = (PACMDRVSTREAMHEADER)pash;

477
    if (!ACM_ValidatePointers(padsh)) {
478
        WARN("invalid parameter\n");
479
        return MMSYSERR_INVALPARAM;
480
    }
481 482 483

    padsh->fdwConvert = fdwUnprepare;

484
    ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_UNPREPARE, (LPARAM)&was->drvInst, (LPARAM)padsh);
485 486
    if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
	ret = MMSYSERR_NOERROR;
487
	padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED);
488 489 490
    }
    TRACE("=> (%d)\n", ret);
    return ret;
491
}