aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
Diffstat (limited to 'Alc')
-rw-r--r--Alc/ALc.c363
-rw-r--r--Alc/ALu.c31
-rw-r--r--Alc/backends/mmdevapi.c14
-rw-r--r--Alc/effects/autowah.c6
-rw-r--r--Alc/effects/compressor.c4
-rw-r--r--Alc/midi/base.c69
-rw-r--r--Alc/midi/base.h17
-rw-r--r--Alc/midi/dummy.c27
-rw-r--r--Alc/midi/fluidsynth.c72
-rw-r--r--Alc/midi/sf2load.c9
-rw-r--r--Alc/mixer_defs.h2
-rw-r--r--Alc/mixer_neon.c80
12 files changed, 434 insertions, 260 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index fea0320d..805be2fa 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -153,6 +153,8 @@ static const ALCfunction alcFunctions[] = {
DECL(alcDevicePauseSOFT),
DECL(alcDeviceResumeSOFT),
+ DECL(alcGetInteger64vSOFT),
+
DECL(alEnable),
DECL(alDisable),
DECL(alIsEnabled),
@@ -744,8 +746,8 @@ static const ALCchar alcNoDeviceExtList[] =
static const ALCchar alcExtensionList[] =
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
"ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX "
- "ALC_EXT_thread_local_context ALC_SOFTX_HRTF ALC_SOFT_loopback "
- "ALC_SOFTX_midi_interface ALC_SOFTX_pause_device";
+ "ALC_EXT_thread_local_context ALC_SOFTX_device_clock ALC_SOFTX_HRTF "
+ "ALC_SOFT_loopback ALC_SOFTX_midi_interface ALC_SOFTX_pause_device";
static const ALCint alcMajorVersion = 1;
static const ALCint alcMinorVersion = 1;
@@ -1577,6 +1579,18 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode)
}
+/* UpdateClockBase
+ *
+ * Updates the device's base clock time with however many samples have been
+ * done. This is used so frequency changes on the device don't cause the time
+ * to jump forward or back.
+ */
+static inline void UpdateClockBase(ALCdevice *device)
+{
+ device->ClockBase += device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency;
+ device->SamplesDone = 0;
+}
+
/* UpdateDeviceParams
*
* Updates device parameters according to the attribute list (caller is
@@ -1683,6 +1697,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
+ if(freq != device->Frequency)
+ UpdateClockBase(device);
device->Frequency = freq;
device->FmtChans = schans;
device->FmtType = stype;
@@ -1745,10 +1761,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->UpdateSize = (ALuint64)device->UpdateSize * freq /
device->Frequency;
- /* SSE does best with the update size being a multiple of 4 */
- if((CPUCapFlags&CPU_CAP_SSE))
+ /* SSE and Neon do best with the update size being a multiple of 4 */
+ if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
device->UpdateSize = (device->UpdateSize+3)&~3;
+ if(freq != device->Frequency)
+ UpdateClockBase(device);
device->Frequency = freq;
device->NumMonoSources = numMono;
device->NumStereoSources = numStereo;
@@ -1758,6 +1776,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
if((device->Flags&DEVICE_RUNNING))
return ALC_NO_ERROR;
+ UpdateClockBase(device);
+
oldFreq = device->Frequency;
oldChans = device->FmtChans;
oldType = device->FmtType;
@@ -1861,6 +1881,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
if((CPUCapFlags&CPU_CAP_SSE))
WARN("SSE performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
+ if((CPUCapFlags&CPU_CAP_NEON))
+ WARN("NEON performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
}
SetMixerFPUMode(&oldMode);
@@ -2415,19 +2437,14 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para
}
-/* alcGetIntegerv
- *
- * Returns information about the device and the version of OpenAL
- */
-ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsizei size,ALCint *data)
+static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
{
- device = VerifyDevice(device);
+ ALCsizei i;
- if(size == 0 || data == NULL)
+ if(size <= 0 || values == NULL)
{
alcSetError(device, ALC_INVALID_VALUE);
- if(device) ALCdevice_DecRef(device);
- return;
+ return 0;
}
if(!device)
@@ -2435,11 +2452,11 @@ ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsi
switch(param)
{
case ALC_MAJOR_VERSION:
- *data = alcMajorVersion;
- break;
+ values[0] = alcMajorVersion;
+ return 1;
case ALC_MINOR_VERSION:
- *data = alcMinorVersion;
- break;
+ values[0] = alcMinorVersion;
+ return 1;
case ALC_ATTRIBUTES_SIZE:
case ALC_ALL_ATTRIBUTES:
@@ -2452,153 +2469,269 @@ ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsi
case ALC_FORMAT_CHANNELS_SOFT:
case ALC_FORMAT_TYPE_SOFT:
alcSetError(NULL, ALC_INVALID_DEVICE);
- break;
+ return 0;
default:
alcSetError(NULL, ALC_INVALID_ENUM);
- break;
+ return 0;
}
+ return 0;
}
- else if(device->Type == Capture)
+
+ if(device->Type == Capture)
{
switch(param)
{
case ALC_CAPTURE_SAMPLES:
ALCdevice_Lock(device);
- *data = V0(device->Backend,availableSamples)();
+ values[0] = V0(device->Backend,availableSamples)();
ALCdevice_Unlock(device);
- break;
+ return 1;
case ALC_CONNECTED:
- *data = device->Connected;
- break;
+ values[0] = device->Connected;
+ return 1;
default:
alcSetError(device, ALC_INVALID_ENUM);
- break;
+ return 0;
}
+ return 0;
}
- else /* render device */
+
+ /* render device */
+ switch(param)
{
- switch(param)
- {
- case ALC_MAJOR_VERSION:
- *data = alcMajorVersion;
- break;
+ case ALC_MAJOR_VERSION:
+ values[0] = alcMajorVersion;
+ return 1;
- case ALC_MINOR_VERSION:
- *data = alcMinorVersion;
- break;
+ case ALC_MINOR_VERSION:
+ values[0] = alcMinorVersion;
+ return 1;
- case ALC_EFX_MAJOR_VERSION:
- *data = alcEFXMajorVersion;
- break;
+ case ALC_EFX_MAJOR_VERSION:
+ values[0] = alcEFXMajorVersion;
+ return 1;
- case ALC_EFX_MINOR_VERSION:
- *data = alcEFXMinorVersion;
- break;
+ case ALC_EFX_MINOR_VERSION:
+ values[0] = alcEFXMinorVersion;
+ return 1;
+ case ALC_ATTRIBUTES_SIZE:
+ values[0] = 15;
+ return 1;
+
+ case ALC_ALL_ATTRIBUTES:
+ if(size < 15)
+ {
+ alcSetError(device, ALC_INVALID_VALUE);
+ return 0;
+ }
+
+ i = 0;
+ values[i++] = ALC_FREQUENCY;
+ values[i++] = device->Frequency;
+
+ if(device->Type != Loopback)
+ {
+ values[i++] = ALC_REFRESH;
+ values[i++] = device->Frequency / device->UpdateSize;
+
+ values[i++] = ALC_SYNC;
+ values[i++] = ALC_FALSE;
+ }
+ else
+ {
+ values[i++] = ALC_FORMAT_CHANNELS_SOFT;
+ values[i++] = device->FmtChans;
+
+ values[i++] = ALC_FORMAT_TYPE_SOFT;
+ values[i++] = device->FmtType;
+ }
+
+ values[i++] = ALC_MONO_SOURCES;
+ values[i++] = device->NumMonoSources;
+
+ values[i++] = ALC_STEREO_SOURCES;
+ values[i++] = device->NumStereoSources;
+
+ values[i++] = ALC_MAX_AUXILIARY_SENDS;
+ values[i++] = device->NumAuxSends;
+
+ values[i++] = ALC_HRTF_SOFT;
+ values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+
+ values[i++] = 0;
+ return i;
+
+ case ALC_FREQUENCY:
+ values[0] = device->Frequency;
+ return 1;
+
+ case ALC_REFRESH:
+ if(device->Type == Loopback)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->Frequency / device->UpdateSize;
+ return 1;
+
+ case ALC_SYNC:
+ if(device->Type == Loopback)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = ALC_FALSE;
+ return 1;
+
+ case ALC_FORMAT_CHANNELS_SOFT:
+ if(device->Type != Loopback)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->FmtChans;
+ return 1;
+
+ case ALC_FORMAT_TYPE_SOFT:
+ if(device->Type != Loopback)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->FmtType;
+ return 1;
+
+ case ALC_MONO_SOURCES:
+ values[0] = device->NumMonoSources;
+ return 1;
+
+ case ALC_STEREO_SOURCES:
+ values[0] = device->NumStereoSources;
+ return 1;
+
+ case ALC_MAX_AUXILIARY_SENDS:
+ values[0] = device->NumAuxSends;
+ return 1;
+
+ case ALC_CONNECTED:
+ values[0] = device->Connected;
+ return 1;
+
+ case ALC_HRTF_SOFT:
+ values[0] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+ return 1;
+
+ default:
+ alcSetError(device, ALC_INVALID_ENUM);
+ return 0;
+ }
+ return 0;
+}
+
+/* alcGetIntegerv
+ *
+ * Returns information about the device and the version of OpenAL
+ */
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
+{
+ device = VerifyDevice(device);
+ if(size <= 0 || values == NULL)
+ alcSetError(device, ALC_INVALID_VALUE);
+ else
+ GetIntegerv(device, param, size, values);
+ if(device) ALCdevice_DecRef(device);
+}
+
+ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values)
+{
+ ALCint *ivals;
+ ALsizei i;
+
+ device = VerifyDevice(device);
+ if(size <= 0 || values == NULL)
+ alcSetError(device, ALC_INVALID_VALUE);
+ else if(!device || device->Type == Capture)
+ {
+ ivals = malloc(size * sizeof(ALCint));
+ size = GetIntegerv(device, pname, size, ivals);
+ for(i = 0;i < size;i++)
+ values[i] = ivals[i];
+ free(ivals);
+ }
+ else /* render device */
+ {
+ switch(pname)
+ {
case ALC_ATTRIBUTES_SIZE:
- *data = 15;
+ *values = 17;
break;
case ALC_ALL_ATTRIBUTES:
- if(size < 15)
+ if(size < 17)
alcSetError(device, ALC_INVALID_VALUE);
else
{
int i = 0;
- data[i++] = ALC_FREQUENCY;
- data[i++] = device->Frequency;
+ V0(device->Backend,lock)();
+ values[i++] = ALC_FREQUENCY;
+ values[i++] = device->Frequency;
if(device->Type != Loopback)
{
- data[i++] = ALC_REFRESH;
- data[i++] = device->Frequency / device->UpdateSize;
+ values[i++] = ALC_REFRESH;
+ values[i++] = device->Frequency / device->UpdateSize;
- data[i++] = ALC_SYNC;
- data[i++] = ALC_FALSE;
+ values[i++] = ALC_SYNC;
+ values[i++] = ALC_FALSE;
}
else
{
- data[i++] = ALC_FORMAT_CHANNELS_SOFT;
- data[i++] = device->FmtChans;
+ values[i++] = ALC_FORMAT_CHANNELS_SOFT;
+ values[i++] = device->FmtChans;
- data[i++] = ALC_FORMAT_TYPE_SOFT;
- data[i++] = device->FmtType;
+ values[i++] = ALC_FORMAT_TYPE_SOFT;
+ values[i++] = device->FmtType;
}
- data[i++] = ALC_MONO_SOURCES;
- data[i++] = device->NumMonoSources;
-
- data[i++] = ALC_STEREO_SOURCES;
- data[i++] = device->NumStereoSources;
+ values[i++] = ALC_MONO_SOURCES;
+ values[i++] = device->NumMonoSources;
- data[i++] = ALC_MAX_AUXILIARY_SENDS;
- data[i++] = device->NumAuxSends;
+ values[i++] = ALC_STEREO_SOURCES;
+ values[i++] = device->NumStereoSources;
- data[i++] = ALC_HRTF_SOFT;
- data[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+ values[i++] = ALC_MAX_AUXILIARY_SENDS;
+ values[i++] = device->NumAuxSends;
- data[i++] = 0;
- }
- break;
+ values[i++] = ALC_HRTF_SOFT;
+ values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
- case ALC_FREQUENCY:
- *data = device->Frequency;
- break;
+ values[i++] = ALC_DEVICE_CLOCK_SOFT;
+ values[i++] = device->ClockBase +
+ (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency);
- case ALC_REFRESH:
- if(device->Type == Loopback)
- alcSetError(device, ALC_INVALID_DEVICE);
- else
- *data = device->Frequency / device->UpdateSize;
- break;
-
- case ALC_SYNC:
- if(device->Type == Loopback)
- alcSetError(device, ALC_INVALID_DEVICE);
- else
- *data = ALC_FALSE;
- break;
-
- case ALC_FORMAT_CHANNELS_SOFT:
- if(device->Type != Loopback)
- alcSetError(device, ALC_INVALID_DEVICE);
- else
- *data = device->FmtChans;
- break;
-
- case ALC_FORMAT_TYPE_SOFT:
- if(device->Type != Loopback)
- alcSetError(device, ALC_INVALID_DEVICE);
- else
- *data = device->FmtType;
- break;
-
- case ALC_MONO_SOURCES:
- *data = device->NumMonoSources;
- break;
-
- case ALC_STEREO_SOURCES:
- *data = device->NumStereoSources;
- break;
-
- case ALC_MAX_AUXILIARY_SENDS:
- *data = device->NumAuxSends;
- break;
-
- case ALC_CONNECTED:
- *data = device->Connected;
+ values[i++] = 0;
+ V0(device->Backend,unlock)();
+ }
break;
- case ALC_HRTF_SOFT:
- *data = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+ case ALC_DEVICE_CLOCK_SOFT:
+ V0(device->Backend,lock)();
+ *values = device->ClockBase +
+ (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency);
+ V0(device->Backend,unlock)();
break;
default:
- alcSetError(device, ALC_INVALID_ENUM);
+ ivals = malloc(size * sizeof(ALCint));
+ size = GetIntegerv(device, pname, size, ivals);
+ for(i = 0;i < size;i++)
+ values[i] = ivals[i];
+ free(ivals);
break;
}
}
@@ -2933,6 +3066,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
device->ContextList = NULL;
+ device->ClockBase = 0;
+ device->SamplesDone = 0;
+
device->MaxNoOfSources = 256;
device->AuxiliaryEffectSlotMax = 4;
device->NumAuxSends = MAX_SENDS;
@@ -3400,6 +3536,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
device->ContextList = NULL;
+ device->ClockBase = 0;
+ device->SamplesDone = 0;
+
device->MaxNoOfSources = 256;
device->AuxiliaryEffectSlotMax = 4;
device->NumAuxSends = MAX_SENDS;
diff --git a/Alc/ALu.c b/Alc/ALu.c
index 34ac6687..fd8065ed 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -118,6 +118,10 @@ static DryMixerFunc SelectDirectMixer(void)
if((CPUCapFlags&CPU_CAP_SSE))
return MixDirect_SSE;
#endif
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return MixDirect_Neon;
+#endif
return MixDirect_C;
}
@@ -128,6 +132,10 @@ static WetMixerFunc SelectSendMixer(void)
if((CPUCapFlags&CPU_CAP_SSE))
return MixSend_SSE;
#endif
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return MixSend_Neon;
+#endif
return MixSend_C;
}
@@ -1118,25 +1126,20 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
for(i = 0;i < SamplesToDo;i++)
(*slot)->WetBuffer[0][i] = 0.0f;
}
+
+ /* Increment the clock time. Every second's worth of samples is
+ * converted and added to clock base so that large sample counts don't
+ * overflow during conversion. This also guarantees an exact, stable
+ * conversion. */
+ device->SamplesDone += SamplesToDo;
+ device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
+ device->SamplesDone %= device->Frequency;
ALCdevice_Unlock(device);
/* Click-removal. Could do better; this only really handles immediate
* changes between updates where a predictive sample could be
* generated. Delays caused by effects and HRTF aren't caught. */
- if(device->FmtChans == DevFmtMono)
- {
- ALfloat offset = device->ClickRemoval[FrontCenter];
- if(offset < (1.0f/32768.0f))
- offset = 0.0f;
- else for(i = 0;i < SamplesToDo;i++)
- {
- device->DryBuffer[FrontCenter][i] += offset;
- offset -= offset * (1.0f/256.0f);
- }
- device->ClickRemoval[FrontCenter] = offset + device->PendingClicks[FrontCenter];
- device->PendingClicks[FrontCenter] = 0.0f;
- }
- else if(device->FmtChans == DevFmtStereo)
+ if(device->FmtChans == DevFmtStereo)
{
/* Assumes the first two channels are FrontLeft and FrontRight */
for(c = 0;c < 2;c++)
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c
index fa7c54f9..b93ff667 100644
--- a/Alc/backends/mmdevapi.c
+++ b/Alc/backends/mmdevapi.c
@@ -600,6 +600,13 @@ static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
CoUninitialize();
+ /* HACK: Force Windows to create a message queue for this thread before
+ * returning success, otherwise PostThreadMessage may fail if it gets
+ * called before GetMessage.
+ */
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ TRACE("Message thread initialization complete\n");
req->result = S_OK;
SetEvent(req->FinishedEvt);
@@ -834,7 +841,10 @@ static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName
data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(data->NotifyEvent == NULL || data->MsgEvent == NULL)
+ {
+ ERR("Failed to create message events: %lu\n", GetLastError());
hr = E_FAIL;
+ }
if(SUCCEEDED(hr))
{
@@ -859,6 +869,8 @@ static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName
break;
}
}
+ if(FAILED(hr))
+ WARN("Failed to find device name matching \"%s\"\n", deviceName);
}
}
@@ -869,6 +881,8 @@ static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName
hr = E_FAIL;
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
hr = WaitForResponse(&req);
+ else
+ ERR("Failed to post thread message: %lu\n", GetLastError());
}
if(FAILED(hr))
diff --git a/Alc/effects/autowah.c b/Alc/effects/autowah.c
index 9a45e233..527e3be6 100644
--- a/Alc/effects/autowah.c
+++ b/Alc/effects/autowah.c
@@ -59,7 +59,7 @@ static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
{
- state->Frequency = device->Frequency;
+ state->Frequency = (ALfloat)device->Frequency;
return AL_TRUE;
}
@@ -100,7 +100,7 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo,
/* Similar to compressor, we get the current amplitude of the
* incoming signal, and attack or release to reach it. */
- amplitude = fabs(smp);
+ amplitude = fabsf(smp);
if(amplitude > gain)
gain = minf(gain+state->AttackRate, amplitude);
else if(amplitude < gain)
@@ -108,7 +108,7 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo,
gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
/* FIXME: What range does the filter cover? */
- cutoff = lerp(1000.0f, (ALfloat)LOWPASSFREQREF, minf(gain/state->PeakGain, 1.0f));
+ cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
/* The code below is like calling ALfilterState_setParams with
* ALfilterType_LowPass. However, instead of passing a bandwidth,
diff --git a/Alc/effects/compressor.c b/Alc/effects/compressor.c
index 14c0ed10..bab155c8 100644
--- a/Alc/effects/compressor.c
+++ b/Alc/effects/compressor.c
@@ -84,12 +84,12 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint Samples
{
smp = SamplesIn[it+base];
- amplitude = fabs(smp);
+ amplitude = fabsf(smp);
if(amplitude > gain)
gain = minf(gain+state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain-state->ReleaseRate, amplitude);
- output = 1.0 / clampf(gain, 0.5f, 2.0f);
+ output = 1.0f / clampf(gain, 0.5f, 2.0f);
temps[it] = smp * output;
}
diff --git a/Alc/midi/base.c b/Alc/midi/base.c
index 25dd19d9..1850a6c6 100644
--- a/Alc/midi/base.c
+++ b/Alc/midi/base.c
@@ -16,9 +16,6 @@
#include "alu.h"
-/* Microsecond resolution */
-#define TICKS_PER_SECOND (1000000)
-
/* MIDI events */
#define SYSEX_EVENT (0xF0)
@@ -129,12 +126,9 @@ void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
self->Gain = 1.0f;
self->State = AL_INITIAL;
- self->LastEvtTime = 0;
- self->NextEvtTime = UINT64_MAX;
- self->SamplesSinceLast = 0.0;
- self->SamplesToNext = 0.0;
-
- self->SamplesPerTick = (ALdouble)device->Frequency / TICKS_PER_SECOND;
+ self->ClockBase = 0;
+ self->SamplesDone = 0;
+ self->SampleRate = device->Frequency;
}
void MidiSynth_Destruct(MidiSynth *self)
@@ -195,29 +189,22 @@ void MidiSynth_stop(MidiSynth *self)
{
ResetEvtQueue(&self->EventQueue);
- self->LastEvtTime = 0;
- self->NextEvtTime = UINT64_MAX;
- self->SamplesSinceLast = 0.0;
- self->SamplesToNext = 0.0;
+ self->ClockBase = 0;
+ self->SamplesDone = 0;
}
extern inline void MidiSynth_reset(MidiSynth *self);
-
-ALuint64 MidiSynth_getTime(const MidiSynth *self)
-{
- ALuint64 time = self->LastEvtTime + (self->SamplesSinceLast/self->SamplesPerTick);
- return clampu64(time, self->LastEvtTime, self->NextEvtTime);
-}
-
+extern inline ALuint64 MidiSynth_getTime(const MidiSynth *self);
extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
-void MidiSynth_setSampleRate(MidiSynth *self, ALdouble srate)
+void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate)
{
- ALdouble sampletickrate = srate / TICKS_PER_SECOND;
-
- self->SamplesSinceLast = self->SamplesSinceLast * sampletickrate / self->SamplesPerTick;
- self->SamplesToNext = self->SamplesToNext * sampletickrate / self->SamplesPerTick;
- self->SamplesPerTick = sampletickrate;
+ if(self->SampleRate != srate)
+ {
+ self->ClockBase += self->SamplesDone * MIDI_CLOCK_RES / self->SampleRate;
+ self->SamplesDone = 0;
+ self->SampleRate = srate;
+ }
}
extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
@@ -225,25 +212,11 @@ extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
{
MidiEvent entry;
- ALenum err;
-
entry.time = time;
entry.event = event;
entry.param.val[0] = param1;
entry.param.val[1] = param2;
-
- err = InsertEvtQueue(&self->EventQueue, &entry);
- if(err != AL_NO_ERROR) return err;
-
- if(entry.time < self->NextEvtTime)
- {
- self->NextEvtTime = entry.time;
-
- self->SamplesToNext = (self->NextEvtTime - self->LastEvtTime) * self->SamplesPerTick;
- self->SamplesToNext -= self->SamplesSinceLast;
- }
-
- return AL_NO_ERROR;
+ return InsertEvtQueue(&self->EventQueue, &entry);
}
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
@@ -261,18 +234,6 @@ ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *
err = InsertEvtQueue(&self->EventQueue, &entry);
if(err != AL_NO_ERROR)
- {
free(entry.param.sysex.data);
- return err;
- }
-
- if(entry.time < self->NextEvtTime)
- {
- self->NextEvtTime = entry.time;
-
- self->SamplesToNext = (self->NextEvtTime - self->LastEvtTime) * self->SamplesPerTick;
- self->SamplesToNext -= self->SamplesSinceLast;
- }
-
- return AL_NO_ERROR;
+ return err;
}
diff --git a/Alc/midi/base.h b/Alc/midi/base.h
index f900c941..4d13a054 100644
--- a/Alc/midi/base.h
+++ b/Alc/midi/base.h
@@ -23,17 +23,17 @@ typedef struct Reader {
ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
+#define MIDI_CLOCK_RES U64(1000000000)
+
+
struct MidiSynthVtable;
typedef struct MidiSynth {
EvtQueue EventQueue;
- ALuint64 LastEvtTime;
- ALuint64 NextEvtTime;
- ALdouble SamplesSinceLast;
- ALdouble SamplesToNext;
-
- ALdouble SamplesPerTick;
+ ALuint64 ClockBase;
+ ALuint SamplesDone;
+ ALuint SampleRate;
/* NOTE: This rwlock is for the state and soundfont. The EventQueue and
* related must instead use the device lock as they're used in the mixer
@@ -59,14 +59,15 @@ inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&sel
inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; }
void MidiSynth_stop(MidiSynth *self);
inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); }
-ALuint64 MidiSynth_getTime(const MidiSynth *self);
+inline ALuint64 MidiSynth_getTime(const MidiSynth *self)
+{ return self->ClockBase + (self->SamplesDone*MIDI_CLOCK_RES/self->SampleRate); }
inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self)
{
if(self->EventQueue.pos == self->EventQueue.size)
return UINT64_MAX;
return self->EventQueue.events[self->EventQueue.pos].time;
}
-void MidiSynth_setSampleRate(MidiSynth *self, ALdouble srate);
+void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate);
inline void MidiSynth_update(MidiSynth *self, ALCdevice *device)
{ MidiSynth_setSampleRate(self, device->Frequency); }
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
diff --git a/Alc/midi/dummy.c b/Alc/midi/dummy.c
index 71c03efb..79f82b87 100644
--- a/Alc/midi/dummy.c
+++ b/Alc/midi/dummy.c
@@ -49,30 +49,17 @@ static void DSynth_processQueue(DSynth *self, ALuint64 time)
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALuint64 curtime;
if(synth->State != AL_PLAYING)
return;
- synth->SamplesSinceLast += SamplesToDo;
- synth->SamplesToNext -= SamplesToDo;
- while(synth->SamplesToNext < 1.0f)
- {
- ALuint64 time = synth->NextEvtTime;
- if(time == UINT64_MAX)
- {
- synth->SamplesToNext = 0.0;
- break;
- }
-
- synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
- synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
- synth->LastEvtTime = time;
- DSynth_processQueue(self, time);
-
- synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
- if(synth->NextEvtTime != UINT64_MAX)
- synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
- }
+ synth->SamplesDone += SamplesToDo;
+ synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
+ synth->SamplesDone %= synth->SampleRate;
+
+ curtime = MidiSynth_getTime(synth);
+ DSynth_processQueue(self, maxi64(curtime-1, 0));
}
diff --git a/Alc/midi/fluidsynth.c b/Alc/midi/fluidsynth.c
index 9d58f87b..d4e594e6 100644
--- a/Alc/midi/fluidsynth.c
+++ b/Alc/midi/fluidsynth.c
@@ -640,24 +640,12 @@ static void FSynth_setState(FSynth *self, ALenum state)
static void FSynth_stop(FSynth *self)
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALuint64 curtime;
ALsizei chan;
/* Make sure all pending events are processed. */
- while(!(synth->SamplesToNext >= 1.0))
- {
- ALuint64 time = synth->NextEvtTime;
- if(time == UINT64_MAX)
- break;
-
- synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
- synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
- synth->LastEvtTime = time;
- FSynth_processQueue(self, time);
-
- synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
- if(synth->NextEvtTime != UINT64_MAX)
- synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
- }
+ curtime = MidiSynth_getTime(synth);
+ FSynth_processQueue(self, curtime);
/* All notes off */
for(chan = 0;chan < 16;chan++)
@@ -759,6 +747,7 @@ static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALenum state = synth->State;
+ ALuint64 curtime;
ALuint total = 0;
if(state == AL_INITIAL)
@@ -770,41 +759,42 @@ static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict
return;
}
+ curtime = MidiSynth_getTime(synth);
while(total < SamplesToDo)
{
- if(synth->SamplesToNext >= 1.0)
- {
- ALuint todo = minu(SamplesToDo - total, fastf2u(synth->SamplesToNext));
+ ALuint64 time, diff;
+ ALint tonext;
- fluid_synth_write_float(self->Synth, todo,
- &DryBuffer[FrontLeft][total], 0, 1,
- &DryBuffer[FrontRight][total], 0, 1);
- total += todo;
- synth->SamplesSinceLast += todo;
- synth->SamplesToNext -= todo;
+ time = MidiSynth_getNextEvtTime(synth);
+ diff = maxu64(time, curtime) - curtime;
+ if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
+ {
+ /* If there's no pending event, or if it's more than 1 second
+ * away, do as many samples as we can. */
+ tonext = INT_MAX;
}
else
{
- ALuint64 time = synth->NextEvtTime;
- if(time == UINT64_MAX)
- {
- synth->SamplesSinceLast += SamplesToDo-total;
- fluid_synth_write_float(self->Synth, SamplesToDo-total,
- &DryBuffer[FrontLeft][total], 0, 1,
- &DryBuffer[FrontRight][total], 0, 1);
- break;
- }
-
- synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
- synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
- synth->LastEvtTime = time;
- FSynth_processQueue(self, time);
+ /* Figure out how many samples until the next event. */
+ tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
+ tonext -= total;
+ }
- synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
- if(synth->NextEvtTime != UINT64_MAX)
- synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ if(tonext > 0)
+ {
+ ALuint todo = mini(tonext, SamplesToDo-total);
+ fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1,
+ DryBuffer[FrontRight], total, 1);
+ total += todo;
+ tonext -= todo;
}
+ if(total < SamplesToDo && tonext == 0)
+ FSynth_processQueue(self, time);
}
+
+ synth->SamplesDone += SamplesToDo;
+ synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
+ synth->SamplesDone %= synth->SampleRate;
}
diff --git a/Alc/midi/sf2load.c b/Alc/midi/sf2load.c
index 5bba345f..169a5189 100644
--- a/Alc/midi/sf2load.c
+++ b/Alc/midi/sf2load.c
@@ -1192,15 +1192,18 @@ ALboolean loadSf2(Reader *stream, ALsoundfont *soundfont, ALCcontext *context)
READ(stream, ptr, smpl.mSize);
else
{
- while(smpl.mSize > 0)
+ ALuint total = 0;
+ while(total < smpl.mSize)
{
ALbyte buf[4096];
- ALuint todo = minu(smpl.mSize, sizeof(buf));
+ ALuint todo = minu(smpl.mSize-total, sizeof(buf));
ALuint i;
READ(stream, buf, todo);
for(i = 0;i < todo;i++)
- ptr[i] = buf[i^1];
+ ptr[total+i] = buf[i^1];
+
+ total += todo;
}
}
list.mSize -= smpl.mSize;
diff --git a/Alc/mixer_defs.h b/Alc/mixer_defs.h
index 5e43af15..f8968a0a 100644
--- a/Alc/mixer_defs.h
+++ b/Alc/mixer_defs.h
@@ -27,5 +27,7 @@ void MixSend_SSE(const struct SendParams*,const ALfloat*restrict,ALuint,ALuint,A
/* Neon mixers */
void MixDirect_Hrtf_Neon(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint);
+void MixDirect_Neon(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint);
+void MixSend_Neon(const struct SendParams*,const ALfloat*restrict,ALuint,ALuint,ALuint);
#endif /* MIXER_DEFS_H */
diff --git a/Alc/mixer_neon.c b/Alc/mixer_neon.c
index 571221be..0aa450ad 100644
--- a/Alc/mixer_neon.c
+++ b/Alc/mixer_neon.c
@@ -14,11 +14,15 @@ static inline void ApplyCoeffsStep(const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2])
{
+ float32x4_t coeffs, deltas;
ALuint c;
- for(c = 0;c < IrSize;c++)
+
+ for(c = 0;c < IrSize;c += 2)
{
- Coeffs[c][0] += CoeffStep[c][0];
- Coeffs[c][1] += CoeffStep[c][1];
+ coeffs = vld1q_f32(&Coeffs[c][0]);
+ deltas = vld1q_f32(&CoeffStep[c][0]);
+ coeffs = vaddq_f32(coeffs, deltas);
+ vst1q_f32(&Coeffs[c][0], coeffs);
}
}
@@ -54,3 +58,73 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
#define SUFFIX Neon
#include "mixer_inc.c"
#undef SUFFIX
+
+
+void MixDirect_Neon(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan,
+ ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize)
+{
+ ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer;
+ ALfloat *restrict ClickRemoval = params->ClickRemoval;
+ ALfloat *restrict PendingClicks = params->PendingClicks;
+ ALfloat DrySend;
+ float32x4_t gain;
+ ALuint pos;
+ ALuint c;
+
+ for(c = 0;c < MaxChannels;c++)
+ {
+ DrySend = params->Gains[srcchan][c];
+ if(!(DrySend > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ if(OutPos == 0)
+ ClickRemoval[c] -= data[0]*DrySend;
+
+ gain = vdupq_n_f32(DrySend);
+ for(pos = 0;BufferSize-pos > 3;pos += 4)
+ {
+ const float32x4_t val4 = vld1q_f32(&data[pos]);
+ float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
+ dry4 = vaddq_f32(dry4, vmulq_f32(val4, gain));
+ vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+ }
+ for(;pos < BufferSize;pos++)
+ OutBuffer[c][OutPos+pos] += data[pos]*DrySend;
+
+ if(OutPos+pos == SamplesToDo)
+ PendingClicks[c] += data[pos]*DrySend;
+ }
+}
+
+
+void MixSend_Neon(const SendParams *params, const ALfloat *restrict data,
+ ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize)
+{
+ ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer;
+ ALfloat *restrict ClickRemoval = params->ClickRemoval;
+ ALfloat *restrict PendingClicks = params->PendingClicks;
+ ALfloat WetGain;
+ float32x4_t gain;
+ ALuint pos;
+
+ WetGain = params->Gain;
+ if(!(WetGain > GAIN_SILENCE_THRESHOLD))
+ return;
+
+ if(OutPos == 0)
+ ClickRemoval[0] -= data[0] * WetGain;
+
+ gain = vdupq_n_f32(WetGain);
+ for(pos = 0;BufferSize-pos > 3;pos += 4)
+ {
+ const float32x4_t val4 = vld1q_f32(&data[pos]);
+ float32x4_t wet4 = vld1q_f32(&OutBuffer[0][OutPos+pos]);
+ wet4 = vaddq_f32(wet4, vmulq_f32(val4, gain));
+ vst1q_f32(&OutBuffer[0][OutPos+pos], wet4);
+ }
+ for(;pos < BufferSize;pos++)
+ OutBuffer[0][OutPos+pos] += data[pos] * WetGain;
+
+ if(OutPos+pos == SamplesToDo)
+ PendingClicks[0] += data[pos] * WetGain;
+}