/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
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 <windows.h>
#ifdef __MINGW32__
#include "../config/mmsystem.h"
#endif
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#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));
}