mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-27 06:22:06 +00:00
Add multi channels support for audio driver. (#667)
This PR addresses #665. 1) Add new functions for multi channels support: `fluid_synth_write_float_channels()`, `fluid_synth_write_s16_channels()` 2) `dsound` and `waveout` driver make use of this support. tested on 2 audio devices: - creative SB Live! (6 channels). - Realtek: ALC889A (8 channels).
This commit is contained in:
parent
57af8803f2
commit
f94cee0a50
4 changed files with 619 additions and 101 deletions
|
@ -19,10 +19,12 @@
|
|||
*/
|
||||
|
||||
|
||||
|
||||
#include "fluid_synth.h"
|
||||
#include "fluid_adriver.h"
|
||||
#include "fluid_settings.h"
|
||||
|
||||
|
||||
#if DSOUND_SUPPORT
|
||||
|
||||
#define INITGUID
|
||||
|
@ -37,21 +39,60 @@ static DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter);
|
|||
|
||||
static char *fluid_win32_error(HRESULT hr);
|
||||
|
||||
/**
|
||||
* The driver handle multiple channels.
|
||||
* Actually the number maximum of channels is limited to 2 * DSOUND_MAX_STEREO_CHANNELS.
|
||||
* The only reason of this limitation is because we dont know how to define the mapping
|
||||
* of speakers for stereo output number above DSOUND_MAX_STEREO_CHANNELS.
|
||||
*/
|
||||
/* Maximum number of stereo outputs */
|
||||
#define DSOUND_MAX_STEREO_CHANNELS 4
|
||||
/* speakers mapping */
|
||||
const static DWORD channel_mask_speakers[DSOUND_MAX_STEREO_CHANNELS] =
|
||||
{
|
||||
/* 1 stereo output */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
|
||||
},
|
||||
/* 2 stereo outputs */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
|
||||
},
|
||||
/* 3 stereo outputs */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
|
||||
},
|
||||
/* 4 stereo outputs */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
|
||||
SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fluid_audio_driver_t driver;
|
||||
LPDIRECTSOUND direct_sound;
|
||||
LPDIRECTSOUNDBUFFER prim_buffer;
|
||||
LPDIRECTSOUNDBUFFER sec_buffer;
|
||||
HANDLE thread;
|
||||
LPDIRECTSOUND direct_sound; /* dsound instance */
|
||||
LPDIRECTSOUNDBUFFER prim_buffer; /* dsound buffer*/
|
||||
LPDIRECTSOUNDBUFFER sec_buffer; /* dsound buffer */
|
||||
|
||||
HANDLE thread; /* driver task */
|
||||
DWORD threadID;
|
||||
fluid_synth_t *synth;
|
||||
fluid_audio_callback_t write;
|
||||
HANDLE quit_ev;
|
||||
int bytes_per_second;
|
||||
DWORD buffer_byte_size;
|
||||
DWORD queue_byte_size;
|
||||
DWORD frame_size;
|
||||
fluid_synth_t *synth; /* fluidsynth instance */
|
||||
/* callback called by the task for audio rendering in dsound buffers */
|
||||
fluid_audio_channels_callback_t write;
|
||||
HANDLE quit_ev; /* Event object to request the audio task to stop */
|
||||
|
||||
int bytes_per_second; /* number of bytes per second */
|
||||
DWORD buffer_byte_size; /* size of one buffer in bytes */
|
||||
DWORD queue_byte_size; /* total size of all buffers in bytes */
|
||||
DWORD frame_size; /* frame size in bytes */
|
||||
int channels_count; /* number of channels in audio stream */
|
||||
} fluid_dsound_audio_driver_t;
|
||||
|
||||
typedef struct
|
||||
|
@ -60,6 +101,7 @@ typedef struct
|
|||
char *devname;
|
||||
} fluid_dsound_devsel_t;
|
||||
|
||||
/* enumeration callback to add "device name" option on setting "audio.dsound.device" */
|
||||
BOOL CALLBACK
|
||||
fluid_dsound_enum_callback(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
|
||||
{
|
||||
|
@ -69,6 +111,11 @@ fluid_dsound_enum_callback(LPGUID guid, LPCTSTR description, LPCTSTR module, LPV
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/* enumeration callback to look if a certain device exists and return its GUID.
|
||||
@context, (fluid_dsound_devsel_t *) context->devname provide the device name to look for.
|
||||
(fluid_dsound_devsel_t *) context->devGUID pointer to return device GUID.
|
||||
@return TRUE to continue enumeration, FALSE otherwise.
|
||||
*/
|
||||
BOOL CALLBACK
|
||||
fluid_dsound_enum_callback2(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
|
||||
{
|
||||
|
@ -77,18 +124,25 @@ fluid_dsound_enum_callback2(LPGUID guid, LPCTSTR description, LPCTSTR module, LP
|
|||
|
||||
if(FLUID_STRCASECMP(devsel->devname, description) == 0)
|
||||
{
|
||||
/* The device exists, return a copy of its GUID */
|
||||
devsel->devGUID = FLUID_NEW(GUID);
|
||||
|
||||
if(devsel->devGUID)
|
||||
{
|
||||
/* return GUID */
|
||||
memcpy(devsel->devGUID, guid, sizeof(GUID));
|
||||
FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %p", devsel->devGUID);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
- register setting "audio.dsound.device".
|
||||
- add list of dsound device name as option of "audio.dsound.device" setting.
|
||||
*/
|
||||
void fluid_dsound_audio_driver_settings(fluid_settings_t *settings)
|
||||
{
|
||||
fluid_settings_register_str(settings, "audio.dsound.device", "default", 0);
|
||||
|
@ -99,7 +153,27 @@ void fluid_dsound_audio_driver_settings(fluid_settings_t *settings)
|
|||
|
||||
/*
|
||||
* new_fluid_dsound_audio_driver
|
||||
*/
|
||||
* The driver handle the case of multiple stereo buffers provided by fluidsynth
|
||||
* mixer.
|
||||
* Each stereo buffers (left, right) are written to respective channels pair
|
||||
* of the audio device card.
|
||||
* For example, if the number of internal mixer buffer is 2, the audio device
|
||||
* must have at least 4 channels:
|
||||
* - buffer 0 (left, right) will be written to channel pair (0, 1).
|
||||
* - buffer 1 (left, right) will be written to channel pair (2, 3).
|
||||
*
|
||||
* @param setting. The settings the driver looks for:
|
||||
* "synth.sample-rate", the sample rate.
|
||||
* "audio.periods", the number of buffers and
|
||||
* "audio.period-size", the size of each buffer.
|
||||
* "audio.sample-format",the sample format, 16bits or float.
|
||||
|
||||
* @param synth, fluidsynth synth instance to associate to the driver.
|
||||
*
|
||||
* Note: The number of internal mixer buffer is indicated by synth->audio_channels.
|
||||
* If the audio device cannot handle the format or do not have enough channels,
|
||||
* the driver fails and return NULL.
|
||||
*/
|
||||
fluid_audio_driver_t *
|
||||
new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
||||
{
|
||||
|
@ -112,7 +186,7 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
double sample_rate;
|
||||
int periods, period_size;
|
||||
fluid_dsound_devsel_t devsel;
|
||||
WAVEFORMATEX format;
|
||||
WAVEFORMATEXTENSIBLE format;
|
||||
|
||||
/* create and clear the driver data */
|
||||
dev = FLUID_NEW(fluid_dsound_audio_driver_t);
|
||||
|
@ -130,27 +204,29 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
fluid_settings_getint(settings, "audio.periods", &periods);
|
||||
fluid_settings_getint(settings, "audio.period-size", &period_size);
|
||||
|
||||
/* Clear the buffer format */
|
||||
ZeroMemory(&format, sizeof(WAVEFORMATEX));
|
||||
/* Clear format structure*/
|
||||
ZeroMemory(&format, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
|
||||
/* check the format */
|
||||
if(fluid_settings_str_equal(settings, "audio.sample-format", "float"))
|
||||
{
|
||||
GUID guid_float = {DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_IEEE_FLOAT)};
|
||||
FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format");
|
||||
|
||||
dev->frame_size = 2 * sizeof(float);
|
||||
dev->write = fluid_synth_write_float;
|
||||
|
||||
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
dev->write = fluid_synth_write_float_channels;
|
||||
/* sample container size in bits: 32 bits */
|
||||
format.Format.wBitsPerSample = 8 * sizeof(float);
|
||||
format.SubFormat = guid_float;
|
||||
format.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
}
|
||||
else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
|
||||
{
|
||||
GUID guid_pcm = {DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)};
|
||||
FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format");
|
||||
|
||||
dev->frame_size = 2 * sizeof(short);
|
||||
dev->write = fluid_synth_write_s16;
|
||||
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
dev->write = fluid_synth_write_s16_channels;
|
||||
/* sample container size in bits: 16bits */
|
||||
format.Format.wBitsPerSample = 8 * sizeof(short);
|
||||
format.SubFormat = guid_pcm;
|
||||
format.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -158,16 +234,41 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
goto error_recovery;
|
||||
}
|
||||
|
||||
/* Finish to initialize the format structure */
|
||||
/* number of channels in a frame */
|
||||
format.Format.nChannels = synth->audio_channels * 2;
|
||||
|
||||
if(synth->audio_groups > DSOUND_MAX_STEREO_CHANNELS)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Channels number %d exceed internal limit %d",
|
||||
format.Format.nChannels, DSOUND_MAX_STEREO_CHANNELS * 2);
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
/* size of frame in bytes */
|
||||
format.Format.nBlockAlign = format.Format.nChannels * format.Format.wBitsPerSample / 8;
|
||||
format.Format.nSamplesPerSec = (DWORD) sample_rate;
|
||||
format.Format.nAvgBytesPerSec = format.Format.nBlockAlign * format.Format.nSamplesPerSec;
|
||||
|
||||
/* extension */
|
||||
if(format.Format.nChannels > 2)
|
||||
{
|
||||
format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
format.Format.cbSize = 22;
|
||||
format.Samples.wValidBitsPerSample = format.Format.wBitsPerSample;
|
||||
|
||||
/* CreateSoundBuffer accepts only format.dwChannelMask compatible with
|
||||
format.Format.nChannels
|
||||
*/
|
||||
format.dwChannelMask = channel_mask_speakers[synth->audio_groups - 1];
|
||||
}
|
||||
|
||||
/* Finish to initialize dev structure */
|
||||
dev->frame_size = format.Format.nBlockAlign;
|
||||
dev->buffer_byte_size = period_size * dev->frame_size;
|
||||
dev->queue_byte_size = periods * dev->buffer_byte_size;
|
||||
dev->bytes_per_second = sample_rate * dev->frame_size;
|
||||
|
||||
/* Finish to initialize the buffer format */
|
||||
format.nChannels = 2;
|
||||
format.wBitsPerSample = dev->frame_size * 4;
|
||||
format.nSamplesPerSec = (DWORD) sample_rate;
|
||||
format.nBlockAlign = (WORD) dev->frame_size;
|
||||
format.nAvgBytesPerSec = dev->bytes_per_second;
|
||||
dev->bytes_per_second = format.Format.nAvgBytesPerSec;
|
||||
dev->channels_count = format.Format.nChannels;
|
||||
|
||||
devsel.devGUID = NULL;
|
||||
|
||||
|
@ -187,6 +288,11 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
/* open DirectSound */
|
||||
hr = DirectSoundCreate(devsel.devGUID, &dev->direct_sound, NULL);
|
||||
|
||||
if(devsel.devGUID)
|
||||
{
|
||||
FLUID_FREE(devsel.devGUID); /* -- free device GUID */
|
||||
}
|
||||
|
||||
if(hr != DS_OK)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Failed to create the DirectSound object");
|
||||
|
@ -231,7 +337,7 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
|
||||
/* set the primary sound buffer to this format. if it fails, just
|
||||
print a warning. */
|
||||
hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, &format);
|
||||
hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, (WAVEFORMATEX *)&format);
|
||||
|
||||
if(hr != DS_OK)
|
||||
{
|
||||
|
@ -243,7 +349,11 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
ZeroMemory(&desc, sizeof(DSBUFFERDESC));
|
||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||
desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||
desc.lpwfxFormat = &format;
|
||||
|
||||
/* CreateSoundBuffer accepts only format.dwChannelMask compatible with
|
||||
format.Format.nChannels
|
||||
*/
|
||||
desc.lpwfxFormat = (WAVEFORMATEX *)&format;
|
||||
desc.dwBufferBytes = dev->queue_byte_size;
|
||||
|
||||
if(caps.dwFreeHwMixingStreamingBuffers > 0)
|
||||
|
@ -262,7 +372,7 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
}
|
||||
|
||||
|
||||
/* Lock */
|
||||
/* Lock and get dsound buffer */
|
||||
hr = IDirectSoundBuffer_Lock(dev->sec_buffer, 0, 0, (void *) &buf1, &bytes1, 0, 0, DSBLOCK_ENTIREBUFFER);
|
||||
|
||||
if((hr != DS_OK) || (buf1 == NULL))
|
||||
|
@ -274,7 +384,7 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
/* fill the buffer with silence */
|
||||
memset(buf1, 0, bytes1);
|
||||
|
||||
/* Unlock */
|
||||
/* Unlock dsound buffer */
|
||||
IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0);
|
||||
|
||||
/* Create object to signal thread exit */
|
||||
|
@ -369,6 +479,37 @@ static DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
|
|||
HRESULT res;
|
||||
int ms;
|
||||
|
||||
/* pointers table on output first sample channels */
|
||||
void *channels_out[DSOUND_MAX_STEREO_CHANNELS * 2];
|
||||
int channels_off[DSOUND_MAX_STEREO_CHANNELS * 2];
|
||||
int channels_incr[DSOUND_MAX_STEREO_CHANNELS * 2];
|
||||
int i;
|
||||
|
||||
/* initialize write callback constant parameters:
|
||||
dsound expects interleaved channels in a unique buffer.
|
||||
For example 4 channels (c1, c2, c3, c4) and n samples:
|
||||
{ s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,...
|
||||
sn:c1, sn:c2, sn:c3, sn:c4 }.
|
||||
|
||||
So, channels_off[], channnel_incr[] tables should initialized like this:
|
||||
channels_off[0] = 0 channels_incr[0] = 4
|
||||
channels_off[1] = 1 channels_incr[1] = 4
|
||||
channels_off[2] = 2 channels_incr[2] = 4
|
||||
channels_off[3] = 3 channels_incr[3] = 4
|
||||
|
||||
channels_out[], table will be initialized later, just before calling
|
||||
the write callback function.
|
||||
channels_out[0] = address of dsound buffer
|
||||
channels_out[1] = address of dsound buffer
|
||||
channels_out[2] = address of dsound buffer
|
||||
channels_out[3] = address of dsound buffer
|
||||
*/
|
||||
for(i = 0; i < dev->channels_count; i++)
|
||||
{
|
||||
channels_off[i] = i;
|
||||
channels_incr[i] = dev->channels_count;
|
||||
}
|
||||
|
||||
cur_position = 0;
|
||||
|
||||
/* boost the priority of the audio thread */
|
||||
|
@ -409,7 +550,23 @@ static DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
|
|||
if(bytes1 > 0)
|
||||
{
|
||||
frames = bytes1 / dev->frame_size;
|
||||
dev->write(dev->synth, frames, buf1, 0, 2, buf1, 1, 2);
|
||||
/* Before calling write function, finish to initialize
|
||||
channels_out[] table parameter:
|
||||
dsound expects interleaved channels in a unique buffer.
|
||||
So, channels_out[] table must be initialized with the address
|
||||
of the same buffer (buf1).
|
||||
*/
|
||||
i = dev->channels_count;
|
||||
|
||||
do
|
||||
{
|
||||
channels_out[--i] = buf1;
|
||||
}
|
||||
while(i);
|
||||
|
||||
/* calling write function */
|
||||
dev->write(dev->synth, frames, dev->channels_count,
|
||||
channels_out, channels_off, channels_incr);
|
||||
cur_position += frames * dev->frame_size;
|
||||
}
|
||||
|
||||
|
@ -417,7 +574,23 @@ static DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
|
|||
if((buf2 != NULL) && (bytes2 > 0))
|
||||
{
|
||||
frames = bytes2 / dev->frame_size;
|
||||
dev->write(dev->synth, frames, buf2, 0, 2, buf2, 1, 2);
|
||||
/* Before calling write function, finish to initialize
|
||||
channels_out[] table parameter:
|
||||
dsound expects interleaved channels in a unique buffer.
|
||||
So, channels_out[] table must be initialized with the address
|
||||
of the same buffer (buf2).
|
||||
*/
|
||||
i = dev->channels_count;
|
||||
|
||||
do
|
||||
{
|
||||
channels_out[--i] = buf2;
|
||||
}
|
||||
while(i);
|
||||
|
||||
/* calling write function */
|
||||
dev->write(dev->synth, frames, dev->channels_count,
|
||||
channels_out, channels_off, channels_incr);
|
||||
cur_position += frames * dev->frame_size;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,12 +36,48 @@
|
|||
/* Milliseconds of a single sound buffer */
|
||||
#define MS_BUFFER_LENGTH 20
|
||||
|
||||
/**
|
||||
* The driver handle multiple channels.
|
||||
* Actually the number maximum of channels is limited to 2 * WAVEOUT_MAX_STEREO_CHANNELS.
|
||||
* The only reason of this limitation is because we dont know how to define the mapping
|
||||
* of speakers for stereo output number above WAVEOUT_MAX_STEREO_CHANNELS.
|
||||
*/
|
||||
/* Maximum number of stereo outputs */
|
||||
#define WAVEOUT_MAX_STEREO_CHANNELS 4
|
||||
|
||||
/* speakers mapping */
|
||||
const static DWORD channel_mask_speakers[WAVEOUT_MAX_STEREO_CHANNELS] =
|
||||
{
|
||||
/* 1 stereo output */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
|
||||
},
|
||||
/* 2 stereo outputs */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
|
||||
},
|
||||
/* 3 stereo outputs */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
|
||||
},
|
||||
/* 4 stereo outputs */
|
||||
{
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
|
||||
SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fluid_audio_driver_t driver;
|
||||
|
||||
fluid_synth_t *synth;
|
||||
fluid_audio_callback_t write_ptr;
|
||||
fluid_audio_channels_callback_t write_ptr;
|
||||
|
||||
HWAVEOUT hWaveOut;
|
||||
WAVEHDR waveHeader[NB_SOUND_BUFFERS];
|
||||
|
@ -54,6 +90,7 @@ typedef struct
|
|||
|
||||
int nQuit;
|
||||
HANDLE hQuit;
|
||||
int channels_count; /* number of channels in audio stream */
|
||||
|
||||
} fluid_waveout_audio_driver_t;
|
||||
|
||||
|
@ -66,6 +103,38 @@ static DWORD WINAPI fluid_waveout_synth_thread(void *data)
|
|||
|
||||
MSG msg;
|
||||
int code;
|
||||
/* pointers table on output first sample channels */
|
||||
void *channels_out[WAVEOUT_MAX_STEREO_CHANNELS * 2];
|
||||
int channels_off[WAVEOUT_MAX_STEREO_CHANNELS * 2];
|
||||
int channels_incr[WAVEOUT_MAX_STEREO_CHANNELS * 2];
|
||||
int i;
|
||||
|
||||
dev = (fluid_waveout_audio_driver_t *)data;
|
||||
|
||||
/* initialize write callback constant parameters:
|
||||
MME expects interleaved channels in a unique buffer.
|
||||
For example 4 channels (c1, c2, c3, c4) and n samples:
|
||||
{ s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,...
|
||||
sn:c1, sn:c2, sn:c3, sn:c4 }.
|
||||
|
||||
So, channels_off[], channnel_incr[] tables should initialized like this:
|
||||
channels_off[0] = 0 channels_incr[0] = 4
|
||||
channels_off[1] = 1 channels_incr[1] = 4
|
||||
channels_off[2] = 2 channels_incr[2] = 4
|
||||
channels_off[3] = 3 channels_incr[3] = 4
|
||||
|
||||
channels_out[], table will be initialized later, just before calling
|
||||
the write callback function.
|
||||
channels_out[0] = address of dsound buffer
|
||||
channels_out[1] = address of dsound buffer
|
||||
channels_out[2] = address of dsound buffer
|
||||
channels_out[3] = address of dsound buffer
|
||||
*/
|
||||
for(i = 0; i < dev->channels_count; i++)
|
||||
{
|
||||
channels_off[i] = i;
|
||||
channels_incr[i] = dev->channels_count;
|
||||
}
|
||||
|
||||
/* Forces creation of message queue */
|
||||
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
|
||||
|
@ -103,7 +172,22 @@ static DWORD WINAPI fluid_waveout_synth_thread(void *data)
|
|||
}
|
||||
else
|
||||
{
|
||||
dev->write_ptr(dev->synth, dev->num_frames, pWave->lpData, 0, 2, pWave->lpData, 1, 2);
|
||||
/* Before calling write function, finish to initialize
|
||||
channels_out[] table parameter:
|
||||
MME expects interleaved channels in a unique buffer.
|
||||
So, channels_out[] table must be initialized with the address
|
||||
of the same buffer (lpData).
|
||||
*/
|
||||
i = dev->channels_count;
|
||||
|
||||
do
|
||||
{
|
||||
channels_out[--i] = pWave->lpData;
|
||||
}
|
||||
while(i);
|
||||
|
||||
dev->write_ptr(dev->synth, dev->num_frames, dev->channels_count,
|
||||
channels_out, channels_off, channels_incr);
|
||||
|
||||
waveOutWrite((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR));
|
||||
}
|
||||
|
@ -149,48 +233,64 @@ void fluid_waveout_audio_driver_settings(fluid_settings_t *settings)
|
|||
|
||||
/*
|
||||
* new_fluid_waveout_audio_driver
|
||||
* The driver handle the case of multiple stereo buffers provided by fluidsynth
|
||||
* mixer.
|
||||
* Each stereo buffers (left, right) are written to respective channels pair
|
||||
* of the audio device card.
|
||||
* For example, if the number of internal mixer buffer is 2, the audio device
|
||||
* must have at least 4 channels:
|
||||
* - buffer 0 (left, right) will be written to channel pair (0, 1).
|
||||
* - buffer 1 (left, right) will be written to channel pair (2, 3).
|
||||
*
|
||||
* @param setting. The settings the driver looks for:
|
||||
* "synth.sample-rate", the sample rate.
|
||||
* "audio.sample-format",the sample format, 16bits or float.
|
||||
*
|
||||
* @param synth, fluidsynth synth instance to associate to the driver.
|
||||
*
|
||||
* Note: The number of internal mixer buffer is indicated by synth->audio_channels.
|
||||
* If the audio device cannot handle the format or do not have enough channels,
|
||||
* the driver fails and return NULL.
|
||||
*/
|
||||
fluid_audio_driver_t *
|
||||
new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
||||
{
|
||||
fluid_waveout_audio_driver_t *dev = NULL;
|
||||
fluid_audio_callback_t write_ptr;
|
||||
fluid_audio_channels_callback_t write_ptr;
|
||||
double sample_rate;
|
||||
int periods, period_size, frequency, sample_size;
|
||||
int frequency, sample_size;
|
||||
LPSTR ptrBuffer;
|
||||
int lenBuffer;
|
||||
int device;
|
||||
int i;
|
||||
WAVEFORMATEX wfx;
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
char dev_name[MAXPNAMELEN];
|
||||
MMRESULT errCode;
|
||||
|
||||
/* Retrieve the settings */
|
||||
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
|
||||
fluid_settings_getint(settings, "audio.periods", &periods);
|
||||
fluid_settings_getint(settings, "audio.period-size", &period_size);
|
||||
|
||||
/* Clear the format buffer */
|
||||
ZeroMemory(&wfx, sizeof(WAVEFORMATEX));
|
||||
/* Clear format structure */
|
||||
ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
|
||||
/* check the format */
|
||||
if(fluid_settings_str_equal(settings, "audio.sample-format", "float"))
|
||||
{
|
||||
GUID guid_float = {DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_IEEE_FLOAT)};
|
||||
FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format");
|
||||
|
||||
sample_size = sizeof(float);
|
||||
write_ptr = fluid_synth_write_float;
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
write_ptr = fluid_synth_write_float_channels;
|
||||
wfx.SubFormat = guid_float;
|
||||
}
|
||||
else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
|
||||
{
|
||||
GUID guid_pcm = {DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)};
|
||||
FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format");
|
||||
|
||||
sample_size = sizeof(short);
|
||||
write_ptr = fluid_synth_write_s16;
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
write_ptr = fluid_synth_write_s16_channels;
|
||||
wfx.SubFormat = guid_pcm;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -201,19 +301,30 @@ new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
/* Set frequency to integer */
|
||||
frequency = (int)sample_rate;
|
||||
|
||||
/* Compile the format buffer */
|
||||
wfx.nChannels = 2;
|
||||
wfx.wBitsPerSample = sample_size * 8;
|
||||
wfx.nSamplesPerSec = frequency;
|
||||
wfx.nBlockAlign = sample_size * wfx.nChannels;
|
||||
wfx.nAvgBytesPerSec = frequency * wfx.nBlockAlign;
|
||||
/* Initialize the format structure */
|
||||
wfx.Format.nChannels = synth->audio_channels * 2;
|
||||
|
||||
if(synth->audio_groups > WAVEOUT_MAX_STEREO_CHANNELS)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Channels number %d exceed internal limit %d",
|
||||
wfx.Format.nChannels, WAVEOUT_MAX_STEREO_CHANNELS * 2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wfx.Format.wBitsPerSample = sample_size * 8;
|
||||
wfx.Format.nBlockAlign = sample_size * wfx.Format.nChannels;
|
||||
wfx.Format.nSamplesPerSec = frequency;
|
||||
wfx.Format.nAvgBytesPerSec = frequency * wfx.Format.nBlockAlign;
|
||||
/* extension */
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.cbSize = 22;
|
||||
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||
wfx.dwChannelMask = channel_mask_speakers[synth->audio_groups - 1];
|
||||
|
||||
/* Calculate the length of a single buffer */
|
||||
lenBuffer = (MS_BUFFER_LENGTH * wfx.nAvgBytesPerSec + 999) / 1000;
|
||||
|
||||
lenBuffer = (MS_BUFFER_LENGTH * wfx.Format.nAvgBytesPerSec + 999) / 1000;
|
||||
/* Round to 8-bytes size */
|
||||
lenBuffer = (lenBuffer + 7) & ~7;
|
||||
|
||||
/* create and clear the driver data */
|
||||
dev = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
||||
sizeof(fluid_waveout_audio_driver_t) + lenBuffer * NB_SOUND_BUFFERS);
|
||||
|
@ -232,7 +343,8 @@ new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
dev->sample_size = sample_size;
|
||||
|
||||
/* Calculate the number of frames in a block */
|
||||
dev->num_frames = lenBuffer / wfx.nBlockAlign;
|
||||
dev->num_frames = lenBuffer / wfx.Format.nBlockAlign;
|
||||
dev->channels_count = wfx.Format.nChannels;
|
||||
|
||||
/* Set default device to use */
|
||||
device = WAVE_MAPPER;
|
||||
|
@ -302,7 +414,7 @@ new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
|||
|
||||
errCode = waveOutOpen(&dev->hWaveOut,
|
||||
device,
|
||||
&wfx,
|
||||
(WAVEFORMATEX *)&wfx,
|
||||
(DWORD_PTR)dev->dwThread,
|
||||
0,
|
||||
CALLBACK_THREAD);
|
||||
|
|
|
@ -3885,6 +3885,7 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],
|
|||
return FLUID_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synthesize a block of floating point audio samples to audio buffers.
|
||||
* @param synth FluidSynth instance
|
||||
|
@ -3908,43 +3909,121 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
|
|||
void *lout, int loff, int lincr,
|
||||
void *rout, int roff, int rincr)
|
||||
{
|
||||
return fluid_synth_write_float_LOCAL(synth, len, lout, loff, lincr, rout, roff, rincr, fluid_synth_render_blocks);
|
||||
void *channels_out[2] = {lout, rout};
|
||||
int channels_off[2] = {loff, roff };
|
||||
int channels_incr[2] = {lincr, rincr };
|
||||
|
||||
return fluid_synth_write_float_channels(synth, len, 2, channels_out,
|
||||
channels_off, channels_incr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a block of float audio samples channels to audio buffers.
|
||||
* The function is convenient for audio driver to render multiple stereo
|
||||
* channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels).
|
||||
*
|
||||
* @param synth FluidSynth instance.
|
||||
* @param len Count of audio frames to synthesize.
|
||||
* @param channels_count Count of channels in a frame.
|
||||
* must be multiple of 2 and channel_count/2 must not exceed the number
|
||||
* of internal mixer buffers (synth->audio_groups)
|
||||
* @param channels_out Array of channels_count pointers on 16 bit words to
|
||||
* store sample channels. Modified on return.
|
||||
* @param channels_off Array of channels_count offset index to add to respective pointer
|
||||
* in channels_out for first sample.
|
||||
* @param channels_incr Array of channels_count increment between consecutive
|
||||
* samples channels.
|
||||
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
|
||||
*
|
||||
* Useful for storing:
|
||||
* - interleaved channels in a unique buffer.
|
||||
* - non interleaved channels in an unique buffer (or in distinct buffers).
|
||||
*
|
||||
* Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn)
|
||||
* in a unique buffer:
|
||||
* { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,...
|
||||
* sn:c1, sn:c2, sn:c3, sn:c4 }.
|
||||
*
|
||||
* @note Should only be called from synthesis thread.
|
||||
* @note Reverb and Chorus are mixed to \c lout resp. \c rout.
|
||||
*/
|
||||
int
|
||||
fluid_synth_write_float_channels(fluid_synth_t *synth, int len,
|
||||
int channels_count,
|
||||
void *channels_out[], int channels_off[],
|
||||
int channels_incr[])
|
||||
{
|
||||
return fluid_synth_write_float_channels_LOCAL(synth, len, channels_count,
|
||||
channels_out, channels_off, channels_incr,
|
||||
fluid_synth_render_blocks);
|
||||
}
|
||||
|
||||
int
|
||||
fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,
|
||||
void *lout, int loff, int lincr,
|
||||
void *rout, int roff, int rincr,
|
||||
int (*block_render_func)(fluid_synth_t *, int)
|
||||
)
|
||||
fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len,
|
||||
int channels_count,
|
||||
void *channels_out[], int channels_off[],
|
||||
int channels_incr[],
|
||||
int (*block_render_func)(fluid_synth_t *, int))
|
||||
{
|
||||
int n, cur, size;
|
||||
float *left_out = (float *) lout + loff;
|
||||
float *right_out = (float *) rout + roff;
|
||||
float **chan_out = (float **)channels_out;
|
||||
int di, n, cur, size;
|
||||
|
||||
/* pointers on first input mixer buffer */
|
||||
fluid_real_t *left_in;
|
||||
fluid_real_t *right_in;
|
||||
int bufs_in_count; /* number of stereo input buffers */
|
||||
int i;
|
||||
|
||||
/* start average cpu load probe */
|
||||
double time = fluid_utime();
|
||||
float cpu_load;
|
||||
|
||||
/* start profiling duration probe (if profiling is enabled) */
|
||||
fluid_profile_ref_var(prof_ref);
|
||||
|
||||
/* check parameters */
|
||||
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
|
||||
|
||||
fluid_return_val_if_fail(len >= 0, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below
|
||||
|
||||
/* Conversely to fluid_synth_process() (which handle possible multiple stereo output),
|
||||
/* check for valid channel_count: must be multiple of 2 and
|
||||
channel_count/2 must not exceed the number of internal mixer buffers
|
||||
(synth->audio_groups)
|
||||
*/
|
||||
fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */
|
||||
&& channels_count >= 2, FLUID_FAILED);
|
||||
|
||||
bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */
|
||||
fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED);
|
||||
|
||||
fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED);
|
||||
|
||||
/* initialize output channels buffers on first sample position */
|
||||
i = channels_count;
|
||||
do
|
||||
{
|
||||
i--;
|
||||
chan_out[i] += channels_off[i];
|
||||
}
|
||||
while(i);
|
||||
|
||||
/* Conversely to fluid_synth_process(),
|
||||
we want rendered audio effect mixed in internal audio dry buffers.
|
||||
TRUE instructs the mixer that internal audio effects will be mixed in first internal
|
||||
TRUE instructs the mixer that internal audio effects will be mixed in internal
|
||||
audio dry buffers.
|
||||
*/
|
||||
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE);
|
||||
|
||||
/* get first internal mixer audio dry buffer's pointer (left and right channel) */
|
||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||
|
||||
size = len;
|
||||
cur = synth->cur;
|
||||
|
||||
/* synth->cur indicates if available samples are still in internal mixer buffer */
|
||||
cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */
|
||||
|
||||
do
|
||||
{
|
||||
|
@ -3952,8 +4031,11 @@ fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,
|
|||
if(cur >= synth->curmax)
|
||||
{
|
||||
/* render audio (dry and effect) to internal dry buffers */
|
||||
/* always render full blocs multiple of FLUID_BUFSIZE */
|
||||
int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
|
||||
synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft);
|
||||
|
||||
/* get first internal mixer audio dry buffer's pointer (left and right channel) */
|
||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||
cur = 0;
|
||||
}
|
||||
|
@ -3981,28 +4063,61 @@ fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,
|
|||
|
||||
do
|
||||
{
|
||||
*left_out = (float) left_in[n];
|
||||
*right_out = (float) right_in[n];
|
||||
i = bufs_in_count;
|
||||
do
|
||||
{
|
||||
/* input sample index in stereo buffer i */
|
||||
int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n;
|
||||
int c = i << 1; /* channel index c to write */
|
||||
|
||||
left_out += lincr;
|
||||
right_out += rincr;
|
||||
/* write left input sample to channel sample */
|
||||
*chan_out[c] = (float) left_in[in_idx];
|
||||
|
||||
/* write right input sample to next channel sample */
|
||||
*chan_out[c+1] = (float) right_in[in_idx];
|
||||
|
||||
/* advance output pointers */
|
||||
chan_out[c] += channels_incr[c];
|
||||
chan_out[c+1] += channels_incr[c+1];
|
||||
}
|
||||
while(i);
|
||||
}
|
||||
while(++n < 0);
|
||||
}
|
||||
while(size);
|
||||
|
||||
synth->cur = cur;
|
||||
synth->cur = cur; /* save current sample position. It will be used on next call */
|
||||
|
||||
/* save average cpu load, use by API for real time cpu load meter */
|
||||
time = fluid_utime() - time;
|
||||
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
|
||||
fluid_atomic_float_set(&synth->cpu_load, cpu_load);
|
||||
|
||||
/* stop duration probe and save performance measurement (if profiling is enabled) */
|
||||
fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
|
||||
fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
|
||||
len);
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
/* for testing purpose */
|
||||
int
|
||||
fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,
|
||||
void *lout, int loff, int lincr,
|
||||
void *rout, int roff, int rincr,
|
||||
int (*block_render_func)(fluid_synth_t *, int)
|
||||
)
|
||||
{
|
||||
void *channels_out[2] = {lout, rout};
|
||||
int channels_off[2] = {loff, roff };
|
||||
int channels_incr[2] = {lincr, rincr };
|
||||
|
||||
return fluid_synth_write_float_channels_LOCAL(synth, len, 2, channels_out,
|
||||
channels_off, channels_incr,
|
||||
block_render_func);
|
||||
}
|
||||
|
||||
|
||||
#define DITHER_SIZE 48000
|
||||
#define DITHER_CHANNELS 2
|
||||
|
||||
|
@ -4083,25 +4198,100 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
|
|||
void *lout, int loff, int lincr,
|
||||
void *rout, int roff, int rincr)
|
||||
{
|
||||
void *channels_out[2] = {lout, rout};
|
||||
int channels_off[2] = {loff, roff };
|
||||
int channels_incr[2] = {lincr, rincr };
|
||||
|
||||
return fluid_synth_write_s16_channels(synth, len, 2, channels_out,
|
||||
channels_off, channels_incr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a block of 16 bit audio samples channels to audio buffers.
|
||||
* The function is convenient for audio driver to render multiple stereo
|
||||
* channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels).
|
||||
*
|
||||
* @param synth FluidSynth instance.
|
||||
* @param len Count of audio frames to synthesize.
|
||||
* @param channels_count Count of channels in a frame.
|
||||
* must be multiple of 2 and channel_count/2 must not exceed the number
|
||||
* of internal mixer buffers (synth->audio_groups)
|
||||
* @param channels_out Array of channels_count pointers on 16 bit words to
|
||||
* store sample channels. Modified on return.
|
||||
* @param channels_off Array of channels_count offset index to add to respective pointer
|
||||
* in channels_out for first sample.
|
||||
* @param channels_incr Array of channels_count increment between consecutive
|
||||
* samples channels.
|
||||
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
|
||||
*
|
||||
* Useful for storing:
|
||||
* - interleaved channels in a unique buffer.
|
||||
* - non interleaved channels in an unique buffer (or in distinct buffers).
|
||||
*
|
||||
* Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn)
|
||||
* in a unique buffer:
|
||||
* { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4, ....
|
||||
* sn:c1, sn:c2, sn:c3, sn:c4 }.
|
||||
*
|
||||
* @note Should only be called from synthesis thread.
|
||||
* @note Reverb and Chorus are mixed to \c lout resp. \c rout.
|
||||
* @note Dithering is performed when converting from internal floating point to
|
||||
* 16 bit audio.
|
||||
*/
|
||||
int
|
||||
fluid_synth_write_s16_channels(fluid_synth_t *synth, int len,
|
||||
int channels_count,
|
||||
void *channels_out[], int channels_off[],
|
||||
int channels_incr[])
|
||||
{
|
||||
int16_t **chan_out = (int16_t **)channels_out;
|
||||
int di, n, cur, size;
|
||||
int16_t *left_out = (int16_t *)lout + loff;
|
||||
int16_t *right_out = (int16_t *)rout + roff;
|
||||
|
||||
/* pointers on first input mixer buffer */
|
||||
fluid_real_t *left_in;
|
||||
fluid_real_t *right_in;
|
||||
int bufs_in_count; /* number of stereo input buffers */
|
||||
int i;
|
||||
|
||||
/* start average cpu load probe */
|
||||
double time = fluid_utime();
|
||||
float cpu_load;
|
||||
|
||||
/* start profiling duration probe (if profiling is enabled) */
|
||||
fluid_profile_ref_var(prof_ref);
|
||||
|
||||
/* check parameters */
|
||||
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
|
||||
|
||||
fluid_return_val_if_fail(len >= 0, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below
|
||||
|
||||
/* Conversely to fluid_synth_process() (which handle possible multiple stereo output),
|
||||
/* check for valid channel_count: must be multiple of 2 and
|
||||
channel_count/2 must not exceed the number of internal mixer buffers
|
||||
(synth->audio_groups)
|
||||
*/
|
||||
fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */
|
||||
&& channels_count >= 2, FLUID_FAILED);
|
||||
|
||||
bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */
|
||||
fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED);
|
||||
|
||||
fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED);
|
||||
fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED);
|
||||
|
||||
/* initialize output channels buffers on first sample position */
|
||||
i = channels_count;
|
||||
do
|
||||
{
|
||||
i--;
|
||||
chan_out[i] += channels_off[i];
|
||||
}
|
||||
while(i);
|
||||
|
||||
/* Conversely to fluid_synth_process(),
|
||||
we want rendered audio effect mixed in internal audio dry buffers.
|
||||
TRUE instructs the mixer that internal audio effects will be mixed in first internal
|
||||
TRUE instructs the mixer that internal audio effects will be mixed in internal
|
||||
audio dry buffers.
|
||||
*/
|
||||
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE);
|
||||
|
@ -4109,7 +4299,8 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
|
|||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||
|
||||
size = len;
|
||||
cur = synth->cur;
|
||||
/* synth->cur indicates if available samples are still in internal mixer buffer */
|
||||
cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */
|
||||
di = synth->dither_index;
|
||||
|
||||
do
|
||||
|
@ -4118,8 +4309,11 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
|
|||
if(cur >= synth->curmax)
|
||||
{
|
||||
/* render audio (dry and effect) to internal dry buffers */
|
||||
/* always render full blocs multiple of FLUID_BUFSIZE */
|
||||
int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
|
||||
synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
|
||||
|
||||
/* get first internal mixer audio dry buffer's pointer (left and right channel) */
|
||||
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
|
||||
cur = 0;
|
||||
}
|
||||
|
@ -4147,11 +4341,25 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
|
|||
|
||||
do
|
||||
{
|
||||
*left_out = round_clip_to_i16(left_in[n] * 32766.0f + rand_table[0][di]);
|
||||
*right_out = round_clip_to_i16(right_in[n] * 32766.0f + rand_table[1][di]);
|
||||
i = bufs_in_count;
|
||||
do
|
||||
{
|
||||
/* input sample index in stereo buffer i */
|
||||
int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n;
|
||||
int c = i << 1; /* channel index c to write */
|
||||
|
||||
left_out += lincr;
|
||||
right_out += rincr;
|
||||
/* write left input sample to channel sample */
|
||||
*chan_out[c] = round_clip_to_i16(left_in[in_idx] * 32766.0f +
|
||||
rand_table[0][di]);
|
||||
|
||||
/* write right input sample to next channel sample */
|
||||
*chan_out[c+1] = round_clip_to_i16(right_in[in_idx] * 32766.0f +
|
||||
rand_table[1][di]);
|
||||
/* advance output pointers */
|
||||
chan_out[c] += channels_incr[c];
|
||||
chan_out[c+1] += channels_incr[c+1];
|
||||
}
|
||||
while(i);
|
||||
|
||||
if(++di >= DITHER_SIZE)
|
||||
{
|
||||
|
@ -4162,17 +4370,19 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
|
|||
}
|
||||
while(size);
|
||||
|
||||
synth->cur = cur;
|
||||
synth->cur = cur; /* save current sample position. It will be used on next call */
|
||||
synth->dither_index = di; /* keep dither buffer continuous */
|
||||
|
||||
/* save average cpu load, used by API for real time cpu load meter */
|
||||
time = fluid_utime() - time;
|
||||
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
|
||||
fluid_atomic_float_set(&synth->cpu_load, cpu_load);
|
||||
|
||||
/* stop duration probe and save performance measurement (if profiling is enabled) */
|
||||
fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
|
||||
fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
|
||||
len);
|
||||
return 0;
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -185,6 +185,29 @@ typedef int (*fluid_audio_callback_t)(fluid_synth_t *synth, int len,
|
|||
void *out1, int loff, int lincr,
|
||||
void *out2, int roff, int rincr);
|
||||
|
||||
typedef int (*fluid_audio_channels_callback_t)(fluid_synth_t *synth, int len,
|
||||
int channels_count,
|
||||
void *channels_out[], int channels_off[],
|
||||
int channels_incr[]);
|
||||
|
||||
int
|
||||
fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len,
|
||||
int channels_count,
|
||||
void *channels_out[], int channels_off[],
|
||||
int channels_incr[],
|
||||
int (*block_render_func)(fluid_synth_t *, int));
|
||||
|
||||
int
|
||||
fluid_synth_write_s16_channels(fluid_synth_t *synth, int len,
|
||||
int channels_count,
|
||||
void *channels_out[], int channels_off[],
|
||||
int channels_incr[]);
|
||||
int
|
||||
fluid_synth_write_float_channels(fluid_synth_t *synth, int len,
|
||||
int channels_count,
|
||||
void *channels_out[], int channels_off[],
|
||||
int channels_incr[]);
|
||||
|
||||
fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth,
|
||||
int banknum,
|
||||
int prognum);
|
||||
|
@ -215,12 +238,12 @@ int fluid_synth_set_gen2(fluid_synth_t *synth, int chan,
|
|||
|
||||
int
|
||||
fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],
|
||||
int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int));
|
||||
int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int));
|
||||
int
|
||||
fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,
|
||||
void *lout, int loff, int lincr,
|
||||
void *rout, int roff, int rincr,
|
||||
int (*block_render_func)(fluid_synth_t *, int));
|
||||
void *lout, int loff, int lincr,
|
||||
void *rout, int roff, int rincr,
|
||||
int (*block_render_func)(fluid_synth_t *, int));
|
||||
/*
|
||||
* misc
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue