/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. win_audio.c Functions to play sound on the Win32 audio driver (Win 95 or Win NT). */ #include #ifdef __MINGW32__ #include "../config/mmsystem.h" #endif #include #include #include #include "../../Data/Audio.m" AudioVtab *AudioVptr; typedef long int32; static void output_data(int32 * buf, int32 count); /* export the playback mode */ #define PE_MONO 1 #define PE_SIGNED 2 #define PE_16BIT 4 #define PE_ULAW 8 #define PE_BYTESWAP 16 #define DEFAULT_RATE 8000 typedef struct { CRITICAL_SECTION critSect; int32 rate; int32 encoding; int32 extra_param[1]; /* Max audio blocks waiting to be played */ LPHWAVEOUT dev; int nBlocks; } play_audio_t; #pragma argsused static void CALLBACK wave_callback(HWAVE hWave, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { WAVEHDR *wh; HGLOBAL hg; if (uMsg == WOM_DONE) { play_audio_t *dev = (play_audio_t *) dwInstance; EnterCriticalSection(&dev->critSect); wh = (WAVEHDR *) dwParam1; waveOutUnprepareHeader(dev, wh, sizeof(WAVEHDR)); hg = GlobalHandle(wh->lpData); GlobalUnlock(hg); GlobalFree(hg); hg = GlobalHandle(wh); GlobalUnlock(hg); GlobalFree(hg); dev->nBlocks--; LeaveCriticalSection(&dev->critSect); } } static int open_output(play_audio_t *dev) { int i = dev->rate; int j = 1; int mono = (dev->encoding & PE_MONO); int eight_bit = !(dev->encoding & PE_16BIT); int warnings = 0; PCMWAVEFORMAT pcm; MMRESULT res; /* Check if there is at least one audio device */ if (!waveOutGetNumDevs()) { fprintf(stderr, "No audio devices present!"); return -1; } /* They can't mean these */ dev->encoding &= ~(PE_ULAW | PE_BYTESWAP); if (dev->encoding & PE_16BIT) dev->encoding |= PE_SIGNED; else dev->encoding &= ~PE_SIGNED; mono = (dev->encoding & PE_MONO); eight_bit = !(dev->encoding & PE_16BIT); pcm.wf.wFormatTag = WAVE_FORMAT_PCM; pcm.wf.nChannels = mono ? 1 : 2; pcm.wf.nSamplesPerSec = dev->rate; j = 1; if (!mono) { i *= 2; j *= 2; } if (!eight_bit) { i *= 2; j *= 2; } pcm.wf.nAvgBytesPerSec = i; pcm.wf.nBlockAlign = j; pcm.wBitsPerSample = eight_bit ? 8 : 16; res = waveOutOpen(NULL, 0, (LPWAVEFORMAT) & pcm, (DWORD) NULL, (DWORD) 0, WAVE_FORMAT_QUERY); if (res) { fprintf(stderr, "Format not supported!\n"); return -1; } res = waveOutOpen(&dev->dev, 0, (LPWAVEFORMAT) & pcm, (DWORD) (DWORD) wave_callback, (DWORD) dev, CALLBACK_FUNCTION); if (res) { fprintf(stderr, "Can't open audio device"); return -1; } dev->nBlocks = 0; return warnings; } int audio_open(play_audio_t *dev,int dowait) { InitializeCriticalSection(&dev->critSect); dev->rate = DEFAULT_RATE; dev->encoding = PE_16BIT | PE_SIGNED | PE_MONO; dev->extra_param[0] = 16; open_output(dev); return 1; } static void audio_wait(play_audio_t *dev) { while (dev->nBlocks) Sleep(0); } static int play(play_audio_t *dev,void *mem, int len) { HGLOBAL hg; LPWAVEHDR wh; MMRESULT res; while (dev->nBlocks >= dev->extra_param[0]) Sleep(0); hg = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(WAVEHDR)); if (!hg) { fprintf(stderr, "GlobalAlloc failed!"); return FALSE; } wh = GlobalLock(hg); wh->dwBufferLength = len; wh->lpData = mem; res = waveOutPrepareHeader(dev->dev, wh, sizeof(WAVEHDR)); if (res) { fprintf(stderr, "waveOutPrepareHeader: %d", res); GlobalUnlock(hg); GlobalFree(hg); return TRUE; } res = waveOutWrite(dev->dev, wh, sizeof(WAVEHDR)); if (res) { fprintf(stderr, "waveOutWrite: %d", res); GlobalUnlock(hg); GlobalFree(hg); return TRUE; } EnterCriticalSection(&dev->critSect); dev->nBlocks++; LeaveCriticalSection(&dev->critSect); return FALSE; } void conv8bit(short *lp, int c) { unsigned char *cp = (unsigned char *) lp; short l; while (c--) { short l = (*lp++) >> (16 - 8); if (l > 127) l = 127; else if (l < -128) l = -128; *cp++ = 0x80 ^ ((unsigned char) l); } } void audio_play16(play_audio_t * dev, int count, short *buf) { int len = count; HGLOBAL hg; void *b; if (!(dev->encoding & PE_MONO)) /* Stereo sample */ { count *= 2; len *= 2; } if (dev->encoding & PE_16BIT) len *= 2; hg = GlobalAlloc(GMEM_MOVEABLE, len); if (!hg) { fprintf(stderr, "GlobalAlloc failed!"); return; } b = GlobalLock(hg); if (!(dev->encoding & PE_16BIT)) /* Convert to 8-bit unsigned. */ conv8bit(buf, count); #ifdef __MINGW32__ memcpy(b, buf, len); #else CopyMemory(b, buf, len); #endif if (play(dev, b, len)) { GlobalUnlock(hg); GlobalFree(hg); } } close_output(play_audio_t * dev) { audio_wait(dev); waveOutClose(dev->dev); } static void audio_flush(play_audio_t * dev) { audio_wait(dev); } static void audio_purge(play_audio_t * dev) { waveOutReset(dev->dev); audio_wait(dev); } void audio_term(play_audio_t * dev) { close_output(dev); } void audio_DESTROY(play_audio_t *dev) { close_output(dev); DeleteCriticalSection(&dev->critSect); } IV audio_rate(play_audio_t *dev, IV new) { return dev->rate; } float audio_gain(play_audio_t *dev, float new) { return 1.0; } /* API level Play function - volume may go from the interface - it is un-natural - convert to 'short' should be done at Audio::Play level - likewise rate-matching needs to be higher level */ void audio_play(play_audio_t *dev, Audio *au, float volume) { STRLEN samp = Audio_samples(au); SV *tmp = Audio_shorts(au); if (volume >= 0) audio_gain(dev, volume); if (au->rate != audio_rate(dev,0)) audio_rate(dev, au->rate); /* Or re-sample to dev's rate ??? */ audio_play16(dev, samp, (short *) SvPVX(tmp)); SvREFCNT_dec(tmp); } IV audio_speaker(play_audio_t *dev,IV flag) { return flag; } IV audio_headphone(play_audio_t *dev,IV flag) { return flag; } MODULE = Audio::Play::#OSNAME# PACKAGE=Audio::Play::#OSNAME# PREFIX = audio_ PROTOTYPES: DISABLE void audio_new(class,wait = 1) char * class IV wait CODE: {static play_audio_t buf; play_audio_t *p; /* We cannot use the normal typemap scheme in ../Data/typemap as * the open process passes address of elements of the buffer to Win32 */ ST(0) = sv_newmortal(); sv_setref_pvn(ST(0), class, (char *) &buf, sizeof(buf)); p = (play_audio_t *)SvPVX(SvRV(ST(0))); if (!audio_open(p,wait)) { XSRETURN_NO; } } void audio_DESTROY(dev) play_audio_t * dev void audio_flush(dev) play_audio_t * dev IV audio_speaker(dev,flag = 0) play_audio_t * dev IV flag IV audio_headphone(dev,flag = 0) play_audio_t * dev IV flag float audio_gain(dev,val = -1.0) play_audio_t * dev float val IV audio_rate(dev,rate = 0) play_audio_t * dev IV rate void audio_play(dev, au, vol = -1.0) play_audio_t * dev Audio * au; float vol BOOT: { AudioVptr = (AudioVtab *) SvIV(perl_get_sv("Audio::Data::AudioVtab",5)); }