public static QNT_Duration DurationFromBeatSnap(uint beatSnap) { QNT_Duration duration = Constants.QuarterNoteDuration; //Since we use PPQN, anything a quarter note or smaller is a division of the time if (beatSnap >= 4) { duration.tick = (UInt64)(duration.tick / (beatSnap / 4.0f)); } //Otherwise, it is a multiplication else { duration.tick = (UInt64)((4.0f / beatSnap) * duration.tick); } return(duration); }
void ClickTrackPCMReaderCallback(float[] data) { CopyContext ctx; ctx.bufferData = data; ctx.bufferChannels = 2; ctx.bufferFreq = sampleRate; ctx.playbackSpeed = 1.0f; ctx.outputValue = 0.0f; ctx.volume = volume; for (int dataIndex = 0; dataIndex < data.Length / 2; dataIndex++) { ctx.index = dataIndex; QNT_Timestamp currentTick = timeline.ShiftTick(new QNT_Timestamp(0), (float)((dataIndex) / (float)sampleRate)); TempoChange currentTempo = timeline.GetTempoForTime(currentTick); QNT_Duration timeSignatureDuration = new QNT_Duration(Constants.PulsesPerWholeNote / currentTempo.timeSignature.Denominator); GenerateClickTrackSamples(ctx, currentTick, currentTempo, timeSignatureDuration); } }
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; } } }
void OnAudioFilterRead(float[] bufferData, int bufferChannels) { CopyContext ctx; ctx.bufferData = bufferData; ctx.bufferChannels = bufferChannels; ctx.bufferFreq = sampleRate; ctx.playbackSpeed = speed; ctx.outputValue = 0.0f; if (clearHitsounds) { hitsoundEvents.Clear(); hitSoundEnd = new QNT_Timestamp(0); clearHitsounds = false; } if (clearClicksounds) { clickTrackEvents.Clear(); clearClicksounds = false; } if (newPreviewHitsoundEvents != null) { List <HitsoundEvent> newPreview = newPreviewHitsoundEvents; newPreviewHitsoundEvents = null; previewHitsoundEvents.Clear(); previewHitsoundEvents = newPreview; } if (playPreview) { int dataIndex = 0; while (dataIndex < bufferData.Length / bufferChannels && (preview.currentSample < currentPreviewSongSampleEnd) && (preview.scaledCurrentSample) < song.samples.Length) { ctx.index = dataIndex; ctx.volume = volume; ctx.playbackSpeed = speed; preview.CopySampleIntoBuffer(ctx); PlayHitsounds(ctx, previewHitsoundEvents); ++dataIndex; } if (preview.currentSample >= currentPreviewSongSampleEnd || (preview.scaledCurrentSample) >= song.samples.Length) { playPreview = false; } } if (paused || (song.scaledCurrentSample) > song.samples.Length) { hitsoundEvents.Clear(); //Continue to flush the hitsounds if (!playPreview && previewHitsoundEvents.Count > 0) { int dataIndex = 0; while (dataIndex < bufferData.Length / bufferChannels) { ctx.index = dataIndex; ctx.volume = hitSoundVolume; PlayHitsounds(ctx, previewHitsoundEvents); ++dataIndex; } } return; } if (song != null) { QNT_Timestamp timeStart = timeline.ShiftTick(new QNT_Timestamp(0), (float)song.currentTime); QNT_Timestamp timeEnd = timeline.ShiftTick(new QNT_Timestamp(0), (float)(song.currentTime + (bufferData.Length / bufferChannels) / (float)song.frequency * (song.frequency / (float)sampleRate))); if (timeEnd > hitSoundEnd) { hitSoundEnd = timeStart + Constants.QuarterNoteDuration; AddHitsoundEvents(hitsoundEvents, timeStart, hitSoundEnd); } } for (int dataIndex = 0; dataIndex < bufferData.Length / bufferChannels; dataIndex++) { ctx.volume = volume; ctx.index = dataIndex; ctx.playbackSpeed = speed; song.CopySampleIntoBuffer(ctx); if (songExtra != null) { songExtra.CopySampleIntoBuffer(ctx); } //Play hitsounds (reverse iterate so we can remove) ctx.volume = hitSoundVolume; PlayHitsounds(ctx, hitsoundEvents); ctx.playbackSpeed = speed; if (leftSustain != null) { ctx.volume = leftSustainVolume; leftSustain.CopySampleIntoBuffer(ctx); } if (rightSustain != null) { ctx.volume = rightSustainVolume; rightSustain.CopySampleIntoBuffer(ctx); } QNT_Timestamp currentTick = timeline.ShiftTick(new QNT_Timestamp(0), (float)GetTimeFromCurrentSample()); TempoChange currentTempo = timeline.GetTempoForTime(currentTick); QNT_Duration timeSignatureDuration = new QNT_Duration(Constants.PulsesPerWholeNote / currentTempo.timeSignature.Denominator); if (playMetronome) { if (GetTimeFromCurrentSample() > nextMetronomeTick) { metronomeSamplesLeft = (int)(sampleRate * metronomeTickLength); nextMetronomeTick = 0; } if (nextMetronomeTick == 0) { QNT_Timestamp nextBeat = timeline.GetClosestBeatSnapped(timeline.ShiftTick(new QNT_Timestamp(0), (float)GetTimeFromCurrentSample()) + timeSignatureDuration, currentTempo.timeSignature.Denominator); nextMetronomeTick = timeline.TimestampToSeconds(nextBeat); } } //Metronome if (metronomeSamplesLeft > 0) { const uint MetronomeTickFreq = 817; const uint BigMetronomeTickFreq = 1024; QNT_Timestamp tempoStart = currentTempo.time; uint tickFreq = 0; UInt64 currentBeatInBar = ((currentTick.tick - tempoStart.tick) / timeSignatureDuration.tick) % currentTempo.timeSignature.Numerator; if (currentBeatInBar == 0) { tickFreq = BigMetronomeTickFreq; } else { tickFreq = MetronomeTickFreq; } for (int c = 0; c < bufferChannels; ++c) { int totalMetroSamples = (int)(sampleRate * metronomeTickLength); float metro = Mathf.Sin(Mathf.PI * 2 * tickFreq * ((totalMetroSamples - metronomeSamplesLeft) / (float)totalMetroSamples) * metronomeTickLength) * 0.33f; bufferData[dataIndex * bufferChannels + c] = Mathf.Clamp(bufferData[dataIndex * bufferChannels + c] + metro, -1.0f, 1.0f); } metronomeSamplesLeft -= 1; } if (playClickTrack && currentTick < clickTrackEndTime) { ctx.volume = volume; ctx.index = dataIndex; ctx.playbackSpeed = 1.0f; GenerateClickTrackSamples(ctx, currentTick, currentTempo, timeSignatureDuration); } } }
public static QNT_Timestamp GetSnappedValue(QNT_Timestamp time, uint beatSnap) { QNT_Duration snapValue = Constants.DurationFromBeatSnap(beatSnap); return((time / snapValue) * snapValue); }
public int CompareTo(QNT_Duration other) { return(tick.CompareTo(other.tick)); }
public static QNT_Duration GetSnappedValue(QNT_Duration duration, uint beatSnap) { QNT_Duration snapValue = Constants.DurationFromBeatSnap(beatSnap); return((duration / snapValue.tick) * snapValue.tick); }