Esempio n. 1
0
GO_SAMPLER* GOSoundEngine::StartSample(const GOSoundProvider* pipe, int sampler_group_id, unsigned audio_group, unsigned velocity, unsigned delay, uint64_t last_stop)
{
	unsigned delay_samples = (delay * m_SampleRate) / (1000);
	uint64_t start_time = m_CurrentTime + delay_samples;
	uint64_t released_time = ((start_time - last_stop) * 1000) / m_SampleRate;
	if (released_time > (unsigned)-1)
		released_time = (unsigned)-1;

	const GOAudioSection* attack = pipe->GetAttack(velocity, released_time);
	if (!attack || attack->GetChannels() == 0)
		return NULL;
	GO_SAMPLER* sampler = m_SamplerPool.GetSampler();
	if (sampler)
	{
		sampler->pipe = pipe;
		sampler->velocity = velocity;
		attack->InitStream
			(&m_ResamplerCoefs
			,&sampler->stream
			,GetRandomFactor() * pipe->GetTuning() / (float)m_SampleRate
			);
		const float playback_gain = pipe->GetGain() * attack->GetNormGain();
		sampler->fader.NewConstant(playback_gain);
		sampler->delay = delay_samples;
		sampler->time = start_time;
		sampler->fader.SetVelocityVolume(sampler->pipe->GetVelocityVolume(sampler->velocity));
		StartSampler(sampler, sampler_group_id, audio_group);
	}
	return sampler;
}
Esempio n. 2
0
/*
 * NotifyHandler - call back routine for notifications
 */
BOOL __export FAR PASCAL NotifyHandler( WORD id, DWORD data )
{
    notify      *ptr;

    if( id == NFY_TASKIN || id == NFY_TASKOUT ) {
        if( GetCurrentTask() == ourTask ) {
            if( id == NFY_TASKIN ) {
                UnPauseSampler();
            } else {
                PauseSampler();
            }
        }
        return( FALSE );
    }
    PauseSampler();

    ptr = getNotifyData( data );

    switch( id ) {
    case NFY_STARTDLL:
        _fmemcpy( &ptr->startdll, (LPVOID) data, sizeof( NFYSTARTDLL ) );
        MyModuleFindHandle( &ptr->me, ptr->startdll.hModule );
        HandleLibLoad( SAMP_CODE_LOAD, ptr->startdll.hModule );
        break;
    case NFY_STARTTASK:
        if( ourTask == NULL ) { // handle spawned tasks
            ourTask = GetCurrentTask();
            MyTaskFindHandle( &ptr->te, ptr->task );
            MyModuleFindHandle( &ptr->me, ptr->te.hModule );
            HandleLibLoad( SAMP_MAIN_LOAD, ptr->te.hModule );
            StartSampler();
        } else {
            MyTaskFindHandle( &ptr->te, ptr->task );
            MyModuleFindHandle( &ptr->me, ptr->te.hModule );
            HandleLibLoad( SAMP_CODE_LOAD, ptr->te.hModule );
        }
        break;
    case NFY_EXITTASK:
        if( GetCurrentTask() == ourTask ) { // handle spawned tasks
            QuitSampler( &TotalTime );
            SharedMemory->TaskEnded = TRUE;
        }
        break;
    }
    UnPauseSampler();
    return( FALSE );

} /* NotifyHandler */
Esempio n. 3
0
void GOSoundEngine::SwitchAttackSampler(GO_SAMPLER* handle)
{
	if (!handle->pipe)
		return;

	unsigned time = 1000;

	const GOSoundProvider* this_pipe = handle->pipe;
	const GOAudioSection* section = this_pipe->GetAttack(handle->velocity, time);
	if (!section)
		return;
	if (handle->is_release)
		return;

	GO_SAMPLER* new_sampler = m_SamplerPool.GetSampler();
	if (new_sampler != NULL)
	{
		*new_sampler = *handle;
		
		handle->pipe = this_pipe;
		handle->time = m_CurrentTime + 1;

		float gain_target = this_pipe->GetGain() * section->GetNormGain();
		unsigned cross_fade_len = this_pipe->GetReleaseCrossfadeLength();
		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->pipe->GetVelocityVolume(new_sampler->velocity));

		StartSampler(new_sampler, new_sampler->sampler_group_id, new_sampler->audio_group_id);
	}
}
Esempio n. 4
0
void GOSoundEngine::CreateReleaseSampler(GO_SAMPLER* handle)
{
	if (!handle->pipe)
		return;

	/* The beloow code creates a new sampler to playback the release, the
	 * following code takes the active sampler for this pipe (which will be
	 * in either the attack or loop section) and sets the fadeout property
	 * which will decay this portion of the pipe. The sampler will
	 * automatically be placed back in the pool when the fade restores to
	 * zero. */
	unsigned cross_fade_len = handle->pipe->GetReleaseCrossfadeLength();
	handle->fader.StartDecay(cross_fade_len, m_SampleRate);
	handle->is_release = true;

	const GOSoundProvider* this_pipe = handle->pipe;
	float vol = (handle->sampler_group_id < 0) ? 1.0f : m_Windchests[handle->sampler_group_id]->GetWindchestVolume();

	// FIXME: this is wrong... the intention is to not create a release for a
	// sample being played back with zero amplitude but this is a comparison
	// against a double. We should test against a minimum level.
	if (vol)
	{
		const GOAudioSection* release_section = this_pipe->GetRelease(&handle->stream, ((double)(m_CurrentTime - handle->time)) / m_SampleRate);
		if (!release_section)
			return;

		GO_SAMPLER* new_sampler = m_SamplerPool.GetSampler();
		if (new_sampler != NULL)
		{
			new_sampler->pipe = this_pipe;
			new_sampler->time = m_CurrentTime + 1;
			new_sampler->velocity = handle->velocity;

			unsigned gain_decay_length = 0;
			float gain_target = this_pipe->GetGain() * release_section->GetNormGain();
			const bool not_a_tremulant = (handle->sampler_group_id >= 0);
			if (not_a_tremulant)
			{
				/* Because this sampler is about to be moved to a detached
				 * windchest, we must apply the gain of the existing windchest
				 * to the gain target for this fader - otherwise the playback
				 * volume on the detached chest will not match the volume on
				 * the existing chest. */
				gain_target *= vol;
				if (m_ScaledReleases)
				{
					/* Note: "time" is in milliseconds. */
					int time = ((m_CurrentTime - handle->time) * 1000) / m_SampleRate;
					/* TODO: below code should be replaced by a more accurate model of the attack to get a better estimate of the amplitude when playing very short notes
					* estimating attack duration from pipe midi pitch */
					unsigned midikey_frequency = this_pipe->GetMidiKeyNumber();
					/* if MidiKeyNumber is not within an organ 64 feet to 1 foot pipes, we assume average pipe (MIDI = 60)*/
					if (midikey_frequency >133 || midikey_frequency == 0 )
						midikey_frequency = 60;
					/* attack duration is assumed 50 ms above MIDI 96, 800 ms below MIDI 24 and linear in between */	
					float attack_duration = 50.0f; 
					if (midikey_frequency < 96 )
					{
						if (midikey_frequency < 24)
							attack_duration = 500.0f;
						else
							attack_duration = 500.0f + ( ( 24.0f - (float)midikey_frequency ) * 6.25f );
					}
					/* calculate gain (gain_target) to apply to tail amplitude in function of when the note is released during the attack */ 
					if (time < (int)attack_duration)
					{
						float attack_index = (float)time / attack_duration;
						float gain_delta = ( 0.2f + ( 0.8f * ( 2.0f * attack_index - (attack_index * attack_index) )));
						gain_target *= gain_delta;
					}
					/* calculate the volume decay to be applied to the release to take into account the fact reverb is not completely formed during staccato
					* time to full reverb is estimated in function of release length
					* for an organ with a release length of 5 seconds or more, time_to_full_reverb is around 350 ms 
					* for an organ with a release length of 1 second or less, time_to_full_reverb is around 100 ms 
					* time_to_full_reverb is linear in between */
					int time_to_full_reverb = ((60 * release_section->GetLength()) / release_section->GetSampleRate()) + 40;
					if (time_to_full_reverb > 350 ) time_to_full_reverb = 350;
					if (time_to_full_reverb < 100 ) time_to_full_reverb = 100;
					if (time < time_to_full_reverb)
					{
						/* in function of note duration, fading happens between: 
						* 200 ms and 6 s for release with little reverberation e.g. short release 
						* 700 ms and 6 s for release with large reverberation e.g. long release */ 
						gain_decay_length = time_to_full_reverb + 6000 * time / time_to_full_reverb;
					}
				}
			}
			unsigned cross_fade_len = this_pipe->GetReleaseCrossfadeLength();
			new_sampler->fader.NewAttacking(gain_target, cross_fade_len, m_SampleRate);

			if (m_ReleaseLength > 0)
			{
				if ( m_ReleaseLength < gain_decay_length || gain_decay_length == 0 )
					gain_decay_length = m_ReleaseLength;
			}

			if (gain_decay_length > 0)
				new_sampler->fader.StartDecay(gain_decay_length, m_SampleRate);

			if (m_ReleaseAlignmentEnabled && release_section->SupportsStreamAlignment())
			{
				release_section->InitAlignedStream(&new_sampler->stream, &handle->stream);
			}
			else
			{
				release_section->InitStream(&m_ResamplerCoefs, &new_sampler->stream, this_pipe->GetTuning() / (float)m_SampleRate);
			}
			new_sampler->is_release = true;

			int windchest_index;
			if (not_a_tremulant)
			{
				/* detached releases are enabled and the pipe was on a regular
				 * windchest. Play the release on the detached windchest */
				windchest_index = 0;
			}
			else
			{
				/* detached releases are disabled (or this isn't really a pipe)
				 * so put the release on the same windchest as the pipe (which
				 * means it will still be affected by tremulants - yuck). */
				windchest_index = handle->sampler_group_id;
			}
			new_sampler->fader.SetVelocityVolume(new_sampler->pipe->GetVelocityVolume(new_sampler->velocity));
			StartSampler(new_sampler, windchest_index, handle->audio_group_id);
			handle->time = m_CurrentTime;
		}

	}
}