Fix waveout driver crash (#759)

This PR addresses issue https://github.com/FluidSynth/fluidsynth/issues/758.

It ensures that the `internal buffer size` used by waveout device driver and the `extra buffers size` required by fluid_synth_process() are both coherent (i.e they should be of same size). To ensure this, both kind of buffers are now dependent of`audio.period` and `audio.period-size settings`.
This commit is contained in:
jjceresa 2021-01-29 13:56:57 +01:00 committed by GitHub
parent c7878dec74
commit 8322d95425
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -33,12 +33,9 @@
#include <ks.h>
#include <ksmedia.h>
/* Number of buffers in the chain */
/* Number of driver buffers in the chain */
#define NB_SOUND_BUFFERS 4
/* 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.
@ -93,7 +90,7 @@ typedef struct
HWAVEOUT hWaveOut;
WAVEHDR waveHeader[NB_SOUND_BUFFERS];
int sample_size;
int periods; /* numbers of internal driver buffers */
int num_frames;
HANDLE hThread;
@ -255,11 +252,14 @@ void fluid_waveout_audio_driver_settings(fluid_settings_t *settings)
*
* @param setting. The settings the driver looks for:
* "synth.sample-rate", the sample rate.
* "synth.audio-channels", the number of internal mixer stereo buffer.
* "audio.periods",the number of buffers.
* "audio.period-size",the size of one 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.
* Note: The number of internal mixer stereo 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.
*/
@ -292,6 +292,13 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
fluid_settings_getint(settings, "audio.period-size", &period_size);
fluid_settings_getint(settings, "synth.audio-channels", &audio_channels);
if(periods > NB_SOUND_BUFFERS)
{
FLUID_LOG(FLUID_INFO, "audio.periods %d exceeds internal limit %d",
periods, NB_SOUND_BUFFERS);
return NULL;
}
/* Clear format structure */
ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
@ -339,7 +346,6 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
wfx.Format.nBlockAlign = sample_size * wfx.Format.nChannels;
wfx.Format.nSamplesPerSec = frequency;
wfx.Format.nAvgBytesPerSec = frequency * wfx.Format.nBlockAlign;
/* WAVEFORMATEXTENSIBLE extension is used only when channels number
is above 2.
When channels number is below 2, only WAVEFORMATEX structure
@ -355,14 +361,13 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
wfx.dwChannelMask = channel_mask_speakers[audio_channels - 1];
}
/* Calculate the length of a single buffer */
lenBuffer = (MS_BUFFER_LENGTH * wfx.Format.nAvgBytesPerSec + 999) / 1000;
/* Round to 8-bytes size */
lenBuffer = (lenBuffer + 7) & ~7;
/* allocate the internal waveout buffers:
The length of a single buffer in bytes is dependant of period_size.
*/
lenBuffer = wfx.Format.nBlockAlign * period_size;
/* create and clear the driver data */
dev = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(fluid_waveout_audio_driver_t) + lenBuffer * NB_SOUND_BUFFERS);
sizeof(fluid_waveout_audio_driver_t) + lenBuffer * periods);
if(dev == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
@ -375,10 +380,10 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
/* Save copy of other variables */
dev->write_ptr = write_ptr;
dev->sample_size = sample_size;
/* Calculate the number of frames in a block */
dev->num_frames = lenBuffer / wfx.Format.nBlockAlign;
/* number of frames in a buffer */
dev->num_frames = period_size;
/* number of buffers */
dev->periods = periods;
dev->channels_count = wfx.Format.nChannels;
/* Set default device to use */
@ -386,6 +391,12 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
if(func)
{
/* allocate extra buffer used by fluid_waveout_write_processed_channels().
These buffers are buffer adaptation between the rendering
API fluid_synth_process() and the waveout internal buffers
Note: the size (in bytes) of these extra buffer (drybuf[]) must be the
same that the size of internal waveout buffers.
*/
dev->drybuf = FLUID_ARRAY(float*, audio_channels * 2);
if(dev->drybuf == NULL)
{
@ -396,7 +407,8 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
FLUID_MEMSET(dev->drybuf, 0, sizeof(float*) * audio_channels * 2);
for(i = 0; i < audio_channels * 2; ++i)
{
dev->drybuf[i] = FLUID_ARRAY(float, periods * period_size);
/* The length of a single buffer drybuf[i] is dependant of period_size */
dev->drybuf[i] = FLUID_ARRAY(float, period_size);
if(dev->drybuf[i] == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
@ -486,7 +498,7 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
ptrBuffer = (LPSTR)(dev + 1);
/* Setup the sample buffers */
for(i = 0; i < NB_SOUND_BUFFERS; i++)
for(i = 0; i < periods; i++)
{
/* Clear the sample buffer */
memset(ptrBuffer, 0, lenBuffer);
@ -505,7 +517,7 @@ new_fluid_waveout_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t f
}
/* Play the sample buffers */
for(i = 0; i < NB_SOUND_BUFFERS; i++)
for(i = 0; i < periods; i++)
{
waveOutWrite(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR));
}
@ -530,7 +542,7 @@ void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *d)
/* release all the allocated resources */
if(dev->hWaveOut != NULL)
{
dev->nQuit = NB_SOUND_BUFFERS;
dev->nQuit = dev->periods;
WaitForSingleObject(dev->hQuit, INFINITE);
waveOutClose(dev->hWaveOut);