audio.c
来自「Wine-20031016」· C语言 代码 · 共 2,103 行 · 第 1/5 页
C
2,103 行
if (wwo->dwOffCurrHdr == 0) { TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength); if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) { if (wwo->lpLoopPtr) { WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr); } else { TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr); wwo->lpLoopPtr = lpWaveHdr; /* Windows does not touch WAVEHDR.dwLoops, * so we need to make an internal copy */ wwo->dwLoops = lpWaveHdr->dwLoops; } } } lpData = lpWaveHdr->lpData; /* finish current wave hdr ? */ if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) { DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr; /* write end of current wave hdr */ count = AudioIOWrite(lpData + wwo->dwOffCurrHdr, toWrite); TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count); if (count > 0 || toWrite == 0) { DWORD tc = GetTickCount(); if (wwo->dwLastFragDone /* + guard time ?? */ < tc) wwo->dwLastFragDone = tc; wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec; lpWaveHdr->reserved = wwo->dwLastFragDone; TRACE("Tagging hdr %p with %08lx\n", lpWaveHdr, wwo->dwLastFragDone); /* WAVEHDR written, go to next one */ if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) { if (--wwo->dwLoops > 0) { wwo->lpPlayPtr = wwo->lpLoopPtr; } else { /* last one played */ if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) { FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n"); /* shall we consider the END flag for the closing loop or for * the opening one or for both ??? * code assumes for closing loop only */ wwo->lpLoopPtr = lpWaveHdr; } else { wwo->lpLoopPtr = NULL; } wwo->lpPlayPtr = lpWaveHdr->lpNext; } } else { wwo->lpPlayPtr = lpWaveHdr->lpNext; } wwo->dwOffCurrHdr = 0; if ((wwo->dwRemain -= count) == 0) { wwo->dwRemain = wwo->dwFragmentSize; } } continue; /* try to go to use next wavehdr */ } else { count = AudioIOWrite( lpData + wwo->dwOffCurrHdr, wwo->dwRemain); TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count); if (count > 0) { DWORD tc = GetTickCount(); if (wwo->dwLastFragDone /* + guard time ?? */ < tc) wwo->dwLastFragDone = tc; wwo->dwLastFragDone += (wwo->dwRemain * 1000) / wwo->format.wf.nAvgBytesPerSec; TRACE("Tagging frag with %08lx\n", wwo->dwLastFragDone); wwo->dwOffCurrHdr += count; wwo->dwRemain = wwo->dwFragmentSize; } } }}int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param){ TRACE("wodPlayerMessage sammplerate= %d msg=%d\n",spec[PLAYBACK].rate,msg); EnterCriticalSection(&wwo->msg_crst); if ((wwo->msg_tosave == wwo->msg_toget) /* buffer overflow ? */ && (wwo->messages[wwo->msg_toget].msg)) { ERR("buffer overflow !?\n"); LeaveCriticalSection(&wwo->msg_crst); return 0; } wwo->messages[wwo->msg_tosave].msg = msg; wwo->messages[wwo->msg_tosave].param = param; wwo->msg_tosave++; if (wwo->msg_tosave > WWO_RING_BUFFER_SIZE-1) wwo->msg_tosave = 0; LeaveCriticalSection(&wwo->msg_crst); /* signal a new message */ SetEvent(wwo->msg_event); return 1;}int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param){ EnterCriticalSection(&wwo->msg_crst); if (wwo->msg_toget == wwo->msg_tosave) /* buffer empty ? */ { LeaveCriticalSection(&wwo->msg_crst); return 0; } *msg = wwo->messages[wwo->msg_toget].msg; wwo->messages[wwo->msg_toget].msg = 0; *param = wwo->messages[wwo->msg_toget].param; wwo->msg_toget++; if (wwo->msg_toget > WWO_RING_BUFFER_SIZE-1) wwo->msg_toget = 0; LeaveCriticalSection(&wwo->msg_crst); return 1;}/************************************************************************** * wodPlayer_Notify [internal] * * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content * have been played (actually to speaker, not to unixdev fd). */static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force){ LPWAVEHDR lpWaveHdr; DWORD tc = GetTickCount(); while (wwo->lpQueuePtr && (force || (wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) { lpWaveHdr = wwo->lpQueuePtr; if (lpWaveHdr->reserved > tc && !force) break; wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength; wwo->lpQueuePtr = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; TRACE("Notifying client with %p\n", lpWaveHdr); if (LIBAUDIOIO_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) { WARN("can't notify client !\n"); } }}/************************************************************************** * wodPlayer_Reset [internal] * * wodPlayer helper. Resets current output stream. */static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset){ /* updates current notify list */ wodPlayer_Notify(wwo, uDevID, FALSE); /* flush all possible output *//* *FIXME In the original code I think this aborted IO. With Libaudioio you have to wait * The following function just blocks untill I/O is complete * There is possibly a way to abort the blocks buffered in streams * but this approach seems to work OK */ AudioIOFlush(); wwo->dwOffCurrHdr = 0; wwo->dwRemain = wwo->dwFragmentSize; if (reset) { /* empty notify list */ wodPlayer_Notify(wwo, uDevID, TRUE); wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; wwo->state = WINE_WS_STOPPED; wwo->dwPlayedTotal = 0; } else { /* FIXME: this is not accurate when looping, but can be do better ? */ wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr; wwo->state = WINE_WS_PAUSED; }}/************************************************************************** * wodPlayer [internal] */static DWORD CALLBACK wodPlayer(LPVOID pmt){ WORD uDevID = (DWORD)pmt; WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID]; WAVEHDR* lpWaveHdr; DWORD dwSleepTime; int msg; DWORD param; DWORD tc; BOOL had_msg; wwo->state = WINE_WS_STOPPED; wwo->dwLastFragDone = 0; wwo->dwOffCurrHdr = 0; wwo->dwRemain = wwo->dwFragmentSize; wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL; wwo->dwPlayedTotal = 0; TRACE("imhere[0]\n"); SetEvent(wwo->hEvent); for (;;) { /* wait for dwSleepTime or an event in thread's queue * FIXME: * - is wait time calculation optimal ? * - these 100 ms parts should be changed, but Eric reports * that the wodPlayer thread might lock up if we use INFINITE * (strange !), so I better don't change that now... */ if (wwo->state != WINE_WS_PLAYING) dwSleepTime = 100; else { tc = GetTickCount(); if (tc < wwo->dwLastFragDone) { /* calculate sleep time depending on when the last fragment will be played */ dwSleepTime = (wwo->dwLastFragDone - tc)*7/10; if (dwSleepTime > 100) dwSleepTime = 100; } else dwSleepTime = 0; } TRACE("imhere[1] tc = %08lx\n", GetTickCount()); if (dwSleepTime) WaitForSingleObject(wwo->msg_event, dwSleepTime); TRACE("imhere[2] (q=%p p=%p) tc = %08lx\n", wwo->lpQueuePtr, wwo->lpPlayPtr, GetTickCount()); had_msg = FALSE; while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m)) { had_msg = TRUE; switch (msg) { case WINE_WM_PAUSING: wodPlayer_Reset(wwo, uDevID, FALSE); wwo->state = WINE_WS_PAUSED; SetEvent(wwo->hEvent); break; case WINE_WM_RESTARTING: wwo->state = WINE_WS_PLAYING; SetEvent(wwo->hEvent); break; case WINE_WM_HEADER: lpWaveHdr = (LPWAVEHDR)param; /* insert buffer at the end of queue */ { LPWAVEHDR* wh; for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); *wh = lpWaveHdr; } if (!wwo->lpPlayPtr) wwo->lpPlayPtr = lpWaveHdr; if (wwo->state == WINE_WS_STOPPED) wwo->state = WINE_WS_PLAYING; break; case WINE_WM_RESETTING: wodPlayer_Reset(wwo, uDevID, TRUE); SetEvent(wwo->hEvent); break; case WINE_WM_CLOSING: /* sanity check: this should not happen since the device must have been reset before */ if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n"); wwo->hThread = 0; wwo->state = WINE_WS_CLOSED; SetEvent(wwo->hEvent); ExitThread(0); /* shouldn't go here */ default: FIXME("unknown message %d\n", msg); break; } if (wwo->state == WINE_WS_PLAYING) { wodPlayer_WriteFragments(wwo); } wodPlayer_Notify(wwo, uDevID, FALSE); } if (!had_msg) { /* if we've received a msg we've just done this so we won't repeat it */ if (wwo->state == WINE_WS_PLAYING) { wodPlayer_WriteFragments(wwo); } wodPlayer_Notify(wwo, uDevID, FALSE); } } ExitThread(0); /* just for not generating compilation warnings... should never be executed */ return 0;}/************************************************************************** * wodGetDevCaps [internal] */static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize){ TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize); if (lpCaps == NULL) return MMSYSERR_NOTENABLED; if (wDevID >= MAX_WAVEOUTDRV) { TRACE("MAX_WAVOUTDRV reached !\n"); return MMSYSERR_BADDEVICEID; } memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps))); return MMSYSERR_NOERROR;}/************************************************************************** * wodOpen [internal] */static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags){ int audio; int format; int sample_rate; int dsp_stereo; int fragment_size; int audio_fragment; WINE_WAVEOUT* wwo; TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags); if (lpDesc == NULL) { WARN("Invalid Parameter !\n"); return MMSYSERR_INVALPARAM; } if (wDevID >= MAX_WAVEOUTDRV) { TRACE("MAX_WAVOUTDRV reached !\n"); return MMSYSERR_BADDEVICEID; } /* only PCM format is supported so far... */ if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM || lpDesc->lpFormat->nChannels == 0 || lpDesc->lpFormat->nSamplesPerSec == 0) { WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, lpDesc->lpFormat->nSamplesPerSec); return WAVERR_BADFORMAT; } if (dwFlags & WAVE_FORMAT_QUERY) { TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, lpDesc->lpFormat->nSamplesPerSec); return MMSYSERR_NOERROR; } wwo = &WOutDev[wDevID]; if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND)) /* not supported, ignore it */ dwFlags &= ~WAVE_DIRECTSOUND; if (access(SOUND_DEV, 0) != 0) return MMSYSERR_NOTENABLED; audio = AudioIOOpenX( O_WRONLY|O_NDELAY,&spec[PLAYBACK],&spec[PLAYBACK]); if (audio == -1) { WARN("can't open sound device %s (%s)!\n", SOUND_DEV, strerror(errno)); return MMSYSERR_ALLOCATED; } /* fcntl(audio, F_SETFD, 1); *//* set close on exec flag - Dunno about this (RL)*/ wwo->unixdev = audio; wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC)); memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT)); if (wwo->format.wBitsPerSample == 0) { WARN("Resetting zeroed wBitsPerSample\n"); wwo->format.wBitsPerSample = 8 * (wwo->format.wf.nAvgBytesPerSec / wwo->format.wf.nSamplesPerSec) / wwo->format.wf.nChannels; } if (dwFlags & WAVE_DIRECTSOUND) { if (wwo->caps.dwSupport & WAVECAPS_SAMPLEACCURATE) /* we have realtime DirectSound, fragments just waste our time, * but a large buffer is good, so choose 64KB (32 * 2^11) */ audio_fragment = 0x0020000B; else /* to approximate realtime, we must use small fragments, * let's try to fragment the above 64KB (256 * 2^8) */ audio_fragment = 0x01000008; } else { /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec * thus leading to 46ms per fragment, and a turnaround time of 185ms */ /* 16 fragments max, 2^10=1024 bytes per fragment */ audio_fragment = 0x000F000A; } sample_rate = wwo->format.wf.nSamplesPerSec; dsp_stereo = (wwo->format.wf.nChannels > 1) ? 1 : 0; /*Set the sample rate*/ spec[PLAYBACK].rate=sample_rate; /*And the size and signedness*/
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?