void GenerateClickTrackSamples(CopyContext ctx, QNT_Timestamp currentTick, TempoChange currentTempo, QNT_Duration timeSignatureDuration) { QNT_Timestamp tempoStart = currentTempo.time; QNT_Timestamp nextBeat = timeline.GetClosestBeatSnapped(currentTick, currentTempo.timeSignature.Denominator); UInt64 currentBeatInBar = ((currentTick.tick - tempoStart.tick) / timeSignatureDuration.tick) % currentTempo.timeSignature.Numerator; if (currentBeatInBar != 1) { TryAddClickEvent(currentTick, nextBeat, hihat_hit); } else { TryAddClickEvent(currentTick, nextBeat, hihat_hit3); } TryAddClickEvent(currentTick, nextBeat + Constants.SixteenthNoteDuration, hihat_hit2); TryAddClickEvent(currentTick, nextBeat + Constants.EighthNoteDuration, hihat_open); for (int i = clickTrackEvents.Count - 1; i >= 0; i--) { ClickTrackEvent ev = clickTrackEvents[i]; ev.clip.currentSample = ev.currentSample; ev.clip.CopySampleIntoBuffer(ctx); ev.currentSample = ev.clip.currentSample; if (ev.clip.scaledCurrentSample > ev.clip.samples.Length) { clickTrackEvents.RemoveAt(i); } else { clickTrackEvents[i] = ev; } } }
public void CopySampleIntoBuffer(CopyContext ctx) { UInt64 shiftNum = ((UInt64)1 << PrecisionShift); UInt64 speed = (UInt64)frequency * shiftNum / (UInt64)ctx.bufferFreq; if (ctx.playbackSpeed != 1.0f) { speed = (UInt64)(speed * ctx.playbackSpeed); } float panClamp = Mathf.Clamp(pan, -1.0f, 1.0f); int clipChannel = 0; int sourceChannel = 0; float maxValue = 0.0f; while (sourceChannel < ctx.bufferChannels) { float panAmount = 1.0f; if (sourceChannel == 0) { panAmount = Math.Min(1.0f - panClamp, 1.0f); } else if (sourceChannel == 1) { panAmount = Math.Min(1.0f + panClamp, 1.0f); } float value = 0.0f; long samplePos = (uint)(currentSample >> PrecisionShift) * channels + clipChannel; if (samplePos < samples.Length) { value = samples[samplePos]; } float duckValue = Mathf.Clamp(1.0f - duckVolume, 0.0f, 1.0f); maxValue = Math.Max(value * ctx.volume * duckValue, maxValue); ctx.bufferData[ctx.index * ctx.bufferChannels + sourceChannel] += value * ctx.volume * panAmount * duckValue; sourceChannel++; clipChannel++; if (clipChannel >= channels) { clipChannel = 0; } } currentSample += speed; ctx.outputValue = Math.Abs(maxValue); }
void PlayHitsounds(CopyContext ctx, List <HitsoundEvent> events) { kick.duckVolume = 0.0f; snare.duckVolume = 0.0f; percussion.duckVolume = 0.0f; chainStart.duckVolume = 0.0f; chainNote.duckVolume = 0.0f; melee.duckVolume = 0.0f; mine.duckVolume = 0.0f; float oldSpeed = ctx.playbackSpeed; UInt64 waitSpeed = (UInt64)1 << ClipData.PrecisionShift; if (oldSpeed != 1.0f) { waitSpeed = (UInt64)(waitSpeed * oldSpeed); } //Always play hitsounds at full speed ctx.playbackSpeed = 1.0f; for (int i = events.Count - 1; i >= 0; i--) { HitsoundEvent ev = events[i]; bool valid = true; if (ev.waitSamples > 0) { if (waitSpeed > ev.waitSamples) { ev.waitSamples = 0; } else { ev.waitSamples -= waitSpeed; } } else { ctx.volume = hitSoundVolume * ev.volume; ev.sound.currentSample = ev.currentSample; ev.sound.pan = ev.pan; ev.sound.CopySampleIntoBuffer(ctx); ev.sound.duckVolume = Mathf.Clamp(ev.sound.duckVolume + 0.25f, 0.0f, 1.0f); if (ev.sound.scaledCurrentSample > ev.sound.samples.Length) { events.RemoveAt(i); valid = false; } else { ev.currentSample = ev.sound.currentSample; } } //Update the pan: ev.pan = (ev.xPos - mainCameraX) / 7.15f; if (valid) { events[i] = ev; } } ctx.playbackSpeed = 1.0f; }