Files
Konrad Beckmann 20d7fefaf9 Import mikmod
2021-08-02 02:19:41 +02:00

259 lines
6.4 KiB
C

/* MikMod sound library
(c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
complete list.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library 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 Library General Public License for more details.
You should have received a copy of the GNU Library 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.
*/
/*==============================================================================
$Id$
Driver for output on win32 platforms using the multimedia API
==============================================================================*/
/*
Written by Bjornar Henden <bhenden@online.no>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "mikmod_internals.h"
#ifdef DRV_WIN
#include <windows.h>
#if defined(_MSC_VER)
#pragma comment(lib,"winmm.lib")
#if (_MSC_VER < 1300)
typedef DWORD DWORD_PTR;
#endif
#endif
/* PF_XMMI64_INSTRUCTIONS_AVAILABLE not in all SDKs. */
#ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE
#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
#endif
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
#define MINBUFFERS 2 /* min number of buffers */
#define MAXBUFFERS 16 /* max number of buffers */
#define MINBUFFERSIZE_MS 50 /* min buffer size in milliseconds */
#define MAXBUFFERSIZE_MS 1000 /* max buffer size in milliseconds */
#define DEFAULT_NUMBUFFERS 2
#define DEFAULT_BUFFERSIZE_MS 120
static HWAVEOUT hwaveout;
static WAVEHDR header[MAXBUFFERS];
static HPSTR buffer[MAXBUFFERS]; /* pointers to buffers */
static WORD buffersout; /* number of buffers playing/about to be played */
static WORD nextbuffer; /* next buffer to be mixed */
static ULONG buffersize; /* buffer size in bytes */
static ULONG buffersize_ms = DEFAULT_BUFFERSIZE_MS; /* configured buffer size in milliseconds */
static ULONG numbuffers = DEFAULT_NUMBUFFERS; /* configured number of buffers */
static void WIN_CommandLine(const CHAR *cmdline)
{
CHAR *ptr;
if((ptr = MD_GetAtom("buffer",cmdline,0)) != NULL) {
buffersize_ms = atoi(ptr);
if(buffersize_ms < MINBUFFERSIZE_MS || buffersize_ms > MAXBUFFERSIZE_MS)
buffersize_ms = DEFAULT_BUFFERSIZE_MS;
MikMod_free(ptr);
}
if((ptr = MD_GetAtom("count",cmdline,0)) != NULL) {
numbuffers = atoi(ptr);
if(numbuffers < MINBUFFERS || numbuffers > MAXBUFFERS)
numbuffers = DEFAULT_NUMBUFFERS;
MikMod_free(ptr);
}
}
/* converts Windows error to libmikmod error */
static int WIN_GetError(MMRESULT mmr)
{
switch (mmr) {
case MMSYSERR_INVALHANDLE:
return MMERR_WINMM_HANDLE;
case MMSYSERR_NODRIVER:
return MMERR_OPENING_AUDIO;
case MMSYSERR_NOMEM:
return MMERR_OUT_OF_MEMORY;
case MMSYSERR_ALLOCATED:
return MMERR_WINMM_ALLOCATED;
case MMSYSERR_BADDEVICEID:
return MMERR_WINMM_DEVICEID;
case WAVERR_BADFORMAT:
return MMERR_WINMM_FORMAT;
default:
return MMERR_WINMM_UNKNOWN; /* should hopefully not happen */
}
}
static BOOL WIN_IsThere(void)
{
return waveOutGetNumDevs()>0?1:0;
}
static void CALLBACK WIN_CallBack(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
{
if (uMsg==WOM_DONE) --buffersout;
}
static int WIN_Init(void)
{
WAVEFORMATEX wfe;
WORD samplesize;
MMRESULT mmr;
int n;
samplesize=1;
if (md_mode&DMODE_STEREO) samplesize<<=1;
if (md_mode&DMODE_FLOAT) samplesize<<=2;
else if (md_mode&DMODE_16BITS) samplesize<<=1;
wfe.wFormatTag=(md_mode&DMODE_FLOAT)? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
wfe.nChannels=md_mode&DMODE_STEREO?2:1;
wfe.nSamplesPerSec=md_mixfreq;
wfe.nAvgBytesPerSec=md_mixfreq*samplesize;
wfe.nBlockAlign=samplesize;
wfe.wBitsPerSample=(md_mode&DMODE_FLOAT)?32:(md_mode&DMODE_16BITS)?16:8;
wfe.cbSize=sizeof(wfe);
mmr=waveOutOpen(&hwaveout,WAVE_MAPPER,&wfe,(DWORD_PTR)WIN_CallBack,0,CALLBACK_FUNCTION);
if (mmr!=MMSYSERR_NOERROR) {
_mm_errno=WIN_GetError(mmr);
return 1;
}
buffersize=md_mixfreq*samplesize*buffersize_ms/1000;
for (n=0;n<numbuffers;n++) {
buffer[n]=(HPSTR)MikMod_malloc(buffersize);
header[n].lpData=buffer[n];
header[n].dwBufferLength=buffersize;
mmr=waveOutPrepareHeader(hwaveout,&header[n],sizeof(WAVEHDR));
if (!buffer[n]||mmr!=MMSYSERR_NOERROR) {
if (!buffer[n])
_mm_errno=MMERR_OUT_OF_MEMORY;
else
_mm_errno=WIN_GetError(mmr);
return 1;
}
}
md_mode|=DMODE_SOFT_MUSIC|DMODE_SOFT_SNDFX;
#if defined HAVE_SSE2
/* This test only works on Windows XP or later */
if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
md_mode|=DMODE_SIMDMIXER;
}
#endif
buffersout=nextbuffer=0;
return VC_Init();
}
static void WIN_Exit(void)
{
int n;
VC_Exit();
if (hwaveout) {
for (n=0;n<numbuffers;n++) {
if (header[n].dwFlags&WHDR_PREPARED)
waveOutUnprepareHeader(hwaveout,&header[n],sizeof(WAVEHDR));
MikMod_free(buffer[n]);
buffer[n] = NULL;
}
while (waveOutClose(hwaveout)==WAVERR_STILLPLAYING) Sleep(10);
hwaveout=NULL;
}
}
static void WIN_Update(void)
{
ULONG done;
while (buffersout<numbuffers) {
done=VC_WriteBytes((SBYTE*)buffer[nextbuffer],buffersize);
if (!done) break;
header[nextbuffer].dwBufferLength=done;
waveOutWrite(hwaveout,&header[nextbuffer],sizeof(WAVEHDR));
if (++nextbuffer>=numbuffers) nextbuffer%=numbuffers;
++buffersout;
}
}
static void WIN_PlayStop(void)
{
if (hwaveout) waveOutReset(hwaveout);
VC_PlayStop();
}
MIKMODAPI MDRIVER drv_win={
NULL,
"Windows waveform-audio",
"Windows waveform-audio driver v0.2",
0,255,
"winmm",
"buffer:r:50,1000,120:Audio buffer size in milliseconds\n"
"count:r:2,16,2:Audio buffer count\n",
WIN_CommandLine,
WIN_IsThere,
VC_SampleLoad,
VC_SampleUnload,
VC_SampleSpace,
VC_SampleLength,
WIN_Init,
WIN_Exit,
NULL,
VC_SetNumVoices,
VC_PlayStart,
WIN_PlayStop,
WIN_Update,
NULL,
VC_VoiceSetVolume,
VC_VoiceGetVolume,
VC_VoiceSetFrequency,
VC_VoiceGetFrequency,
VC_VoiceSetPanning,
VC_VoiceGetPanning,
VC_VoicePlay,
VC_VoiceStop,
VC_VoiceStopped,
VC_VoiceGetPosition,
VC_VoiceRealVolume
};
#else
MISSING(drv_win);
#endif
/* ex:set ts=4: */