mirror of
https://github.com/DrBeef/QuestZDoom.git
synced 2025-03-05 17:10:55 +00:00
343 lines
10 KiB
C
343 lines
10 KiB
C
/* FluidSynth - A Software Synthesizer
|
|
*
|
|
* Copyright (C) 2003 Peter Hanappe and others.
|
|
*
|
|
* 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 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
|
|
* 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
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
* 02111-1307, USA
|
|
*/
|
|
|
|
|
|
/* fluid_sndmgr.c
|
|
*
|
|
* Driver for MacOS Classic
|
|
*/
|
|
|
|
#if SNDMAN_SUPPORT
|
|
|
|
#include "fluid_synth.h"
|
|
#include "fluid_adriver.h"
|
|
#include "fluid_settings.h"
|
|
|
|
#include <Sound.h>
|
|
|
|
typedef struct {
|
|
fluid_audio_driver_t driver;
|
|
SndDoubleBufferHeader2* doubleHeader;
|
|
SndDoubleBackUPP doubleCallbackProc;
|
|
SndChannelPtr channel;
|
|
int callback_is_audio_func;
|
|
void* data;
|
|
fluid_audio_func_t callback;
|
|
float* convbuffers[2];
|
|
int bufferByteSize;
|
|
int bufferFrameSize;
|
|
} fluid_sndmgr_audio_driver_t;
|
|
|
|
fluid_audio_driver_t* new_fluid_sndmgr_audio_driver(fluid_settings_t* settings,
|
|
fluid_synth_t* synth);
|
|
|
|
fluid_audio_driver_t* new_fluid_sndmgr_audio_driver2(fluid_settings_t* settings,
|
|
fluid_audio_func_t func,
|
|
void* data);
|
|
|
|
int delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t* p);
|
|
void pascal fluid_sndmgr_callback(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer);
|
|
Fixed fluid_sndmgr_double_to_fix(long double theLD);
|
|
|
|
/*
|
|
* generic new : returns error
|
|
*/
|
|
int
|
|
start_fluid_sndmgr_audio_driver(fluid_settings_t* settings,
|
|
fluid_sndmgr_audio_driver_t* dev,
|
|
int buffer_size)
|
|
{
|
|
int i;
|
|
SndDoubleBufferHeader2* doubleHeader = NULL;
|
|
SndDoubleBufferPtr doubleBuffer = NULL;
|
|
OSErr err;
|
|
SndChannelPtr channel = NULL;
|
|
double sample_rate;
|
|
|
|
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
|
|
|
|
dev->doubleCallbackProc = NewSndDoubleBackProc(fluid_sndmgr_callback);
|
|
|
|
/* the channel */
|
|
FLUID_LOG(FLUID_DBG, "FLUID-SndManager@2");
|
|
err = SndNewChannel(&channel, sampledSynth, initStereo, NULL);
|
|
if ((err != noErr) || (channel == NULL)) {
|
|
FLUID_LOG(FLUID_ERR, "Failed to allocate a sound channel (error %i)", err);
|
|
return err;
|
|
}
|
|
|
|
/* the double buffer struct */
|
|
FLUID_LOG(FLUID_DBG, "FLUID-SndManager@3");
|
|
doubleHeader = FLUID_NEW(SndDoubleBufferHeader2);
|
|
if (doubleHeader == NULL) {
|
|
FLUID_LOG(FLUID_PANIC, "Out of memory");
|
|
return -1;
|
|
}
|
|
doubleHeader->dbhBufferPtr[0] = NULL;
|
|
doubleHeader->dbhBufferPtr[1] = NULL;
|
|
doubleHeader->dbhNumChannels = 2;
|
|
doubleHeader->dbhSampleSize = 16;
|
|
doubleHeader->dbhCompressionID = 0;
|
|
doubleHeader->dbhPacketSize = 0;
|
|
doubleHeader->dbhSampleRate = fluid_sndmgr_double_to_fix((long double) sample_rate);
|
|
doubleHeader->dbhDoubleBack = dev->doubleCallbackProc;
|
|
doubleHeader->dbhFormat = 0;
|
|
|
|
/* prepare dev */
|
|
FLUID_LOG(FLUID_DBG, "FLUID-SndManager@4");
|
|
dev->doubleHeader = doubleHeader;
|
|
dev->channel = channel;
|
|
dev->bufferFrameSize = buffer_size;
|
|
dev->bufferByteSize = buffer_size * 2 * 2;
|
|
|
|
/* the 2 doublebuffers */
|
|
FLUID_LOG(FLUID_DBG, "FLUID-SndManager@5");
|
|
for (i = 0; i < 2; i++) {
|
|
doubleBuffer = (SndDoubleBufferPtr) FLUID_MALLOC(sizeof(SndDoubleBuffer)
|
|
+ dev->bufferByteSize);
|
|
if (doubleBuffer == NULL) {
|
|
FLUID_LOG(FLUID_PANIC, "Out of memory");
|
|
return -1;
|
|
}
|
|
doubleBuffer->dbNumFrames = 0;
|
|
doubleBuffer->dbFlags = 0;
|
|
doubleBuffer->dbUserInfo[0] = (long) dev;
|
|
doubleHeader->dbhBufferPtr[i] = doubleBuffer;
|
|
CallSndDoubleBackProc(doubleHeader->dbhDoubleBack, channel, doubleBuffer);
|
|
}
|
|
|
|
/* start */
|
|
FLUID_LOG(FLUID_DBG, "FLUID-SndManager@6");
|
|
|
|
err = SndPlayDoubleBuffer(channel, (SndDoubleBufferHeader *)doubleHeader);
|
|
if (err != noErr) {
|
|
FLUID_LOG(FLUID_ERR, "Failed to start the sound driver (error %i)", err);
|
|
return err;
|
|
}
|
|
|
|
FLUID_LOG(FLUID_DBG, "FLUID-SndManager@7");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* new_fluid_sndmgr_audio_driver
|
|
* This implementation used the 16bit format.
|
|
*/
|
|
fluid_audio_driver_t*
|
|
new_fluid_sndmgr_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth)
|
|
{
|
|
fluid_sndmgr_audio_driver_t* dev = NULL;
|
|
int period_size, periods, buffer_size;
|
|
|
|
/* check the format */
|
|
if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) {
|
|
FLUID_LOG(FLUID_ERR, "Unhandled sample format");
|
|
return NULL;
|
|
}
|
|
|
|
/* compute buffer size */
|
|
fluid_settings_getint(settings, "audio.period-size", &period_size);
|
|
fluid_settings_getint(settings, "audio.periods", &periods);
|
|
buffer_size = period_size*periods;
|
|
|
|
/* allocated dev */
|
|
dev = FLUID_NEW(fluid_sndmgr_audio_driver_t);
|
|
if (dev == NULL) {
|
|
FLUID_LOG(FLUID_PANIC, "Out of memory");
|
|
return NULL;
|
|
}
|
|
FLUID_MEMSET(dev, 0, sizeof(fluid_sndmgr_audio_driver_t));
|
|
|
|
dev->callback_is_audio_func = false;
|
|
dev->data = (void *)synth;
|
|
dev->callback = NULL;
|
|
|
|
if (start_fluid_sndmgr_audio_driver(settings, dev, buffer_size) != 0) {
|
|
delete_fluid_sndmgr_audio_driver((fluid_audio_driver_t*)dev);
|
|
return NULL;
|
|
}
|
|
return (fluid_audio_driver_t*)dev;
|
|
}
|
|
|
|
/*
|
|
* new_fluid_sndmgr_audio_driver2
|
|
*
|
|
* This implementation used the audio_func float format, with
|
|
* conversion from float to 16bits in the driver.
|
|
*/
|
|
fluid_audio_driver_t*
|
|
new_fluid_sndmgr_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data)
|
|
{
|
|
fluid_sndmgr_audio_driver_t* dev = NULL;
|
|
int period_size, periods, buffer_size;
|
|
|
|
/* compute buffer size */
|
|
fluid_settings_getint(settings, "audio.period-size", &period_size);
|
|
fluid_settings_getint(settings, "audio.periods", &periods);
|
|
buffer_size = period_size*periods;
|
|
|
|
/* allocated dev */
|
|
dev = FLUID_NEW(fluid_sndmgr_audio_driver_t);
|
|
if (dev == NULL) {
|
|
FLUID_LOG(FLUID_PANIC, "Out of memory");
|
|
return NULL;
|
|
}
|
|
FLUID_MEMSET(dev, 0, sizeof(fluid_sndmgr_audio_driver_t));
|
|
|
|
/* allocate the conversion buffers */
|
|
dev->convbuffers[0] = FLUID_ARRAY(float, buffer_size);
|
|
dev->convbuffers[1] = FLUID_ARRAY(float, buffer_size);
|
|
if ((dev->convbuffers[0] == NULL) || (dev->convbuffers[1] == NULL)) {
|
|
FLUID_LOG(FLUID_PANIC, "Out of memory");
|
|
goto error_recovery;
|
|
}
|
|
|
|
dev->callback_is_audio_func = true;
|
|
dev->data = data;
|
|
dev->callback = func;
|
|
|
|
if (start_fluid_sndmgr_audio_driver(settings, dev, buffer_size) != 0) {
|
|
goto error_recovery;
|
|
}
|
|
return (fluid_audio_driver_t*)dev;
|
|
|
|
error_recovery:
|
|
delete_fluid_sndmgr_audio_driver((fluid_audio_driver_t*)dev);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* delete_fluid_sndmgr_audio_driver
|
|
*/
|
|
int delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t* p)
|
|
{
|
|
fluid_sndmgr_audio_driver_t* dev = (fluid_sndmgr_audio_driver_t*) p;
|
|
|
|
if (dev != NULL) {
|
|
if (dev->channel != NULL) {
|
|
SndDisposeChannel(dev->channel, 1);
|
|
}
|
|
if (dev->doubleCallbackProc != NULL) {
|
|
DisposeRoutineDescriptor(dev->doubleCallbackProc);
|
|
}
|
|
if (dev->doubleHeader != NULL) {
|
|
if(dev->doubleHeader->dbhBufferPtr[0] != NULL) {
|
|
FLUID_FREE(dev->doubleHeader->dbhBufferPtr[0]);
|
|
}
|
|
if (dev->doubleHeader->dbhBufferPtr[1] != NULL) {
|
|
FLUID_FREE(dev->doubleHeader->dbhBufferPtr[1]);
|
|
}
|
|
FLUID_FREE(dev->doubleHeader);
|
|
}
|
|
if (dev->convbuffers[0] != NULL) {
|
|
FLUID_FREE(dev->convbuffers[0]);
|
|
}
|
|
if (dev->convbuffers[1] != NULL) {
|
|
FLUID_FREE(dev->convbuffers[1]);
|
|
}
|
|
|
|
FLUID_FREE(dev);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* fluid_sndmgr_callback
|
|
*
|
|
*/
|
|
void pascal fluid_sndmgr_callback(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer)
|
|
{
|
|
fluid_sndmgr_audio_driver_t* dev;
|
|
signed short* buf;
|
|
float* left;
|
|
float* right;
|
|
float v;
|
|
int i, k, buffer_size;
|
|
|
|
dev = (fluid_sndmgr_audio_driver_t*) doubleBuffer->dbUserInfo[0];
|
|
buf = (signed short*)doubleBuffer->dbSoundData;
|
|
buffer_size = dev->bufferFrameSize;
|
|
|
|
if (dev->callback_is_audio_func) {
|
|
/* float API : conversion to signed short */
|
|
left = dev->convbuffers[0];
|
|
right = dev->convbuffers[1];
|
|
|
|
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->convbuffers);
|
|
|
|
for (i = 0, k = 0; i < buffer_size; i++) {
|
|
v = 32767.0f * left[i];
|
|
fluid_clip(v, -32768.0f, 32767.0f);
|
|
buf[k++] = (signed short) v;
|
|
|
|
v = 32767.0f * right[i];
|
|
fluid_clip(v, -32768.0f, 32767.0f);
|
|
buf[k++] = (signed short) v;
|
|
}
|
|
|
|
} else {
|
|
/* let the synth do the convertion */
|
|
fluid_synth_write_s16((fluid_synth_t*)dev->data, buffer_size, buf, 0, 2, buf, 1, 2);
|
|
}
|
|
|
|
doubleBuffer->dbFlags = doubleBuffer->dbFlags | dbBufferReady;
|
|
doubleBuffer->dbNumFrames = buffer_size;
|
|
}
|
|
|
|
/*
|
|
* fluid_sndmgr_double_to_fix
|
|
*
|
|
* A Fixed number is of the type 12345.67890. It is 32 bits in size with the
|
|
* high order bits representing the significant value (that before the point)
|
|
* and the lower 16 bits representing the fractional part of the number.
|
|
* The Sound Manager further complicates matters by using Fixed numbers, but
|
|
* needing to represent numbers larger than what the Fixed is capable of.
|
|
* To do this the Sound Manager treats the sign bit as having the value 32768
|
|
* which will cause any number greater or equal to 32768 to look like it is
|
|
* negative.
|
|
* This routine is designed to "do the right thing" and convert any long double
|
|
* into the Fixed number it represents.
|
|
* long double is the input type because AIFF files use extended80 numbers and
|
|
* there are routines that will convert from an extended80 to a long double.
|
|
* A long double has far greater precision than a Fixed, so any number whose
|
|
* significant or fraction is larger than 65535 will not convert correctly.
|
|
*/
|
|
#define _MAX_VALUE 65535
|
|
#define _BITS_PER_BYTE 8
|
|
Fixed fluid_sndmgr_double_to_fix(long double theLD) {
|
|
unsigned long theResult = 0;
|
|
unsigned short theSignificant = 0, theFraction = 0;
|
|
|
|
if (theLD < _MAX_VALUE) {
|
|
theSignificant = theLD;
|
|
theFraction = theLD - theSignificant;
|
|
if (theFraction > _MAX_VALUE) {
|
|
/* Won't be able to convert */
|
|
theSignificant = 0;
|
|
theFraction = 0;
|
|
}
|
|
}
|
|
theResult |= theSignificant;
|
|
theResult = theResult << (sizeof (unsigned short) * _BITS_PER_BYTE);
|
|
theResult |= theFraction;
|
|
return theResult;
|
|
}
|
|
|
|
#endif
|