diff options
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/ALc.c | 363 | ||||
-rw-r--r-- | Alc/ALu.c | 31 | ||||
-rw-r--r-- | Alc/backends/mmdevapi.c | 14 | ||||
-rw-r--r-- | Alc/effects/autowah.c | 6 | ||||
-rw-r--r-- | Alc/effects/compressor.c | 4 | ||||
-rw-r--r-- | Alc/midi/base.c | 69 | ||||
-rw-r--r-- | Alc/midi/base.h | 17 | ||||
-rw-r--r-- | Alc/midi/dummy.c | 27 | ||||
-rw-r--r-- | Alc/midi/fluidsynth.c | 72 | ||||
-rw-r--r-- | Alc/midi/sf2load.c | 9 | ||||
-rw-r--r-- | Alc/mixer_defs.h | 2 | ||||
-rw-r--r-- | Alc/mixer_neon.c | 80 |
12 files changed, 434 insertions, 260 deletions
@@ -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; @@ -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; +} |