📄 audio.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Wine Driver for jack Sound Server * http://jackit.sourceforge.net * * Copyright 1994 Martin Ayotte * Copyright 1999 Eric Pouech (async playing in waveOut/waveIn) * Copyright 2000 Eric Pouech (loops in waveOut) * Copyright 2002 Chris Morgan (jack version of this file) * * 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 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * TODO: * implement audio stream resampling for any arbitrary frequenty * 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 */#include "config.h"#include <stdlib.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <fcntl.h>#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winerror.h"#include "wine/winuser16.h"#include "mmddk.h"#include "dsound.h"#include "dsdriver.h"#include "jack.h"#include "wine/debug.h"#ifdef HAVE_JACK_JACK_H#include <jack/jack.h>#endifWINE_DEFAULT_DEBUG_CHANNEL(wave);#ifdef HAVE_JACK_JACK_H#define MAKE_FUNCPTR(f) static typeof(f) * fp_##f = NULL;/* Function pointers for dynamic loading of libjack *//* these are prefixed with "fp_", ie. "fp_jack_client_new" */MAKE_FUNCPTR(jack_activate);MAKE_FUNCPTR(jack_connect);MAKE_FUNCPTR(jack_client_new);MAKE_FUNCPTR(jack_client_close);MAKE_FUNCPTR(jack_deactivate);MAKE_FUNCPTR(jack_set_process_callback);MAKE_FUNCPTR(jack_set_buffer_size_callback);MAKE_FUNCPTR(jack_set_sample_rate_callback);MAKE_FUNCPTR(jack_on_shutdown);MAKE_FUNCPTR(jack_get_sample_rate);MAKE_FUNCPTR(jack_port_register);MAKE_FUNCPTR(jack_port_get_buffer);MAKE_FUNCPTR(jack_get_ports);MAKE_FUNCPTR(jack_port_name);#undef MAKE_FUNCPTR/* define the below to work around a bug in jack where closing a port *//* takes a very long time, so to get around this we actually don't *//* close the port when the device is closed but instead mark the *//* corresponding device as unused */#define JACK_CLOSE_HACK 1typedef jack_default_audio_sample_t sample_t;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)/* state diagram for waveOut writing: * * +---------+-------------+---------------+---------------------------------+ * | state | function | event | new state | * +---------+-------------+---------------+---------------------------------+ * | | open() | | STOPPED | * | PAUSED | write() | | PAUSED | * | STOPPED | write() | <thrd create> | PLAYING | * | PLAYING | write() | HEADER | PLAYING | * | (other) | write() | <error> | | * | (any) | pause() | PAUSING | PAUSED | * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) | * | (any) | reset() | RESETTING | STOPPED | * | (any) | close() | CLOSING | CLOSED | * +---------+-------------+---------------+---------------------------------+ *//* states of the playing device */#define WINE_WS_PLAYING 0#define WINE_WS_PAUSED 1#define WINE_WS_STOPPED 2#define WINE_WS_CLOSED 3typedef struct { volatile int state; /* one of the WINE_WS_ manifest constants */ WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; WAVEOUTCAPSA caps; WORD wDevID; jack_port_t* out_port_l; /* ports for left and right channels */ jack_port_t* out_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; DWORD volume_left; DWORD volume_right; LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */ LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */ DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */ LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */ DWORD dwLoops; /* private copy of loop counter */ DWORD dwPlayedTotal; /* number of bytes actually played since opening */ DWORD dwWrittenTotal; /* number of bytes written to jack since opening */ DWORD bytesInJack; /* bytes that we wrote during the previous JACK_Callback() */ DWORD tickCountMS; /* time in MS of last JACK_Callback() */ /* synchronization stuff */ CRITICAL_SECTION access_crst;} WINE_WAVEOUT;typedef struct { volatile int state; WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; LPWAVEHDR lpQueuePtr; DWORD dwTotalRecorded; WAVEINCAPSA caps; BOOL bTriggerSupport; /* synchronization stuff */ CRITICAL_SECTION access_crst;} 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);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);#if JACK_CLOSE_HACKstatic void JACK_CloseDevice(WINE_WAVEOUT* wwo, BOOL close_client);#elsestatic void JACK_CloseDevice(WINE_WAVEOUT* wwo);#endif/*======================================================================* * Low level WAVE implementation * *======================================================================*/#define SAMPLE_MAX_16BIT 32767.0f/* Alsaplayer function that applies volume changes to a buffer *//* (C) Andy Lo A Foe *//* Length is in terms of 32 bit samples */void volume_effect32(void *buffer, int length, int left, int right){ short *data = (short *)buffer; int i, v; if (right == -1) right = left; for(i = 0; i < length; i++) { v = (int) ((*(data) * left) / 100); *(data++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v); v = (int) ((*(data) * right) / 100); *(data++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v); }}/* move 16 bit mono/stereo to 16 bit stereo */void sample_move_d16_d16(short *dst, short *src, unsigned long nsamples, int nChannels){ while(nsamples--) { *dst = *src; dst++; if(nChannels == 2) src++; *dst = *src; dst++; src++; }}/* convert from 16 bit to floating point *//* allow for copying of stereo data with alternating left/right *//* channels to a buffer that will hold a single channel stream *//* nsamples is in terms of 16bit samples *//* src_skip is in terms of 16bit samples */void sample_move_d16_s16 (sample_t *dst, short *src, unsigned long nsamples, unsigned long src_skip){ /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { *dst = (*src) / SAMPLE_MAX_16BIT; dst++; src += src_skip; }} /* fill dst buffer with nsamples worth of silence */void sample_silence_dS (sample_t *dst, unsigned long nsamples){ /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { *dst = 0; dst++; }} /****************************************************************** * JACK_callback *//* 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){ sample_t* out_l; sample_t* out_r; WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)arg; TRACE("wDevID: %d, nframes %ld\n", wwo->wDevID, nframes); 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); 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 */ long written = 0; char* buffer;#if JACK_CLOSE_HACK if(wwo->in_use == FALSE) { /* output silence if nothing is being outputted */ sample_silence_dS(out_l, nframes); sample_silence_dS(out_r, nframes); return 0; }#endif 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) { 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); return 0; } /* while we have jackBytesLeft and a wave header to be played */ while(jackBytesLeft && 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 */ /* 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); 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); } else /* just copy the memory over */ { memcpy(wwo->sound_buffer + (jackBytesAvailableThisCallback - jackBytesLeft), buffer, jackBytesToWrite); } /* 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) { 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 */ } written+=appBytesToWrite; /* add on what we wrote */ jackBytesLeft-=jackBytesToWrite; /* take away what was written in terms of output bytes */ } wwo->tickCountMS = GetTickCount(); /* record the current time */ wwo->dwWrittenTotal+=written; /* update states on wave device */ wwo->dwPlayedTotal+=wwo->bytesInJack; /* we must have finished with the last bytes or we wouldn't be back inside of this callback again... */ wwo->bytesInJack = written; /* record the bytes inside of jack */ /* Now that we have finished filling the buffer either until it is full or until */ /* we have run out of application sound data to process, apply volume and output */ /* 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); /* 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); /* 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) { 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)); } } else if(wwo->state == WINE_WS_PAUSED || wwo->state == WINE_WS_STOPPED || wwo->state == WINE_WS_CLOSED) { /* output silence if nothing is being outputted */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -