Skip to content

Commit

Permalink
Fixed changing sound of a playing pipe without Pipe999IsTremulant whe…
Browse files Browse the repository at this point in the history
…n a wave tremulant state is changed #1855
  • Loading branch information
oleg68 committed Apr 11, 2024
1 parent 75c4abb commit 530e239
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 79 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Fixed changing sound of a playing pipe without Pipe999IsTremulant when a wave tremulant state is changed https://github.com/GrandOrgue/grandorgue/issues/1855
# 3.14.0 (2024-03-29)
- Fixed crash on loading an organ without a pedal but wit a unison-off coupler https://github.com/GrandOrgue/grandorgue/issues/1846
- Changed displaying of the right part of paths https://github.com/GrandOrgue/grandorgue/issues/1663
Expand Down
57 changes: 30 additions & 27 deletions src/grandorgue/model/GOSoundingPipe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ GOSoundingPipe::GOSoundingPipe(
bool retune)
: GOPipe(pOrganModel, rank, midi_key_number),
p_OrganModel(pOrganModel),
m_Sampler(NULL),
p_CurrentLoopSampler(nullptr),
m_LastStart(0),
m_LastStop(0),
m_Instances(0),
m_IsWaveTremulantActive(false),
m_AttackFileInfos(),
m_ReleaseFileInfos(),
m_Filename(),
Expand Down Expand Up @@ -475,14 +474,16 @@ void GOSoundingPipe::Validate() {
}

void GOSoundingPipe::SetWaveTremulant(bool on) {
if (on != m_IsWaveTremulantActive) {
m_IsWaveTremulantActive = on;
if (m_SoundProvider.IsWaveTremulant() != on) {
m_SoundProvider.SetWaveTremulant(on);

GOSoundEngine *pSoundEngine = GetSoundEngine();

if (pSoundEngine && m_Sampler)
pSoundEngine->SwitchSample(&m_SoundProvider, m_Sampler);
if (
pSoundEngine && p_CurrentLoopSampler
&& !m_SoundProvider.IsWaveTremulantStateSuitable(
p_CurrentLoopSampler->m_WaveTremulantStateFor))
pSoundEngine->SwitchSample(&m_SoundProvider, p_CurrentLoopSampler);
}
}

Expand All @@ -492,28 +493,30 @@ void GOSoundingPipe::VelocityChanged(

if (!m_Instances && velocity) {
// the key pressed
m_Sampler = pSoundEngine ? pSoundEngine->StartPipeSample(
&m_SoundProvider,
m_WindchestN,
m_AudioGroupID,
velocity,
m_PipeConfigNode.GetEffectiveDelay(),
m_LastStop,
false,
&m_LastStart)
: nullptr;
if (m_Sampler)
GOSoundSampler *pSampler = pSoundEngine ? pSoundEngine->StartPipeSample(
&m_SoundProvider,
m_WindchestN,
m_AudioGroupID,
velocity,
m_PipeConfigNode.GetEffectiveDelay(),
m_LastStop,
false,
&m_LastStart)
: nullptr;
if (pSampler) {
m_Instances++;
if (m_SoundProvider.IsOneshot())
m_Sampler = nullptr;
if (!m_SoundProvider.IsOneshot()) {
p_CurrentLoopSampler = pSampler;
}
}
} else if (m_Instances && !velocity) {
// the key released
m_Instances--;
if (m_Sampler) {
if (p_CurrentLoopSampler) {
m_LastStop = pSoundEngine
? pSoundEngine->StopSample(&m_SoundProvider, m_Sampler)
? pSoundEngine->StopSample(&m_SoundProvider, p_CurrentLoopSampler)
: 0;
m_Sampler = nullptr;
p_CurrentLoopSampler = nullptr;
} else if (m_PipeConfigNode.IsEffectiveIndependentRelease() && pSoundEngine)
pSoundEngine->StartPipeSample(
&m_SoundProvider,
Expand All @@ -524,10 +527,11 @@ void GOSoundingPipe::VelocityChanged(
m_LastStart,
true,
&m_LastStop);
} else if (m_Sampler && last_velocity != velocity)
} else if (p_CurrentLoopSampler && last_velocity != velocity)
// the key was pressed before and the velocity is changed now
if (pSoundEngine)
pSoundEngine->UpdateVelocity(&m_SoundProvider, m_Sampler, velocity);
pSoundEngine->UpdateVelocity(
&m_SoundProvider, p_CurrentLoopSampler, velocity);
}

void GOSoundingPipe::UpdateAmplitude() {
Expand Down Expand Up @@ -569,9 +573,8 @@ void GOSoundingPipe::PreparePlayback() {

void GOSoundingPipe::AbortPlayback() {
m_Instances = 0;
m_IsWaveTremulantActive = false;
m_Sampler = 0;
p_CurrentLoopSampler = 0;
m_LastStop = 0;
m_SoundProvider.SetWaveTremulant(0);
m_SoundProvider.SetWaveTremulant(false);
GOPipe::AbortPlayback();
}
3 changes: 1 addition & 2 deletions src/grandorgue/model/GOSoundingPipe.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ class GOSoundingPipe : public GOPipe,
private GOPipeWindchestCallback {
private:
GOOrganModel *p_OrganModel;
GOSoundSampler *m_Sampler;
GOSoundSampler *p_CurrentLoopSampler;
uint64_t m_LastStart;
uint64_t m_LastStop;
int m_Instances;
bool m_IsWaveTremulantActive;
std::vector<GOSoundProviderWave::AttackFileInfo> m_AttackFileInfos;
std::vector<GOSoundProviderWave::ReleaseFileInfo> m_ReleaseFileInfos;
wxString m_Filename;
Expand Down
66 changes: 36 additions & 30 deletions src/grandorgue/sound/GOSoundEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ void GOSoundEngine::ProcessRelease(GOSoundSampler *sampler) {
CreateReleaseSampler(sampler);
sampler->stop = 0;
} else if (sampler->new_attack) {
SwitchAttackSampler(sampler);
SwitchToAnotherAttack(sampler);
sampler->new_attack = 0;
}
PassSampler(sampler);
Expand Down Expand Up @@ -386,7 +386,7 @@ unsigned GOSoundEngine::SamplesDiffToMs(
(toSamples - fromSamples) * 1000 / m_SampleRate, (uint64_t)UINT_MAX);
}

GOSoundSampler *GOSoundEngine::StartTaskSample(
GOSoundSampler *GOSoundEngine::CreateTaskSample(
const GOSoundProvider *pSoundProvider,
int samplerTaskId,
unsigned audioGroup,
Expand All @@ -411,6 +411,7 @@ GOSoundSampler *GOSoundEngine::StartTaskSample(
sampler = m_SamplerPool.GetSampler();
if (sampler) {
sampler->p_SoundProvider = pSoundProvider;
sampler->m_WaveTremulantStateFor = section->GetWaveTremulantStateFor();
sampler->velocity = velocity;
section->InitStream(
&m_ResamplerCoefs,
Expand All @@ -434,40 +435,43 @@ GOSoundSampler *GOSoundEngine::StartTaskSample(
return sampler;
}

void GOSoundEngine::SwitchAttackSampler(GOSoundSampler *handle) {
if (!handle->p_SoundProvider)
return;
void GOSoundEngine::SwitchToAnotherAttack(GOSoundSampler *pSampler) {
const GOSoundProvider *pProvider = pSampler->p_SoundProvider;

unsigned time = 1000;
if (pProvider && !pSampler->is_release) {
const GOSoundAudioSection *section
= pProvider->GetAttack(pSampler->velocity, 1000);

const GOSoundProvider *this_pipe = handle->p_SoundProvider;
const GOSoundAudioSection *section
= this_pipe->GetAttack(handle->velocity, time);
if (!section)
return;
if (handle->is_release)
return;
if (section) {
GOSoundSampler *new_sampler = m_SamplerPool.GetSampler();

if (new_sampler != NULL) {
float gain_target = pProvider->GetGain() * section->GetNormGain();
unsigned cross_fade_len = pProvider->GetAttackSwitchCrossfadeLength();

GOSoundSampler *new_sampler = m_SamplerPool.GetSampler();
if (new_sampler != NULL) {
*new_sampler = *handle;
// copy old sampler to the new one
*new_sampler = *pSampler;

handle->p_SoundProvider = this_pipe;
handle->time = m_CurrentTime + 1;
// start decay in the new sampler
new_sampler->is_release = true;
new_sampler->time = m_CurrentTime;
new_sampler->fader.StartDecay(cross_fade_len, m_SampleRate);
new_sampler->fader.SetVelocityVolume(
new_sampler->p_SoundProvider->GetVelocityVolume(
new_sampler->velocity));

float gain_target = this_pipe->GetGain() * section->GetNormGain();
unsigned cross_fade_len = this_pipe->GetAttackSwitchCrossfadeLength();
// start new section stream in the old sampler
pSampler->m_WaveTremulantStateFor = section->GetWaveTremulantStateFor();
section->InitAlignedStream(&pSampler->stream, &new_sampler->stream);
pSampler->p_SoundProvider = pProvider;
pSampler->time = m_CurrentTime + 1;

handle->fader.NewAttacking(gain_target, cross_fade_len, m_SampleRate);
section->InitAlignedStream(&handle->stream, &new_sampler->stream);
handle->is_release = false;
new_sampler->is_release = true;
new_sampler->time = m_CurrentTime;
new_sampler->fader.StartDecay(cross_fade_len, m_SampleRate);
new_sampler->fader.SetVelocityVolume(
new_sampler->p_SoundProvider->GetVelocityVolume(new_sampler->velocity));
pSampler->fader.NewAttacking(gain_target, cross_fade_len, m_SampleRate);
pSampler->is_release = false;

StartSampler(new_sampler);
StartSampler(new_sampler);
}
}
}
}

Expand All @@ -483,7 +487,7 @@ void GOSoundEngine::CreateReleaseSampler(GOSoundSampler *handle) {
* zero. */
const GOSoundProvider *this_pipe = handle->p_SoundProvider;
const GOSoundAudioSection *release_section = this_pipe->GetRelease(
handle->stream.audio_section->GetWaveTremulantStateFor(),
handle->m_WaveTremulantStateFor,
SamplesDiffToMs(handle->time, m_CurrentTime));
unsigned cross_fade_len = release_section
? release_section->GetReleaseCrossfadeLength()
Expand All @@ -506,6 +510,8 @@ void GOSoundEngine::CreateReleaseSampler(GOSoundSampler *handle) {
new_sampler->p_SoundProvider = this_pipe;
new_sampler->time = m_CurrentTime + 1;
new_sampler->velocity = handle->velocity;
new_sampler->m_WaveTremulantStateFor
= release_section->GetWaveTremulantStateFor();

unsigned gain_decay_length = 0;
float gain_target = this_pipe->GetGain() * release_section->GetNormGain();
Expand Down
15 changes: 11 additions & 4 deletions src/grandorgue/sound/GOSoundEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class GOSoundEngine {

void StartSampler(GOSoundSampler *sampler);

GOSoundSampler *StartTaskSample(
GOSoundSampler *CreateTaskSample(
const GOSoundProvider *soundProvider,
int samplerTaskId,
unsigned audioGroup,
Expand All @@ -98,7 +98,14 @@ class GOSoundEngine {
bool isRelease,
uint64_t *pStartTimeSamples);
void CreateReleaseSampler(GOSoundSampler *sampler);
void SwitchAttackSampler(GOSoundSampler *sampler);

/**
* Creates a new sampler with decay of current loop.
* Switch this sampler to the new attack.
* It is used when a wave tremulant is switched on or off.
* @param pSampler current playing sampler for switching to a new attack
*/
void SwitchToAnotherAttack(GOSoundSampler *pSampler);
float GetRandomFactor();
unsigned GetBufferSizeFor(unsigned outputIndex, unsigned n_frames);

Expand Down Expand Up @@ -135,7 +142,7 @@ class GOSoundEngine {
uint64_t prevEventTime,
bool isRelease = false,
uint64_t *pStartTimeSamples = nullptr) {
return StartTaskSample(
return CreateTaskSample(
pipeProvider,
windchestN,
audioGroup,
Expand All @@ -150,7 +157,7 @@ class GOSoundEngine {
const GOSoundProvider *tremProvider,
unsigned tremulantN,
uint64_t prevEventTime) {
return StartTaskSample(
return CreateTaskSample(
tremProvider, -tremulantN, 0, 0x7f, 0, prevEventTime, false, nullptr);
}

Expand Down
29 changes: 13 additions & 16 deletions src/grandorgue/sound/GOSoundProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,23 +192,20 @@ const GOSoundAudioSection *GOSoundProvider::GetAttack(

for (unsigned i = 0; i < m_Attack.size(); i++) {
const unsigned idx = (i + x) % m_Attack.size();

if (
m_AttackInfo[idx].m_WaveTremulantStateFor != BOOL3_DEFAULT
&& m_AttackInfo[idx].m_WaveTremulantStateFor
!= to_bool3(m_IsWaveTremulantActive))
continue;
if (m_AttackInfo[idx].min_attack_velocity > velocity)
continue;
if (m_AttackInfo[idx].max_released_time < releasedDurationMs)
continue;
if (best_match == -1)
best_match = idx;
else if (
m_AttackInfo[best_match].min_attack_velocity
< m_AttackInfo[idx].min_attack_velocity
&& m_AttackInfo[best_match].max_released_time
> m_AttackInfo[idx].max_released_time)
best_match = idx;
IsWaveTremulantStateSuitable(m_AttackInfo[idx].m_WaveTremulantStateFor)
&& m_AttackInfo[idx].min_attack_velocity <= velocity
&& m_AttackInfo[idx].max_released_time >= releasedDurationMs) {
if (best_match == -1)
best_match = idx;
else if (
m_AttackInfo[best_match].min_attack_velocity
< m_AttackInfo[idx].min_attack_velocity
&& m_AttackInfo[best_match].max_released_time
> m_AttackInfo[idx].max_released_time)
best_match = idx;
}
}

if (best_match != -1)
Expand Down
6 changes: 6 additions & 0 deletions src/grandorgue/sound/GOSoundProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,16 @@ class GOSoundProvider : public GOStatisticCallback {
virtual bool LoadCache(GOMemoryPool &pool, GOCache &cache);
virtual bool SaveCache(GOCacheWriter &cache) const;

bool IsWaveTremulant() const { return m_IsWaveTremulantActive; }
void SetWaveTremulant(bool isActive) { m_IsWaveTremulantActive = isActive; }

void SetVelocityParameter(float min_volume, float max_volume);

bool IsWaveTremulantStateSuitable(GOBool3 waveTremulantStateFor) const {
return to_bool(waveTremulantStateFor, m_IsWaveTremulantActive)
== m_IsWaveTremulantActive;
}

const GOSoundAudioSection *GetAttack(
unsigned velocity, unsigned releasedDurationMs) const;
const GOSoundAudioSection *GetRelease(
Expand Down
1 change: 1 addition & 0 deletions src/grandorgue/sound/GOSoundSampler.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct GOSoundSampler {
/* current index of the current block into this sample */
volatile unsigned long stop;
volatile unsigned long new_attack;
GOBool3 m_WaveTremulantStateFor;
bool is_release;
unsigned drop_counter;
};
Expand Down

0 comments on commit 530e239

Please sign in to comment.