Commit ed69f0bf authored by Jeremy Shaw's avatar Jeremy Shaw Committed by Alexandre Julliard

Added wave-in support and fixed a few bugs in the wave-out code.

parent 19dfc3ce
......@@ -32,8 +32,7 @@
* FIXME:
* pause in waveOut does not work correctly in loop mode
*
* TODO:
* implement wave-in support with artsc
* does something need to be done in for WaveIn DirectSound?
*/
/*#define EMULATE_SB16*/
......@@ -65,10 +64,32 @@ WINE_DEFAULT_DEBUG_CHANNEL(wave);
#include <artsc.h>
#define BUFFER_SIZE 16 * 1024
#define SPACE_THRESHOLD 5 * 1024
/* The following four #defines allow you to fine-tune the packet
* settings in arts for better low-latency support. You must also
* adjust the latency in the KDE arts control panel. I recommend 4
* fragments, 1024 bytes.
*
* The following is from the ARTS documentation and explains what CCCC
* and SSSS mean:
*
* @li ARTS_P_PACKET_SETTINGS (rw) This is a way to configure packet
* size & packet count at the same time. The format is 0xCCCCSSSS,
* where 2^SSSS is the packet size, and CCCC is the packet count. Note
* that when writing this, you don't necessarily get the settings you
* requested.
*/
#define WAVEOUT_PACKET_CCCC 0x000C
#define WAVEOUT_PACKET_SSSS 0x0008
#define WAVEIN_PACKET_CCCC 0x000C
#define WAVEIN_PACKET_SSSS 0x0008
#define BUFFER_REFILL_THRESHOLD 4
#define WAVEOUT_PACKET_SETTINGS ((WAVEOUT_PACKET_CCCC << 16) | (WAVEOUT_PACKET_SSSS))
#define WAVEIN_PACKET_SETTINGS ((WAVEIN_PACKET_CCCC << 16) | (WAVEIN_PACKET_SSSS))
#define MAX_WAVEOUTDRV (10)
#define MAX_WAVEINDRV (10)
/* state diagram for waveOut writing:
*
......@@ -96,7 +117,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(wave);
/* events to be send to device */
enum win_wm_message {
WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
};
typedef struct {
......@@ -126,14 +147,16 @@ typedef struct {
PCMWAVEFORMAT format;
WAVEOUTCAPSA caps;
DWORD dwSleepTime; /* Num of milliseconds to sleep between filling the dsp buffers */
/* arts information */
arts_stream_t play_stream; /* the stream structure we get from arts when opening a stream for playing */
DWORD dwBufferSize; /* size of whole buffer in bytes */
int packetSettings;
char* sound_buffer;
long buffer_size;
DWORD volume_left; /* volume control information */
DWORD volume_right;
......@@ -154,7 +177,29 @@ typedef struct {
ARTS_MSG_RING msgRing;
} WINE_WAVEOUT;
typedef struct {
volatile int state; /* one of the WINE_WS_ manifest constants */
WAVEOPENDESC waveDesc;
WORD wFlags;
PCMWAVEFORMAT format;
WAVEINCAPSA caps;
/* arts information */
arts_stream_t record_stream; /* the stream structure we get from arts when opening a stream for recording */
int packetSettings;
LPWAVEHDR lpQueuePtr;
DWORD dwRecordedTotal;
/* synchronization stuff */
HANDLE hStartUpEvent;
HANDLE hThread;
DWORD dwThreadID;
ARTS_MSG_RING msgRing;
} WINE_WAVEIN;
static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
......@@ -169,6 +214,8 @@ static const char *wodPlayerCmdString[] = {
"WINE_WM_UPDATE",
"WINE_WM_BREAKLOOP",
"WINE_WM_CLOSING",
"WINE_WM_STARTING",
"WINE_WM_STOPPING",
};
/*======================================================================*
......@@ -229,22 +276,35 @@ void volume_effect8(void *bufin, void* bufout, int length, int left,
}
/******************************************************************
* ARTS_CloseDevice
* ARTS_CloseWaveOutDevice
*
*/
void ARTS_CloseDevice(WINE_WAVEOUT* wwo)
void ARTS_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
{
arts_close_stream(wwo->play_stream); /* close the arts stream */
wwo->play_stream = (arts_stream_t*)-1;
/* free up the buffer we use for volume and reset the size */
if(wwo->sound_buffer)
{
HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
wwo->sound_buffer = NULL;
}
wwo->buffer_size = 0;
}
/******************************************************************
* ARTS_CloseWaveInDevice
*
*/
void ARTS_CloseWaveInDevice(WINE_WAVEIN* wwi)
{
arts_close_stream(wwi->record_stream); /* close the arts stream */
wwi->record_stream = (arts_stream_t*)-1;
}
/******************************************************************
* ARTS_Init
*/
static int ARTS_Init(void)
......@@ -264,7 +324,15 @@ LONG ARTS_WaveClose(void)
{
if(WOutDev[iDevice].play_stream != (arts_stream_t*)-1)
{
ARTS_CloseDevice(&WOutDev[iDevice]);
ARTS_CloseWaveOutDevice(&WOutDev[iDevice]);
}
}
for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
{
if(WInDev[iDevice].record_stream != (arts_stream_t*)-1)
{
ARTS_CloseWaveInDevice(&WInDev[iDevice]);
}
}
......@@ -331,7 +399,44 @@ LONG ARTS_WaveInit(void)
WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
}
for (i = 0; i < MAX_WAVEINDRV; ++i)
{
WInDev[i].record_stream = (arts_stream_t*)-1;
memset(&WInDev[i].caps, 0, sizeof(WInDev[i].caps)); /* zero out
caps values */
/* FIXME: some programs compare this string against the content of the registry
* for MM drivers. The names have to match in order for the program to work
* (e.g. MS win9x mplayer.exe)
*/
#ifdef EMULATE_SB16
WInDev[i].caps.wMid = 0x0002;
WInDev[i].caps.wPid = 0x0104;
strcpy(WInDev[i].caps.szPname, "SB16 Wave In");
#else
WInDev[i].caps.wMid = 0x00FF;
WInDev[i].caps.wPid = 0x0001;
strcpy(WInDev[i].caps.szPname,"CS4236/37/38");
#endif
WInDev[i].caps.vDriverVersion = 0x0100;
WInDev[i].caps.dwFormats = 0x00000000;
WInDev[i].caps.wChannels = 2;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
WInDev[i].caps.wReserved1 = 0;
}
return 0;
}
......@@ -360,6 +465,7 @@ static int ARTS_DestroyRingMessage(ARTS_MSG_RING* mr)
{
CloseHandle(mr->msg_event);
HeapFree(GetProcessHeap(),0,mr->messages);
mr->messages=NULL;
DeleteCriticalSection(&mr->msg_crst);
return 0;
}
......@@ -376,9 +482,22 @@ static int ARTS_AddRingMessage(ARTS_MSG_RING* mr, enum win_wm_message msg, DWORD
EnterCriticalSection(&mr->msg_crst);
if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
{
int old_ring_buffer_size = mr->ring_buffer_size;
mr->ring_buffer_size += ARTS_RING_BUFFER_INCREMENT;
TRACE("mr->ring_buffer_size=%d\n",mr->ring_buffer_size);
mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
/* Now we need to rearrange the ring buffer so that the new
buffers just allocated are in between mr->msg_tosave and
mr->msg_toget.
*/
if (mr->msg_tosave < mr->msg_toget)
{
memmove(&(mr->messages[mr->msg_toget + ARTS_RING_BUFFER_INCREMENT]),
&(mr->messages[mr->msg_toget]),
sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
);
mr->msg_toget += ARTS_RING_BUFFER_INCREMENT;
}
}
if (wait)
{
......@@ -554,25 +673,6 @@ static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
}
/**************************************************************************
* wodPlayer_DSPWait [internal]
* Returns the number of milliseconds to wait for the DSP buffer to clear.
* This is based on the number of fragments we want to be clear before
* writing and the number of free fragments we already have.
*/
static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
{
int waitvalue = (wwo->dwBufferSize - arts_stream_get(wwo->play_stream,
ARTS_P_BUFFER_SPACE)) / ((wwo->format.wf.nSamplesPerSec *
wwo->format.wBitsPerSample * wwo->format.wf.nChannels)
/1000);
TRACE("wait value of %d\n", waitvalue);
/* return the time left to play the buffer */
return waitvalue;
}
/**************************************************************************
* wodPlayer_NotifyWait [internal]
* Returns the number of milliseconds to wait before attempting to notify
* completion of the specified wavehdr.
......@@ -618,7 +718,10 @@ static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
if(wwo->buffer_size < toWrite)
{
if(wwo->sound_buffer)
HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
{
HeapRealloc(GetProcessHeap(), 0, wwo->sound_buffer, toWrite);
wwo->buffer_size = toWrite;
}
}
/* if we don't have a buffer then get one */
......@@ -664,14 +767,22 @@ static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
TRACE("written = %d\n", written);
if (written <= 0) return written; /* if we wrote nothing just return */
if (written <= 0)
{
*bytes = 0; /* apparently arts is actually full */
return written; /* if we wrote nothing just return */
}
if (written >= dwLength)
wodPlayer_PlayPtrNext(wwo); /* If we wrote all current wavehdr, skip to the next one */
else
wwo->dwPartialOffset += written; /* Remove the amount written */
*bytes -= written;
if (written < toWrite)
*bytes = 0;
else
*bytes -= written;
wwo->dwWrittenTotal += written; /* update stats on this wave device */
return written; /* return the number of bytes written */
......@@ -690,6 +801,23 @@ static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
{
LPWAVEHDR lpWaveHdr;
if (wwo->lpQueuePtr) {
TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), reserved=(%ld), dwWrittenTotal=(%ld), force=(%d)\n",
wwo->lpQueuePtr,
wwo->lpPlayPtr,
wwo->lpLoopPtr,
wwo->lpQueuePtr->reserved,
wwo->dwWrittenTotal,
force);
} else {
TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), dwWrittenTotal=(%ld), force=(%d)\n",
wwo->lpQueuePtr,
wwo->lpPlayPtr,
wwo->lpLoopPtr,
wwo->dwWrittenTotal,
force);
}
/* Start from lpQueuePtr and keep notifying until:
* - we hit an unwritten wavehdr
* - we hit the beginning of a running loop
......@@ -699,7 +827,7 @@ static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
(force ||
(lpWaveHdr != wwo->lpPlayPtr &&
lpWaveHdr != wwo->lpLoopPtr &&
lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
lpWaveHdr->reserved <= wwo->dwWrittenTotal))) {
wwo->lpQueuePtr = lpWaveHdr->lpNext;
......@@ -854,10 +982,9 @@ static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
availInQ = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE);
TRACE("availInQ = %ld\n", availInQ);
/* input queue empty and output buffer with no space */
if (!wwo->lpPlayPtr && availInQ) {
/* input queue empty */
if (!wwo->lpPlayPtr) {
TRACE("Run out of wavehdr:s... flushing\n");
wwo->dwPlayedTotal = wwo->dwWrittenTotal;
return INFINITE;
}
......@@ -865,7 +992,7 @@ static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
if(!availInQ)
{
TRACE("no more room, no need to try to feed\n");
return wodPlayer_DSPWait(wwo);
return wwo->dwSleepTime;
}
/* Feed from partial wavehdr */
......@@ -878,16 +1005,26 @@ static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
/* Feed wavehdrs until we run out of wavehdrs or DSP space */
if (!wwo->dwPartialOffset)
{
while(wwo->lpPlayPtr && availInQ > SPACE_THRESHOLD)
{
TRACE("feeding waveheaders until we run out of space\n");
/* note the value that dwPlayedTotal will return when this wave finishes playing */
wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
wodPlayer_WriteMaxFrags(wwo, &availInQ);
}
while(wwo->lpPlayPtr && availInQ)
{
TRACE("feeding waveheaders until we run out of space\n");
/* note the value that dwPlayedTotal will return when this wave finishes playing */
wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
TRACE("reserved=(%ld) dwWrittenTotal=(%ld) dwBufferLength=(%ld)\n",
wwo->lpPlayPtr->reserved,
wwo->dwWrittenTotal,
wwo->lpPlayPtr->dwBufferLength
);
wodPlayer_WriteMaxFrags(wwo, &availInQ);
}
}
if (!wwo->lpPlayPtr) {
TRACE("Ran out of wavehdrs\n");
return INFINITE;
}
return wodPlayer_DSPWait(wwo);
return wwo->dwSleepTime;
}
......@@ -1010,14 +1147,19 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
if(!wwo->play_stream) return MMSYSERR_ALLOCATED;
/* Try to set buffer size from constant and store the value that it
was set to for future use */
wwo->dwBufferSize = arts_stream_set(wwo->play_stream,
ARTS_P_BUFFER_SIZE, BUFFER_SIZE);
TRACE("Tried to set BUFFER_SIZE of %d, wwo->dwBufferSize is actually %ld\n", BUFFER_SIZE, wwo->dwBufferSize);
/* Try to set the packet settings from constant and store the value that it
was actually set to for future use */
wwo->packetSettings = arts_stream_set(wwo->play_stream, ARTS_P_PACKET_SETTINGS, WAVEOUT_PACKET_SETTINGS);
TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEOUT_PACKET_SETTINGS, wwo->packetSettings);
wwo->dwBufferSize = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SIZE);
TRACE("Buffer size is now (%ld)\n",wwo->dwBufferSize);
wwo->dwPlayedTotal = 0;
wwo->dwWrittenTotal = 0;
wwo->dwSleepTime = ((1 << (wwo->packetSettings & 0xFFFF)) * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->format.wf.nAvgBytesPerSec;
/* Initialize volume to full level */
wwo->volume_left = 100;
wwo->volume_right = 100;
......@@ -1076,7 +1218,7 @@ static DWORD wodClose(WORD wDevID)
ARTS_DestroyRingMessage(&wwo->msgRing);
ARTS_CloseDevice(wwo); /* close the stream and clean things up */
ARTS_CloseWaveOutDevice(wwo); /* close the stream and clean things up */
ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
}
......@@ -1399,6 +1541,499 @@ DWORD WINAPI ARTS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
}
/*======================================================================*
* Low level WAVE IN implementation *
*======================================================================*/
/**************************************************************************
* widGetNumDevs [internal]
*/
static DWORD widGetNumDevs(void)
{
TRACE("%d \n",MAX_WAVEINDRV);
return MAX_WAVEINDRV;
}
/**************************************************************************
* widNotifyClient [internal]
*/
static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
{
TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
switch (wMsg) {
case WIM_OPEN:
case WIM_CLOSE:
case WIM_DATA:
if (wwi->wFlags != DCB_NULL &&
!DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
(HDRVR)wwi->waveDesc.hWave, wMsg,
wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
WARN("can't notify client !\n");
return MMSYSERR_ERROR;
}
break;
default:
FIXME("Unknown callback message %u\n", wMsg);
return MMSYSERR_INVALPARAM;
}
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widGetDevCaps [internal]
*/
static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
{
TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
if (wDevID >= MAX_WAVEINDRV) {
TRACE("MAX_WAVINDRV reached !\n");
return MMSYSERR_BADDEVICEID;
}
memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widRecorder [internal]
*/
static DWORD CALLBACK widRecorder(LPVOID pmt)
{
WORD uDevID = (DWORD)pmt;
WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
WAVEHDR* lpWaveHdr;
DWORD dwSleepTime;
DWORD bytesRead;
int dwBufferSpace;
enum win_wm_message msg;
DWORD param;
HANDLE ev;
SetEvent(wwi->hStartUpEvent);
/* make sleep time to be # of ms to record one packet */
dwSleepTime = ((1 << (wwi->packetSettings & 0xFFFF)) * 1000) / wwi->format.wf.nAvgBytesPerSec;
TRACE("sleeptime=%ld ms\n", dwSleepTime);
for(;;) {
/* Oddly enough, dwBufferSpace is sometimes negative....
*
* NOTE: If you remove this call to arts_stream_get() and
* remove the && (dwBufferSpace > 0) the code will still
* function correctly. I don't know which way is
* faster/better.
*/
dwBufferSpace = arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SPACE);
TRACE("wwi->lpQueuePtr=(%p), wwi->state=(%d), dwBufferSpace=(%d)\n",wwi->lpQueuePtr,wwi->state,dwBufferSpace);
/* read all data is arts input buffer. */
if ((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING) && (dwBufferSpace > 0))
{
lpWaveHdr = wwi->lpQueuePtr;
TRACE("read as much as we can\n");
while(wwi->lpQueuePtr)
{
TRACE("attempt to read %ld bytes\n",lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
bytesRead = arts_read(wwi->record_stream,
lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
TRACE("bytesRead=%ld\n",bytesRead);
if (bytesRead==0) break;
lpWaveHdr->dwBytesRecorded += bytesRead;
wwi->dwRecordedTotal += bytesRead;
/* buffer full. notify client */
if (lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength)
{
/* must copy the value of next waveHdr, because we have no idea of what
* will be done with the content of lpWaveHdr in callback
*/
LPWAVEHDR lpNext = lpWaveHdr->lpNext;
TRACE("waveHdr full.\n");
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
lpWaveHdr = wwi->lpQueuePtr = lpNext;
}
}
}
/* wait for dwSleepTime or an event in thread's queue */
WaitForSingleObject(wwi->msgRing.msg_event, dwSleepTime);
while (ARTS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
{
TRACE("msg=%s param=0x%lx\n",wodPlayerCmdString[msg - WM_USER - 1], param);
switch(msg) {
case WINE_WM_PAUSING:
wwi->state = WINE_WS_PAUSED;
/* Put code here to "pause" arts recording
*/
SetEvent(ev);
break;
case WINE_WM_STARTING:
wwi->state = WINE_WS_PLAYING;
/* Put code here to "start" arts recording
*/
SetEvent(ev);
break;
case WINE_WM_HEADER:
lpWaveHdr = (LPWAVEHDR)param;
/* insert buffer at end of queue */
{
LPWAVEHDR* wh;
int num_headers = 0;
for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
{
num_headers++;
}
*wh=lpWaveHdr;
}
break;
case WINE_WM_STOPPING:
if (wwi->state != WINE_WS_STOPPED)
{
/* Put code here to "stop" arts recording
*/
/* return current buffer to app */
lpWaveHdr = wwi->lpQueuePtr;
if (lpWaveHdr)
{
LPWAVEHDR lpNext = lpWaveHdr->lpNext;
TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
wwi->lpQueuePtr = lpNext;
}
}
wwi->state = WINE_WS_STOPPED;
SetEvent(ev);
break;
case WINE_WM_RESETTING:
wwi->state = WINE_WS_STOPPED;
wwi->dwRecordedTotal = 0;
/* return all buffers to the app */
for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
}
wwi->lpQueuePtr = NULL;
SetEvent(ev);
break;
case WINE_WM_CLOSING:
wwi->hThread = 0;
wwi->state = WINE_WS_CLOSED;
SetEvent(ev);
ExitThread(0);
/* shouldn't go here */
default:
FIXME("unknown message %d\n", msg);
break;
}
}
}
ExitThread(0);
/* just for not generating compilation warnings... should never be executed */
return 0;
}
/**************************************************************************
* widOpen [internal]
*/
static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
{
WINE_WAVEIN* wwi;
TRACE("(%u, %p %08lX);\n",wDevID, lpDesc, dwFlags);
if (lpDesc == NULL) {
WARN("Invalid Parametr (lpDesc == NULL)!\n");
return MMSYSERR_INVALPARAM;
}
if (wDevID >= MAX_WAVEINDRV) {
TRACE ("MAX_WAVEINDRV reached !\n");
return MMSYSERR_BADDEVICEID;
}
/* if this device is already open tell the app that it is allocated */
if(WInDev[wDevID].record_stream != (arts_stream_t*)-1)
{
TRACE("device already allocated\n");
return MMSYSERR_ALLOCATED;
}
/* only PCM format is support 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;
}
wwi = &WInDev[wDevID];
/* direct sound not supported, ignore the flag */
dwFlags &= ~WAVE_DIRECTSOUND;
wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
if (wwi->format.wBitsPerSample == 0) {
WARN("Resetting zerod wBitsPerSample\n");
wwi->format.wBitsPerSample = 8 *
(wwi->format.wf.nAvgBytesPerSec /
wwi->format.wf.nSamplesPerSec) /
wwi->format.wf.nChannels;
}
wwi->record_stream = arts_record_stream(wwi->format.wf.nSamplesPerSec,
wwi->format.wBitsPerSample,
wwi->format.wf.nChannels,
"winearts");
TRACE("(wwi->record_stream=%p)\n",wwi->record_stream);
wwi->state = WINE_WS_STOPPED;
wwi->packetSettings = arts_stream_set(wwi->record_stream, ARTS_P_PACKET_SETTINGS, WAVEIN_PACKET_SETTINGS);
TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEIN_PACKET_SETTINGS, wwi->packetSettings);
TRACE("Buffer size is now (%d)\n", arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SIZE));
if (wwi->lpQueuePtr) {
WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
wwi->lpQueuePtr = NULL;
}
arts_stream_set(wwi->record_stream, ARTS_P_BLOCKING, 0); /* disable blocking on this stream */
if(!wwi->record_stream) return MMSYSERR_ALLOCATED;
wwi->dwRecordedTotal = 0;
wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
ARTS_InitRingMessage(&wwi->msgRing);
/* create recorder thread */
if (!(dwFlags & WAVE_DIRECTSOUND)) {
wwi->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
CloseHandle(wwi->hStartUpEvent);
} else {
wwi->hThread = INVALID_HANDLE_VALUE;
wwi->dwThreadID = 0;
}
wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
wwi->format.wf.nBlockAlign);
return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
}
/**************************************************************************
* widClose [internal]
*/
static DWORD widClose(WORD wDevID)
{
WINE_WAVEIN* wwi;
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't close !\n");
return MMSYSERR_INVALHANDLE;
}
wwi = &WInDev[wDevID];
if (wwi->lpQueuePtr != NULL) {
WARN("still buffers open !\n");
return WAVERR_STILLPLAYING;
}
ARTS_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
ARTS_CloseWaveInDevice(wwi);
wwi->state = WINE_WS_CLOSED;
ARTS_DestroyRingMessage(&wwi->msgRing);
return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
}
/**************************************************************************
* widAddBuffer [internal]
*/
static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
{
TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't do it !\n");
return MMSYSERR_INVALHANDLE;
}
if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
TRACE("never been prepared !\n");
return WAVERR_UNPREPARED;
}
if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
TRACE("header already in use !\n");
return WAVERR_STILLPLAYING;
}
lpWaveHdr->dwFlags |= WHDR_INQUEUE;
lpWaveHdr->dwFlags &= ~WHDR_DONE;
lpWaveHdr->dwBytesRecorded = 0;
lpWaveHdr->lpNext = NULL;
ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widPrepare [internal]
*/
static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
{
TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
return WAVERR_STILLPLAYING;
lpWaveHdr->dwFlags |= WHDR_PREPARED;
lpWaveHdr->dwFlags &= ~WHDR_DONE;
lpWaveHdr->dwBytesRecorded = 0;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widUnprepare [internal]
*/
static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
{
TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
if (wDevID >= MAX_WAVEINDRV) {
WARN("bad device ID !\n");
return MMSYSERR_INVALHANDLE;
}
if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
TRACE("Still playing...\n");
return WAVERR_STILLPLAYING;
}
lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
lpWaveHdr->dwFlags |= WHDR_DONE;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widStart [internal]
*/
static DWORD widStart(WORD wDevID)
{
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't start recording !\n");
return MMSYSERR_INVALHANDLE;
}
ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widStop [internal]
*/
static DWORD widStop(WORD wDevID)
{
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't stop !\n");
return MMSYSERR_INVALHANDLE;
}
ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widReset [internal]
*/
static DWORD widReset(WORD wDevID)
{
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't reset !\n");
return MMSYSERR_INVALHANDLE;
}
ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widMessage (WINEARTS.6)
*/
DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
DWORD dwParam1, DWORD dwParam2)
{
TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
switch (wMsg) {
case DRVM_INIT:
case DRVM_EXIT:
case DRVM_ENABLE:
case DRVM_DISABLE:
/* FIXME: Pretend this is supported */
return 0;
case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
case WIDM_CLOSE: return widClose (wDevID);
case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
case WIDM_GETNUMDEVS: return widGetNumDevs ();
case WIDM_RESET: return widReset (wDevID);
case WIDM_START: return widStart (wDevID);
case WIDM_STOP: return widStop (wDevID);
default:
FIXME("unknown message %d!\n", wMsg);
}
return MMSYSERR_NOTSUPPORTED;
}
/*======================================================================*
* Low level DSOUND implementation *
*======================================================================*/
static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
......@@ -1436,4 +2071,14 @@ DWORD WINAPI ARTS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
return MMSYSERR_NOTENABLED;
}
/**************************************************************************
* widMessage (WINEARTS.6)
*/
DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
DWORD dwParam1, DWORD dwParam2)
{
FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
return MMSYSERR_NOTENABLED;
}
#endif /* HAVE_ARTS */
@ stdcall DriverProc(long long long long long) ARTS_DriverProc
@ stdcall wodMessage(long long long long long) ARTS_wodMessage
@ stdcall widMessage(long long long long long) ARTS_widMessage
/* -*- tab-width: 8; c-basic-offset: 4 -*- */
/* -*- tab-width: 8; c-basic-offset: 2 -*- */
/*
* Wine Driver for jack Sound Server
* http://jackit.sourceforge.net
......@@ -29,7 +29,6 @@
* right now we use the winmm layer to do resampling although it would
* be nice to have a full set of algorithms to choose from based on cpu
* time
* implement wave-in support with jack
*
* FIXME:
* pause in waveOut during loop is not handled correctly
......@@ -83,6 +82,7 @@ MAKE_FUNCPTR(jack_port_register);
MAKE_FUNCPTR(jack_port_get_buffer);
MAKE_FUNCPTR(jack_get_ports);
MAKE_FUNCPTR(jack_port_name);
MAKE_FUNCPTR(jack_get_buffer_size);
#undef MAKE_FUNCPTR
/* define the below to work around a bug in jack where closing a port */
......@@ -96,7 +96,7 @@ typedef jack_nframes_t nframes_t;
/* only allow 10 output devices through this driver, this ought to be adequate */
#define MAX_WAVEOUTDRV (10)
#define MAX_WAVEINDRV (1)
#define MAX_WAVEINDRV (10)
/* state diagram for waveOut writing:
*
......@@ -170,6 +170,19 @@ typedef struct {
DWORD dwTotalRecorded;
WAVEINCAPSA caps;
BOOL bTriggerSupport;
WORD wDevID;
jack_port_t* in_port_l; /* ports for left and right channels */
jack_port_t* in_port_r;
jack_client_t* client;
long sample_rate; /* jack server sample rate */
#if JACK_CLOSE_HACK
BOOL in_use; /* TRUE if this device is in use */
#endif
char* sound_buffer;
unsigned long buffer_size;
/* synchronization stuff */
CRITICAL_SECTION access_crst;
......@@ -185,12 +198,19 @@ static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);
static LPWAVEHDR wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo);
static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force);
static int JACK_OpenDevice(WINE_WAVEOUT* wwo);
static int JACK_OpenWaveOutDevice(WINE_WAVEOUT* wwo);
static int JACK_OpenWaveInDevice(WINE_WAVEIN* wwi, WORD nChannels);
#if JACK_CLOSE_HACK
static void JACK_CloseWaveOutDevice(WINE_WAVEOUT* wwo, BOOL close_client);
#else
static void JACK_CloseWaveOutDevice(WINE_WAVEOUT* wwo);
#endif
#if JACK_CLOSE_HACK
static void JACK_CloseDevice(WINE_WAVEOUT* wwo, BOOL close_client);
static void JACK_CloseWaveInDevice(WINE_WAVEIN* wwi, BOOL close_client);
#else
static void JACK_CloseDevice(WINE_WAVEOUT* wwo);
static void JACK_CloseWaveInDevice(WINE_WAVEIN* wwi);
#endif
......@@ -253,6 +273,25 @@ void sample_move_d16_s16 (sample_t *dst, short *src,
}
}
/* convert from floating point to 16 bit */
/* allow for copying of a buffer that will hold a single channel stream */
/* to stereo data with alternating left/right channels */
/* nsamples is in terms of float samples */
/* dst_skip is in terms of 16bit samples */
void sample_move_s16_d16 (short *dst, sample_t *src,
unsigned long nsamples, unsigned long dst_skip)
{
/* ALERT: signed sign-extension portability !!! */
while (nsamples--)
{
*dst = (*src) * SAMPLE_MAX_16BIT;
/* TRACE("src=(%.8f,%p) dst=(%d,%p)\n",*src,src,*dst,dst); */
dst += dst_skip;
src++;
}
}
/* fill dst buffer with nsamples worth of silence */
void sample_silence_dS (sample_t *dst, unsigned long nsamples)
{
......@@ -265,39 +304,30 @@ void sample_silence_dS (sample_t *dst, unsigned long nsamples)
}
/******************************************************************
* JACK_callback
* JACK_callback_wwo
*/
/* everytime the jack server wants something from us it calls this
function, so we either deliver it some sound to play or deliver it nothing
to play */
int JACK_callback (nframes_t nframes, void *arg)
int JACK_callback_wwo (nframes_t nframes, void *arg)
{
sample_t* out_l;
sample_t* out_r;
WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)arg;
TRACE("wDevID: %d, nframes %ld\n", wwo->wDevID, nframes);
TRACE("wDevID: %u, nframes %u state=%u\n", wwo->wDevID, nframes,wwo->state);
if(!wwo->client)
ERR("client is closed, this is weird...\n");
out_l = (sample_t *) fp_jack_port_get_buffer(wwo->out_port_l,
nframes);
out_r = (sample_t *) fp_jack_port_get_buffer(wwo->out_port_r,
nframes);
EnterCriticalSection(&wwo->access_crst);
out_l = (sample_t *) fp_jack_port_get_buffer(wwo->out_port_l, nframes);
out_r = (sample_t *) fp_jack_port_get_buffer(wwo->out_port_r, nframes);
if(wwo->state == WINE_WS_PLAYING)
{
DWORD jackBytesAvailableThisCallback = sizeof(sample_t) * nframes;
DWORD jackBytesLeft = sizeof(sample_t) * nframes;
DWORD inputBytesAvailable; /* number of bytes we have from the app, after conversion to 16bit stereo */
DWORD jackBytesToWrite; /* number of bytes we are going to write out, after conversion */
DWORD bytesInput; /* the number of bytes from the app */
DWORD appBytesToWrite; /* number of bytes from the app we are going to write */
DWORD jackFramesAvailable = nframes;
DWORD outputFramesAvailable;
DWORD numFramesToWrite;
long written = 0;
char* buffer;
......@@ -315,65 +345,53 @@ int JACK_callback (nframes_t nframes, void *arg)
TRACE("wwo.state == WINE_WS_PLAYING\n");
/* see if our buffer is large enough for the data we are writing */
/* ie. Buffer_size < (bytes we already wrote + bytes we are going to write in this loop) */
if(wwo->buffer_size < jackBytesAvailableThisCallback)
/* see if our sound_buffer is large enough to hold the number of frames jack requested */
/* Note: sound_buffer is always filled with 16-bit stereo data, even for mono mode */
if(wwo->buffer_size < (nframes * sizeof(short) * 2))
{
ERR("for some reason JACK_BufSize() didn't allocate enough memory\n");
ERR("allocated %ld bytes, need %ld bytes\n", wwo->buffer_size,
jackBytesAvailableThisCallback);
LeaveCriticalSection(&wwo->access_crst);
ERR("allocated %ld bytes, need %d bytes\n", wwo->buffer_size, (nframes * sizeof(short) * 2));
return 0;
}
/* while we have jackBytesLeft and a wave header to be played */
while(jackBytesLeft && wwo->lpPlayPtr)
/* while we have jackFramesAvailable and a wave header to be played */
while(jackFramesAvailable && wwo->lpPlayPtr)
{
/* find the amount of audio to be played at this time */
bytesInput = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
inputBytesAvailable = bytesInput;
/* calculate inputBytesAvailable based on audio format conversion */
if(wwo->format.wf.nChannels == 1)
inputBytesAvailable<<=1; /* multiply by two for mono->stereo conversion */
outputFramesAvailable = (wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset) / wwo->format.wf.nBlockAlign;
/* find the minimum of the inputBytesAvailable and the space available */
jackBytesToWrite = min(jackBytesLeft, inputBytesAvailable);
/* calculate appBytesToWrite based on audio format conversion */
appBytesToWrite = jackBytesToWrite;
if(wwo->format.wf.nChannels == 1)
appBytesToWrite>>=1; /* divide by two for stereo->mono conversion */
TRACE("jackBytesToWrite == %ld, appBytesToWrite == %ld\n", jackBytesToWrite, appBytesToWrite);
numFramesToWrite = min(jackFramesAvailable, outputFramesAvailable);
TRACE("dwBufferLength=(%ld) dwPartialOffset=(%ld)\n",wwo->lpPlayPtr->dwBufferLength,wwo->dwPartialOffset);
TRACE("outputFramesAvailable == %ld, jackFramesAvailable == %ld\n", outputFramesAvailable, jackFramesAvailable);
buffer = wwo->lpPlayPtr->lpData + wwo->dwPartialOffset;
/* convert from mono to stereo if necessary */
/* otherwise just memcpy to the output buffer */
if(wwo->format.wf.nChannels == 1)
{
sample_move_d16_d16((short*)wwo->sound_buffer +((jackBytesAvailableThisCallback - jackBytesLeft) / sizeof(short)),
(short*)buffer, jackBytesToWrite, wwo->format.wf.nChannels);
sample_move_d16_d16((short*)wwo->sound_buffer + ((nframes - jackFramesAvailable) * sizeof(short)),
(short*)buffer, numFramesToWrite, wwo->format.wf.nChannels);
} else /* just copy the memory over */
{
memcpy(wwo->sound_buffer + (jackBytesAvailableThisCallback - jackBytesLeft),
buffer, jackBytesToWrite);
memcpy(wwo->sound_buffer + ((nframes - jackFramesAvailable) * wwo->format.wf.nBlockAlign),
buffer, numFramesToWrite * wwo->format.wf.nBlockAlign);
}
/* advance to the next wave header if possible, or advance pointer */
/* inside of the current header if we haven't completed it */
if(appBytesToWrite == bytesInput)
if(numFramesToWrite == outputFramesAvailable)
{
wodHelper_PlayPtrNext(wwo); /* we wrote the whole waveheader, skip to the next one*/
}
else
{
wwo->dwPartialOffset+=appBytesToWrite; /* else advance by the bytes we took in to write */
wwo->dwPartialOffset+=(numFramesToWrite * wwo->format.wf.nBlockAlign); /* else advance by the bytes we took in to write */
}
written+=appBytesToWrite; /* add on what we wrote */
jackBytesLeft-=jackBytesToWrite; /* take away what was written in terms of output bytes */
written+=(numFramesToWrite * wwo->format.wf.nBlockAlign); /* add on what we wrote */
jackFramesAvailable-=numFramesToWrite; /* take away what was written in terms of output bytes */
}
wwo->tickCountMS = GetTickCount(); /* record the current time */
......@@ -386,25 +404,22 @@ int JACK_callback (nframes_t nframes, void *arg)
/* the audio to the jack server */
/* apply volume to the buffer */
/* NOTE: buffer_size >> 2 to convert from bytes to 16 bit stereo(32bit) samples */
volume_effect32(wwo->sound_buffer, (jackBytesAvailableThisCallback - jackBytesLeft)>>2, wwo->volume_left,
wwo->volume_right);
volume_effect32(wwo->sound_buffer, (nframes - jackFramesAvailable), wwo->volume_left, wwo->volume_right);
/* convert from stereo 16 bit to single channel 32 bit float */
/* for each jack server channel */
/* NOTE: we skip over two sample since we want to only get either the left or right channel */
sample_move_d16_s16(out_l, (short*)wwo->sound_buffer, (jackBytesAvailableThisCallback - jackBytesLeft)>>2, 2);
sample_move_d16_s16(out_r, (short*)wwo->sound_buffer + 1,
(jackBytesAvailableThisCallback - jackBytesLeft)>>2, 2);
sample_move_d16_s16(out_l, (short*)wwo->sound_buffer, (nframes - jackFramesAvailable), 2);
sample_move_d16_s16(out_r, (short*)wwo->sound_buffer + 1, (nframes - jackFramesAvailable), 2);
/* see if we still have jackBytesLeft here, if we do that means that we
ran out of wave data to play and had a buffer underrun, fill in
the rest of the space with zero bytes */
if(jackBytesLeft)
if(jackFramesAvailable)
{
ERR("buffer underrun of %ld bytes\n", jackBytesLeft);
sample_silence_dS(out_l + ((jackBytesAvailableThisCallback - jackBytesLeft) / sizeof(sample_t)), jackBytesLeft / sizeof(sample_t));
sample_silence_dS(out_r + ((jackBytesAvailableThisCallback - jackBytesLeft) / sizeof(sample_t)), jackBytesLeft / sizeof(sample_t));
ERR("buffer underrun of %ld frames\n", jackFramesAvailable);
sample_silence_dS(out_l + (nframes - jackFramesAvailable), jackFramesAvailable);
sample_silence_dS(out_r + (nframes - jackFramesAvailable), jackFramesAvailable);
}
}
else if(wwo->state == WINE_WS_PAUSED ||
......@@ -417,33 +432,34 @@ int JACK_callback (nframes_t nframes, void *arg)
}
/* notify the client of completed wave headers */
EnterCriticalSection(&wwo->access_crst);
wodHelper_NotifyCompletions(wwo, FALSE);
LeaveCriticalSection(&wwo->access_crst);
TRACE("ending\n");
return 0;
}
/******************************************************************
* JACK_bufsize
* JACK_bufsize_wwo
*
* Called whenever the jack server changes the the max number
* of frames passed to JACK_callback
*/
int JACK_bufsize (nframes_t nframes, void *arg)
int JACK_bufsize_wwo (nframes_t nframes, void *arg)
{
WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)arg;
DWORD buffer_required;
TRACE("the maximum buffer size is now %lu frames\n", nframes);
TRACE("wDevID=%d\n",wwo->wDevID);
TRACE("the maximum buffer size is now %u frames\n", nframes);
/* make sure the callback routine has adequate memory */
/* see if our buffer is large enough for the data we are writing */
/* ie. Buffer_size < (bytes we already wrote + bytes we are going to write in this loop) */
EnterCriticalSection(&wwo->access_crst);
buffer_required = sizeof(sample_t) * nframes;
/* wwo->sound_buffer is always filled with 16-bit stereo data, even for mono streams */
buffer_required = nframes * sizeof(short) * 2;
TRACE("wwo->buffer_size (%ld) buffer_required (%ld).\n", wwo->buffer_size,buffer_required);
if(wwo->buffer_size < buffer_required)
{
TRACE("expanding buffer from wwo->buffer_size == %ld, to %ld\n",
......@@ -452,9 +468,9 @@ int JACK_bufsize (nframes_t nframes, void *arg)
wwo->buffer_size = buffer_required;
if (wwo->sound_buffer)
wwo->sound_buffer = HeapReAlloc(GetProcessHeap(), 0, wwo->sound_buffer, wwo->buffer_size);
wwo->sound_buffer = HeapReAlloc(GetProcessHeap(), 0, wwo->sound_buffer, wwo->buffer_size);
else
wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, wwo->buffer_size);
wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, wwo->buffer_size);
/* if we don't have a buffer then error out */
if(!wwo->sound_buffer)
......@@ -467,26 +483,37 @@ int JACK_bufsize (nframes_t nframes, void *arg)
LeaveCriticalSection(&wwo->access_crst);
TRACE("called\n");
TRACE("ending\n");
return 0;
}
/******************************************************************
* JACK_bufsize_wwi
*
* Called whenever the jack server changes the the max number
* of frames passed to JACK_callback
*/
int JACK_bufsize_wwi (nframes_t nframes, void *arg)
{
TRACE("the maximum buffer size is now %u frames\n", nframes);
return 0;
}
/******************************************************************
* JACK_srate
*/
int JACK_srate (nframes_t nframes, void *arg)
{
TRACE("the sample rate is now %lu/sec\n", nframes);
TRACE("the sample rate is now %u/sec\n", nframes);
return 0;
}
/******************************************************************
* JACK_shutdown
* JACK_shutdown_wwo
*/
/* if this is called then jack shut down... handle this appropriately */
void JACK_shutdown(void* arg)
void JACK_shutdown_wwo(void* arg)
{
WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)arg;
......@@ -496,7 +523,27 @@ void JACK_shutdown(void* arg)
/* lets see if we can't reestablish the connection */
Sleep(750); /* pause for a short period of time */
if(!JACK_OpenDevice(wwo))
if(!JACK_OpenWaveOutDevice(wwo))
{
ERR("unable to reconnect with jack...\n");
}
}
/******************************************************************
* JACK_shutdown_wwi
*/
/* if this is called then jack shut down... handle this appropriately */
void JACK_shutdown_wwi(void* arg)
{
WINE_WAVEIN* wwi = (WINE_WAVEIN*)arg;
wwi->client = 0; /* reset client */
TRACE("trying to reconnect after sleeping for a short while...\n");
/* lets see if we can't reestablish the connection */
Sleep(750); /* pause for a short period of time */
if(!JACK_OpenWaveInDevice(wwi,wwi->format.wf.nChannels))
{
ERR("unable to reconnect with jack...\n");
}
......@@ -504,9 +551,9 @@ void JACK_shutdown(void* arg)
/******************************************************************
* JACK_OpenDevice
* JACK_OpenWaveOutDevice
*/
static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
static int JACK_OpenWaveOutDevice(WINE_WAVEOUT* wwo)
{
const char** ports;
int i;
......@@ -537,8 +584,8 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
wwo->buffer_size = 0;
/* try to become a client of the JACK server */
snprintf(client_name, sizeof(client_name), "wine_jack_client %d", wwo->wDevID);
TRACE("client name '%s'\n", client_name);
snprintf(client_name, sizeof(client_name), "wine_jack_out_%d", wwo->wDevID);
TRACE("client name '%s'\n", client_name);
if ((client = fp_jack_client_new (client_name)) == 0)
{
/* jack has problems with shutting down clients, so lets */
......@@ -550,16 +597,16 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
return 0;
}
}
/* tell the JACK server to call `JACK_callback()' whenever
/* tell the JACK server to call `JACK_callback_wwo()' whenever
there is work to be done. */
fp_jack_set_process_callback (client, JACK_callback, wwo);
fp_jack_set_process_callback (client, JACK_callback_wwo, wwo);
/* tell the JACK server to call `JACK_bufsize()' whenever
/* tell the JACK server to call `JACK_bufsize_wwo()' whenever
the maximum number of frames that will be passed
to `JACK_Callback()' changes */
fp_jack_set_buffer_size_callback (client, JACK_bufsize, wwo);
fp_jack_set_buffer_size_callback (client, JACK_bufsize_wwo, wwo);
/* tell the JACK server to call `srate()' whenever
the sample rate of the system changes. */
fp_jack_set_sample_rate_callback (client, JACK_srate, wwo);
......@@ -567,7 +614,7 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
/* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us. */
fp_jack_on_shutdown (client, JACK_shutdown, wwo);
fp_jack_on_shutdown (client, JACK_shutdown_wwo, wwo);
/* display the current sample rate. once the client is activated
(see below), you should rely on your own sample rate
......@@ -583,6 +630,8 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
out_port_r = fp_jack_port_register (client, "out_r",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
TRACE("Created ports. (%p) (%p)\n",out_port_l, out_port_r);
/* save away important values to the WINE_WAVEOUT struct */
wwo->client = client;
wwo->out_port_l = out_port_l;
......@@ -592,6 +641,9 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
wwo->in_use = TRUE; /* mark this device as in use since it now is ;-) */
#endif
/* set initial buffer size */
JACK_bufsize_wwo (fp_jack_get_buffer_size(client),wwo);
/* tell the JACK server that we are ready to roll */
if (fp_jack_activate (client))
{
......@@ -599,6 +651,7 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
return 0;
}
TRACE("jack activate.\n");
/* figure out what the ports that we want to output on are */
/* NOTE: we do this instead of using stuff like "alsa_pcm:playback_X" because */
/* this way works if names are changed */
......@@ -637,7 +690,11 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
/* if something failed we need to shut the client down and return 0 */
if(failed)
{
JACK_CloseDevice(wwo, TRUE);
#if JACK_CLOSE_HACK
JACK_CloseWaveOutDevice(wwo, TRUE);
#else
JACK_CloseWaveOutDevice(wwo);
#endif
return 0;
}
......@@ -645,20 +702,20 @@ static int JACK_OpenDevice(WINE_WAVEOUT* wwo)
}
/******************************************************************
* JACK_CloseDevice
* JACK_CloseWaveOutDevice
*
* Close the connection to the server cleanly.
* If close_client is TRUE we close the client for this device instead of
* just marking the device as in_use(JACK_CLOSE_HACK only)
*/
#if JACK_CLOSE_HACK
static void JACK_CloseDevice(WINE_WAVEOUT* wwo, BOOL close_client)
static void JACK_CloseWaveOutDevice(WINE_WAVEOUT* wwo, BOOL close_client)
#else
static void JACK_CloseDevice(WINE_WAVEOUT* wwo)
static void JACK_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
#endif
{
#if JACK_CLOSE_HACK
TRACE("wDevID: %d, close_client: %d\n", wwo->wDevID, close_client);
TRACE("wDevID: %d, close_client (wwo): %d\n", wwo->wDevID, close_client);
#else
TRACE("wDevID: %d\n", wwo->wDevID);
#endif
......@@ -688,6 +745,49 @@ static void JACK_CloseDevice(WINE_WAVEOUT* wwo)
}
/******************************************************************
* JACK_CloseWaveInDevice
*
* Close the connection to the server cleanly.
* If close_client is TRUE we close the client for this device instead of
* just marking the device as in_use(JACK_CLOSE_HACK only)
*/
#if JACK_CLOSE_HACK
static void JACK_CloseWaveInDevice(WINE_WAVEIN* wwi, BOOL close_client)
#else
static void JACK_CloseWaveInDevice(WINE_WAVEIN* wwi)
#endif
{
#if JACK_CLOSE_HACK
TRACE("wDevID: %d, close_client (wwi): %d\n", wwi->wDevID, close_client);
#else
TRACE("wDevID: %d\n", wwi->wDevID);
#endif
#if JACK_CLOSE_HACK
if(close_client)
{
#endif
fp_jack_deactivate(wwi->client); /* supposed to help the jack_client_close() to succeed */
fp_jack_client_close (wwi->client);
EnterCriticalSection(&wwi->access_crst);
wwi->client = 0; /* reset client */
HeapFree(GetProcessHeap(), 0, wwi->sound_buffer); /* free buffer memory */
wwi->sound_buffer = 0;
wwi->buffer_size = 0; /* zero out size of the buffer */
LeaveCriticalSection(&wwi->access_crst);
#if JACK_CLOSE_HACK
} else
{
EnterCriticalSection(&wwi->access_crst);
TRACE("setting in_use to FALSE\n");
wwi->in_use = FALSE;
LeaveCriticalSection(&wwi->access_crst);
}
#endif
}
/******************************************************************
* JACK_WaveRelease
*
*
......@@ -696,23 +796,40 @@ LONG JACK_WaveRelease(void)
{
int iDevice;
TRACE("closing all open devices\n");
TRACE("closing all open waveout devices\n");
/* close all open devices */
/* close all open output devices */
for(iDevice = 0; iDevice < MAX_WAVEOUTDRV; iDevice++)
{
TRACE("iDevice == %d\n", iDevice);
if(WOutDev[iDevice].client)
{
#if JACK_CLOSE_HACK
JACK_CloseDevice(&WOutDev[iDevice], TRUE); /* close the device, FORCE the client to close */
JACK_CloseWaveOutDevice(&WOutDev[iDevice], TRUE); /* close the device, FORCE the client to close */
#else
JACK_CloseDevice(&WOutDev[iDevice]); /* close the device, FORCE the client to close */
JACK_CloseWaveOutDevice(&WOutDev[iDevice]); /* close the device, FORCE the client to close */
#endif
DeleteCriticalSection(&(WOutDev[iDevice].access_crst)); /* delete the critical section */
}
}
TRACE("closing all open wavein devices\n");
/* close all open input devices */
for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
{
TRACE("iDevice == %d\n", iDevice);
if(WInDev[iDevice].client)
{
#if JACK_CLOSE_HACK
JACK_CloseWaveInDevice(&WInDev[iDevice], TRUE); /* close the device, FORCE the client to close */
#else
JACK_CloseWaveInDevice(&WInDev[iDevice]); /* close the device, FORCE the client to close */
#endif
DeleteCriticalSection(&(WInDev[iDevice].access_crst)); /* delete the critical section */
}
}
TRACE("returning 1\n");
return 1;
......@@ -745,6 +862,7 @@ LONG JACK_WaveInit(void)
LOAD_FUNCPTR(jack_port_get_buffer);
LOAD_FUNCPTR(jack_get_ports);
LOAD_FUNCPTR(jack_port_name);
LOAD_FUNCPTR(jack_get_buffer_size);
#undef LOAD_FUNCPTR
/* start with output device */
......@@ -755,6 +873,7 @@ LONG JACK_WaveInit(void)
#if JACK_CLOSE_HACK
WOutDev[i].in_use = FALSE;
WInDev[i].in_use = FALSE;
#endif
memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
......@@ -800,6 +919,37 @@ LONG JACK_WaveInit(void)
{
/* TODO: we should initialize read stuff here */
memset(&WInDev[0].caps, 0, sizeof(WInDev[0].caps));
/* FIXME: some programs compare this string against the content of the registry
* for MM drivers. The names have to match in order for the program to work
* (e.g. MS win9x mplayer.exe)
*/
#ifdef EMULATE_SB16
WInDev[i].caps.wMid = 0x0002;
WInDev[i].caps.wPid = 0x0104;
strcpy(WInDev[i].caps.szPname, "SB16 Wave In");
#else
WInDev[i].caps.wMid = 0x00FF;
WInDev[i].caps.wPid = 0x0001;
strcpy(WInDev[i].caps.szPname,"CS4236/37/38");
#endif
WInDev[i].caps.vDriverVersion = 0x0100;
WInDev[i].caps.wChannels = 0x2;
/* NOTE: we don't support any 8 bit modes so note that */
/* WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08; */
WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
/* WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08; */
WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
/* WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;*/
WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
WInDev[i].caps.wReserved1 = 0;
}
return 1; /* return success */
......@@ -958,11 +1108,13 @@ static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
TRACE("calling notify client\n");
TRACE("notifying client: lpWaveHdr=(%p) lpPlayPtr=(%p) dwFlags=(%ld)\n",
lpWaveHdr, wwo->lpPlayPtr, lpWaveHdr->dwFlags);
wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
}
TRACE("Not notifying client: lpWaveHdr=(%p) lpPlayPtr=(%p) lpLoopPtr=(%p)\n",
lpWaveHdr, wwo->lpPlayPtr, wwo->lpLoopPtr);
retval = (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr !=
wwo->lpLoopPtr) ? 0 : INFINITE;
......@@ -1042,12 +1194,16 @@ static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
return MMSYSERR_BADDEVICEID;
}
TRACE("dwSupport=(0x%lx), dwFormats=(0x%lx)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
return MMSYSERR_NOERROR;
}
/**************************************************************************
* wodOpen [internal]
*
* NOTE: doesn't it seem like there is a race condition if you try to open
* the same device twice?
*/
static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
{
......@@ -1082,27 +1238,6 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
return WAVERR_BADFORMAT;
}
wwo = &WOutDev[wDevID];
wwo->wDevID = wDevID;
/* Set things up before we call JACK_OpenDevice because */
/* we will start getting callbacks before JACK_OpenDevice */
/* even returns and we want to be initialized before then */
wwo->state = WINE_WS_STOPPED; /* start in a stopped state */
wwo->dwPlayedTotal = 0; /* zero out these totals */
wwo->dwWrittenTotal = 0;
wwo->bytesInJack = 0;
wwo->tickCountMS = 0;
InitializeCriticalSection(&wwo->access_crst); /* initialize the critical section */
/* open up jack ports for this device */
if (!JACK_OpenDevice(&WOutDev[wDevID]))
{
ERR("JACK_OpenDevice(%d) failed\n", wDevID);
return MMSYSERR_ERROR; /* return unspecified error */
}
/* only PCM format is supported so far... */
if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
lpDesc->lpFormat->nChannels == 0 ||
......@@ -1122,15 +1257,41 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
return MMSYSERR_NOERROR;
}
dwFlags &= ~WAVE_DIRECTSOUND; /* direct sound not supported, ignore the flag */
wwo = &WOutDev[wDevID];
wwo->wDevID = wDevID;
/* Set things up before we call JACK_OpenWaveOutDevice because */
/* we will start getting callbacks before JACK_OpenWaveOutDevice */
/* even returns and we want to be initialized before then */
wwo->state = WINE_WS_STOPPED; /* start in a stopped state */
wwo->dwPlayedTotal = 0; /* zero out these totals */
wwo->dwWrittenTotal = 0;
wwo->bytesInJack = 0;
wwo->tickCountMS = 0;
/* Initialize volume to full level */
wwo->volume_left = 100;
wwo->volume_right = 100;
InitializeCriticalSection(&wwo->access_crst); /* initialize the critical section */
EnterCriticalSection(&wwo->access_crst);
dwFlags &= ~WAVE_DIRECTSOUND; /* direct sound not supported, ignore the flag */
wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
/* open up jack ports for this device */
if (!JACK_OpenWaveOutDevice(&WOutDev[wDevID]))
{
ERR("JACK_OpenWaveOutDevice(%d) failed\n", wDevID);
LeaveCriticalSection(&wwo->access_crst);
DeleteCriticalSection(&wwo->access_crst); /* delete the critical section so we can initialize it again from wodOpen() */
return MMSYSERR_ERROR; /* return unspecified error */
}
LeaveCriticalSection(&wwo->access_crst);
/* display the current wave format */
......@@ -1147,10 +1308,11 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
wwo->sample_rate, wwo->format.wf.nSamplesPerSec);
#if JACK_CLOSE_HACK
JACK_CloseDevice(wwo, FALSE); /* close this device, don't force the client to close */
JACK_CloseWaveOutDevice(wwo, FALSE); /* close this device, don't force the client to close */
#else
JACK_CloseDevice(wwo); /* close this device */
JACK_CloseWaveOutDevice(wwo); /* close this device */
#endif
DeleteCriticalSection(&wwo->access_crst); /* delete the critical section so we can initialize it again from wodOpen() */
return WAVERR_BADFORMAT;
}
......@@ -1200,11 +1362,11 @@ static DWORD wodClose(WORD wDevID)
wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
#if JACK_CLOSE_HACK
JACK_CloseDevice(wwo, FALSE); /* close the jack device, DO NOT force the client to close */
JACK_CloseWaveOutDevice(wwo, FALSE); /* close the jack device, DO NOT force the client to close */
#else
JACK_CloseDevice(wwo); /* close the jack device */
DeleteCriticalSection(&wwo->access_crst); /* delete the critical section so we can initialize it again from wodOpen() */
JACK_CloseWaveOutDevice(wwo); /* close the jack device */
#endif
DeleteCriticalSection(&wwo->access_crst); /* delete the critical section so we can initialize it again from wodOpen() */
ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
}
......@@ -1212,6 +1374,7 @@ static DWORD wodClose(WORD wDevID)
return ret;
}
/**************************************************************************
* wodWrite [internal]
*
......@@ -1254,9 +1417,6 @@ static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
*wh = lpWaveHdr;
LeaveCriticalSection(&wwo->access_crst);
EnterCriticalSection(&wwo->access_crst);
if (!wwo->lpPlayPtr)
wodHelper_BeginWaveHdr(wwo,lpWaveHdr);
if (wwo->state == WINE_WS_STOPPED)
......@@ -1626,6 +1786,619 @@ static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid)
*======================================================================*/
/**************************************************************************
* widNotifyClient [internal]
*/
static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
{
TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
switch (wMsg) {
case WIM_OPEN:
case WIM_CLOSE:
case WIM_DATA:
if (wwi->wFlags != DCB_NULL &&
!DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
(HDRVR)wwi->waveDesc.hWave, wMsg, wwi->waveDesc.dwInstance,
dwParam1, dwParam2))
{
WARN("can't notify client !\n");
return MMSYSERR_ERROR;
}
break;
default:
FIXME("Unknown callback message %u\n", wMsg);
return MMSYSERR_INVALPARAM;
}
return MMSYSERR_NOERROR;
}
/******************************************************************
* JACK_callback_wwi
*/
/* everytime the jack server wants something from us it calls this
function */
int JACK_callback_wwi (nframes_t nframes, void *arg)
{
sample_t* in_l;
sample_t* in_r = 0;
WINE_WAVEIN* wwi = (WINE_WAVEIN*)arg;
TRACE("wDevID: %u, nframes %u\n", wwi->wDevID, nframes);
if(!wwi->client)
ERR("client is closed, this is weird...\n");
in_l = (sample_t *) fp_jack_port_get_buffer(wwi->in_port_l, nframes);
if (wwi->in_port_r)
in_r = (sample_t *) fp_jack_port_get_buffer(wwi->in_port_r, nframes);
EnterCriticalSection(&wwi->access_crst);
if((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING))
{
LPWAVEHDR lpWaveHdr = wwi->lpQueuePtr;
nframes_t jackFramesLeft = nframes;
#if JACK_CLOSE_HACK
if(wwi->in_use == FALSE)
{
/* do nothing if nothing is being recorded */
return 0;
}
#endif
TRACE("wwi.state == WINE_WS_PLAYING\n");
while (lpWaveHdr && jackFramesLeft)
{
DWORD waveHdrFramesLeft = (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded) / (sizeof(short) * wwi->format.wf.nChannels);
DWORD numFrames = min (jackFramesLeft, waveHdrFramesLeft);
TRACE ("dwBufferLength=(%lu) dwBytesRecorded=(%ld)\n", lpWaveHdr->dwBufferLength, lpWaveHdr->dwBytesRecorded);
TRACE ("jackFramesLeft=(%u) waveHdrFramesLeft=(%lu)\n", jackFramesLeft, waveHdrFramesLeft);
if (!in_r) {
/* mono */
sample_move_s16_d16((short *)((char *)lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded), in_l+(nframes-jackFramesLeft), numFrames, 1);
} else {
/* stereo */
sample_move_s16_d16((short *)((char *)lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded),
in_l+(nframes-jackFramesLeft), numFrames, 2);
sample_move_s16_d16((short *)((char *)lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded + sizeof(short)),
in_r+(nframes-jackFramesLeft), numFrames, 2);
}
lpWaveHdr->dwBytesRecorded += (numFrames * sizeof(short) * wwi->format.wf.nChannels );
jackFramesLeft -= numFrames;
if (lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength)
{
/* must copy the value of next waveHdr, because we have no idea of what
* will be done with the content of lpWaveHdr in callback
*/
LPWAVEHDR lpNext = lpWaveHdr->lpNext;
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
TRACE("WaveHdr full. dwBytesRecorded=(%lu) dwFlags=(0x%lx)\n",lpWaveHdr->dwBytesRecorded,lpWaveHdr->dwFlags);
widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
lpWaveHdr = wwi->lpQueuePtr = lpNext;
}
}
TRACE ("jackFramesLeft=(%u) lpWaveHdr=(%p)\n", jackFramesLeft, lpWaveHdr);
if (jackFramesLeft > 0) { WARN("Record buffer ran out of WaveHdrs\n"); }
}
LeaveCriticalSection(&wwi->access_crst);
return 0;
}
/******************************************************************
* JACK_OpenWaveInDevice
*/
static int JACK_OpenWaveInDevice(WINE_WAVEIN* wwi, WORD nChannels)
{
const char** ports;
int i;
char client_name[64];
jack_port_t* in_port_l;
jack_port_t* in_port_r = 0;
jack_client_t* client;
int failed = 0;
TRACE("creating jack client and setting up callbacks\n");
if ((nChannels == 0) || (nChannels > 2)) {
ERR ("nChannels = (%d), but we only support mono or stereo.\n", nChannels);
return 0;
}
#if JACK_CLOSE_HACK
/* see if this device is already open */
if(wwi->client)
{
/* if this device is already in use then it is bad for us to be in here */
if(wwi->in_use)
return 0;
TRACE("using existing client\n");
wwi->in_use = TRUE;
return 1;
}
#endif
/* zero out the buffer pointer and the size of the buffer */
wwi->sound_buffer = 0;
wwi->buffer_size = 0;
/* try to become a client of the JACK server */
snprintf(client_name, sizeof(client_name), "wine_jack_in_%d", wwi->wDevID);
TRACE("client name '%s'\n", client_name);
if ((client = fp_jack_client_new (client_name)) == 0)
{
/* jack has problems with shutting down clients, so lets */
/* wait a short while and try once more before we give up */
Sleep(250);
if ((client = fp_jack_client_new (client_name)) == 0)
{
ERR("jack server not running?\n");
return 0;
}
}
wwi->client = client;
/* tell the JACK server to call `JACK_wwi_callback()' whenever
there is work to be done. */
fp_jack_set_process_callback (client, JACK_callback_wwi, wwi);
/* tell the JACK server to call `JACK_bufsize_wwi()' whenever
the maximum number of frames that will be passed
to `JACK_Callback()' changes */
fp_jack_set_buffer_size_callback (client, JACK_bufsize_wwi, wwi);
/* tell the JACK server to call `srate()' whenever
the sample rate of the system changes. */
fp_jack_set_sample_rate_callback (client, JACK_srate, wwi);
/* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us. */
fp_jack_on_shutdown (client, JACK_shutdown_wwi, wwi);
/* display the current sample rate. once the client is activated
(see below), you should rely on your own sample rate
callback (see above) for this value. */
wwi->sample_rate = fp_jack_get_sample_rate(client);
TRACE("engine sample rate: %lu\n", wwi->sample_rate);
/* create the left and right channel output ports */
/* jack's ports are all mono so for stereo you need two */
in_port_l = fp_jack_port_register (client, "in_l",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
wwi->in_port_l = in_port_l;
TRACE("Created port. (%p)\n", in_port_l);
if (nChannels == 2)
{
in_port_r = fp_jack_port_register (client, "in_r",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
TRACE("Created port. (%p)\n", in_port_r);
}
wwi->in_port_r = in_port_r;
#if JACK_CLOSE_HACK
wwi->in_use = TRUE; /* mark this device as in use since it now is ;-) */
#endif
TRACE("activating client.\n");
/* tell the JACK server that we are ready to roll */
if (fp_jack_activate (client))
{
ERR( "cannot activate client\n");
return 0;
}
TRACE("activated client.\n");
/* figure out what the ports that we want to output on are */
/* NOTE: we do this instead of using stuff like "alsa_pcm:playback_X" because */
/* this way works if names are changed */
ports = fp_jack_get_ports(client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput);
/* display a trace of the output ports we found */
for(i = 0; ports[i]; i++)
{
TRACE("ports[%d] = '%s'\n", i, ports[i]);
}
if(!ports)
{
ERR("jack_get_ports() failed to find 'JackPortIsPhysical|JackPortIsOutput'\n");
}
/* connect the ports. Note: you can't do this before
the client is activated (this may change in the future).
*/
/* we want to connect to two ports so we have stereo input ;-) */
if(fp_jack_connect(client, ports[0], fp_jack_port_name(in_port_l)))
{
ERR ("cannot connect to input port %d('%s')\n", 0, ports[0]);
failed = 1;
}
TRACE("Connected (%s)<->(%s)\n",ports[0],fp_jack_port_name(in_port_l));
if ((nChannels == 2) && in_port_r) {
if(fp_jack_connect(client, ports[1], fp_jack_port_name(in_port_r)))
{
ERR ("cannot connect to input port %d('%s')\n", 1, ports[1]);
failed = 1;
}
TRACE("Connected (%s)<->(%s)\n",ports[1],fp_jack_port_name(in_port_r));
}
free(ports); /* free the returned array of ports */
/* if something failed we need to shut the client down and return 0 */
if(failed)
{
#if JACK_CLOSE_HACK
JACK_CloseWaveInDevice(wwi, TRUE);
#else
JACK_CloseWaveInDevice(wwi);
#endif
return 0;
}
TRACE("return success.\n");
return 1; /* return success */
}
/**************************************************************************
* widGetDevCaps [internal]
*/
static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
{
TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
if (wDevID >= MAX_WAVEINDRV) {
TRACE("MAX_WAVEINDRV reached !\n");
return MMSYSERR_BADDEVICEID;
}
memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widOpen [internal]
*/
static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
{
WINE_WAVEIN* wwi;
DWORD retval;
TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
if (lpDesc == NULL)
{
WARN("Invalid Parameter !\n");
return MMSYSERR_INVALPARAM;
}
if (wDevID >= MAX_WAVEINDRV) {
TRACE ("MAX_WAVEINDRV reached !\n");
return MMSYSERR_BADDEVICEID;
}
#if JACK_CLOSE_HACK
if(WInDev[wDevID].client && WOutDev[wDevID].in_use)
#else
if(WInDev[wDevID].client)
#endif
{
TRACE("device %d already allocated\n", wDevID);
return MMSYSERR_ALLOCATED;
}
/* make sure we aren't being opened in 8 bit mode */
if(lpDesc->lpFormat->wBitsPerSample == 8)
{
TRACE("8bits per sample unsupported, returning WAVERR_BADFORMAT\n");
return WAVERR_BADFORMAT;
}
/* 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;
}
wwi = &WInDev[wDevID];
wwi->wDevID = wDevID;
/* Set things up before we call JACK_OpenWaveOutDevice because */
/* we will start getting callbacks before JACK_OpenWaveOutDevice */
/* even returns and we want to be initialized before then */
wwi->state = WINE_WS_STOPPED; /* start in a stopped state */
InitializeCriticalSection(&wwi->access_crst); /* initialize the critical section */
EnterCriticalSection(&wwi->access_crst);
/* open up jack ports for this device */
if (!JACK_OpenWaveInDevice(&WInDev[wDevID], lpDesc->lpFormat->nChannels))
{
ERR("JACK_OpenWaveInDevice(%d) failed\n", wDevID);
LeaveCriticalSection(&wwi->access_crst);
DeleteCriticalSection(&wwi->access_crst);
return MMSYSERR_ERROR; /* return unspecified error */
}
dwFlags &= ~WAVE_DIRECTSOUND; /* direct sound not supported, ignore the flag */
wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
LeaveCriticalSection(&wwi->access_crst);
/* display the current wave format */
TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
wwi->format.wf.nBlockAlign);
/* make sure that we have the same sample rate in our audio stream */
/* as we do in the jack server */
if(wwi->format.wf.nSamplesPerSec != wwi->sample_rate)
{
TRACE("error: jack server sample rate is '%ld', wave sample rate is '%ld'\n",
wwi->sample_rate, wwi->format.wf.nSamplesPerSec);
#if JACK_CLOSE_HACK
JACK_CloseWaveInDevice(wwi, FALSE); /* close this device, don't force the client to close */
#else
JACK_CloseWaveInDevice(wwi); /* close this device */
#endif
DeleteCriticalSection(&wwi->access_crst);
return WAVERR_BADFORMAT;
}
/* check for an invalid number of bits per sample */
if (wwi->format.wBitsPerSample == 0)
{
WARN("Resetting zeroed wBitsPerSample to 16\n");
wwi->format.wBitsPerSample = 16 *
(wwi->format.wf.nAvgBytesPerSec /
wwi->format.wf.nSamplesPerSec) /
wwi->format.wf.nChannels;
}
TRACE("notify client.\n");
EnterCriticalSection(&wwi->access_crst);
retval = widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
LeaveCriticalSection(&wwi->access_crst);
return retval;
}
/**************************************************************************
* widClose [internal]
*/
static DWORD widClose(WORD wDevID)
{
DWORD ret = MMSYSERR_NOERROR;
WINE_WAVEIN* wwi;
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || !WInDev[wDevID].client)
{
WARN("bad device ID !\n");
return MMSYSERR_BADDEVICEID;
}
wwi = &WInDev[wDevID];
if (wwi->lpQueuePtr)
{
WARN("buffers still playing !\n");
ret = WAVERR_STILLPLAYING;
} else
{
/* sanity check: this should not happen since the device must have been reset before */
if (wwi->lpQueuePtr) ERR("out of sync\n");
wwi->state = WINE_WS_CLOSED; /* mark the device as closed */
#if JACK_CLOSE_HACK
JACK_CloseWaveInDevice(wwi, FALSE); /* close the jack device, DO NOT force the client to close */
#else
JACK_CloseWaveInDevice(wwi); /* close the jack device */
#endif
DeleteCriticalSection(&wwi->access_crst); /* delete the critical section so we can initialize it again from wodOpen() */
ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
}
return ret;
}
/**************************************************************************
* widAddBuffer [internal]
*/
static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
{
WINE_WAVEIN* wwi = &WInDev[wDevID];
TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't do it !\n");
return MMSYSERR_INVALHANDLE;
}
if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
TRACE("never been prepared !\n");
return WAVERR_UNPREPARED;
}
if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
TRACE("header already in use !\n");
return WAVERR_STILLPLAYING;
}
lpWaveHdr->dwFlags |= WHDR_INQUEUE;
lpWaveHdr->dwFlags &= ~WHDR_DONE;
lpWaveHdr->dwBytesRecorded = 0;
lpWaveHdr->lpNext = NULL;
EnterCriticalSection(&wwi->access_crst);
/* insert buffer at end of queue */
{
LPWAVEHDR* wh;
for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
*wh=lpWaveHdr;
}
LeaveCriticalSection(&wwi->access_crst);
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widPrepare [internal]
*/
static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
{
TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
return WAVERR_STILLPLAYING;
lpWaveHdr->dwFlags |= WHDR_PREPARED;
lpWaveHdr->dwFlags &= ~WHDR_DONE;
lpWaveHdr->dwBytesRecorded = 0;
lpWaveHdr->lpNext = NULL;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widUnprepare [internal]
*/
static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
{
TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
if (wDevID >= MAX_WAVEINDRV) {
WARN("bad device ID !\n");
return MMSYSERR_INVALHANDLE;
}
if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
TRACE("Still playing...\n");
return WAVERR_STILLPLAYING;
}
lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
lpWaveHdr->dwFlags |= WHDR_DONE;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widStart [internal]
*/
static DWORD widStart(WORD wDevID)
{
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't start recording !\n");
return MMSYSERR_INVALHANDLE;
}
WInDev[wDevID].state = WINE_WS_PLAYING;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widStop [internal]
*/
static DWORD widStop(WORD wDevID)
{
WINE_WAVEIN* wwi = &WInDev[wDevID];
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't stop !\n");
return MMSYSERR_INVALHANDLE;
}
if (wwi->state != WINE_WS_STOPPED)
{
WAVEHDR* lpWaveHdr;
/* do something here to stop recording ??? */
/* return current buffer to app */
lpWaveHdr = wwi->lpQueuePtr;
if (lpWaveHdr)
{
LPWAVEHDR lpNext = lpWaveHdr->lpNext;
TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
wwi->lpQueuePtr = lpNext;
}
}
wwi->state = WINE_WS_STOPPED;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widReset [internal]
*/
static DWORD widReset(WORD wDevID)
{
WINE_WAVEIN* wwi = &WInDev[wDevID];
WAVEHDR* lpWaveHdr;
TRACE("(%u);\n", wDevID);
if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
WARN("can't reset !\n");
return MMSYSERR_INVALHANDLE;
}
wwi->state = WINE_WS_STOPPED;
/* return all buffers to the app */
for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
}
wwi->lpQueuePtr = NULL;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* widMessage (WINEJACK.6)
*/
DWORD WINAPI JACK_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
......@@ -1634,11 +2407,39 @@ DWORD WINAPI JACK_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
switch (wMsg) {
case DRVM_INIT:
case DRVM_EXIT:
case DRVM_ENABLE:
case DRVM_DISABLE:
/* FIXME: Pretend this is supported */
return 0;
case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
case WIDM_CLOSE: return widClose (wDevID);
case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
case WIDM_GETNUMDEVS: return MAX_WAVEINDRV;
case WIDM_RESET: return widReset (wDevID);
case WIDM_START: return widStart (wDevID);
case WIDM_STOP: return widStop (wDevID);
default:
FIXME("unknown message %d!\n", wMsg);
}
return MMSYSERR_NOTSUPPORTED;
}
#else /* !HAVE_JACK_JACK_H */
DWORD WINAPI JACK_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
DWORD dwParam1, DWORD dwParam2)
{
FIXME("(%u, %04X, %08lX, %08lX, %08lX):jack support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
return MMSYSERR_NOTENABLED;
}
/**************************************************************************
* wodMessage (WINEJACK.7)
*/
......
@ stdcall DriverProc(long long long long long) JACK_DriverProc
@ stdcall wodMessage(long long long long long) JACK_wodMessage
@ stdcall widMessage(long long long long long) JACK_widMessage
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment