questzdoom/Projects/Android/jni/SupportLibs/fluidsynth/fluid_sndmgr.c
2021-04-20 21:09:02 +01:00

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