예제 #1
0
        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);
                }
            }
        }
예제 #2
0
        void AddHitsoundEvents(List <HitsoundEvent> events, QNT_Timestamp start, QNT_Timestamp end)
        {
            if (Timeline.orderedNotes.Count == 0)
            {
                return;
            }

            if (start > end)
            {
                QNT_Timestamp temp = start;
                start = end;
                end   = temp;
            }

            float startTime = timeline.TimestampToSeconds(start);

            foreach (Target t in new NoteEnumerator(start, end))
            {
                TargetData data = t.data;
                if (data.time < start)
                {
                    continue;
                }
                if (data.time > end)
                {
                    return;
                }

                bool added = false;
                foreach (HitsoundEvent ev in events)
                {
                    if (ev.ID == data.ID)
                    {
                        added = true;
                        break;
                    }
                }

                if (!added)
                {
                    HitsoundEvent ev;
                    ev.ID            = data.ID;
                    ev.currentSample = 0;
                    ev.waitSamples   = (UInt64)((timeline.TimestampToSeconds(data.time) - startTime) * sampleRate) << ClipData.PrecisionShift;
                    ev.sound         = kick;
                    ev.time          = data.time;
                    ev.pan           = (data.x / 7.15f);
                    ev.volume        = 1.0f;
                    ev.xPos          = data.x;

                    switch (data.velocity)
                    {
                    case TargetVelocity.Standard:
                        ev.sound = kick;
                        break;

                    case TargetVelocity.Percussion:
                        ev.sound = percussion;
                        break;

                    case TargetVelocity.Snare:
                        ev.sound = snare;
                        break;

                    case TargetVelocity.Chain:
                        ev.sound = chainNote;
                        break;

                    case TargetVelocity.ChainStart:
                        ev.sound = chainStart;
                        break;

                    case TargetVelocity.Melee:
                        ev.sound = melee;
                        break;

                    case TargetVelocity.Mine:
                        ev.sound = mine;
                        break;

                    default:
                        continue;
                    }

                    //Only one hitsound at a time per point in time
                    for (int j = events.Count - 1; j >= 0; j--)
                    {
                        if (events[j].sound == ev.sound)
                        {
                            if (events[j].waitSamples == ev.waitSamples)
                            {
                                events.RemoveAt(j);
                            }
                        }
                    }

                    events.Add(ev);
                }
            }
        }
예제 #3
0
 public void PlayClickTrack(QNT_Timestamp endTime)
 {
     playClickTrack     = true;
     clickTrackEndTime  = endTime;
     clickTrackNextTick = 0;
 }
예제 #4
0
        public static QNT_Timestamp GetSnappedValue(QNT_Timestamp time, uint beatSnap)
        {
            QNT_Duration snapValue = Constants.DurationFromBeatSnap(beatSnap);

            return((time / snapValue) * snapValue);
        }
예제 #5
0
        public static List <float> Detect(ClipData src, Timeline timeline, QNT_Timestamp start, QNT_Timestamp end)
        {
            for (int i = 0; i < bpmMatchDatas.Length; i++)
            {
                bpmMatchDatas[i].match = 0f;
            }

            if (start == end)
            {
                return(new List <float>());
            }

            if (end < start)
            {
                QNT_Timestamp temp = start;
                start = end;
                end   = temp;
            }

            float startTime = timeline.TimestampToSeconds(start);
            float endTime   = timeline.TimestampToSeconds(end);

            int  channels   = src.channels;
            int  frequency  = src.frequency;
            uint numSamples = (uint)(frequency * (endTime - startTime) * channels);

            int splitFrameSize = BASE_SPLIT_SAMPLE_SIZE;

            var volumeArr  = new float[Mathf.CeilToInt((float)src.samples.Length / (float)splitFrameSize)];
            int powerIndex = 0;

            // Sample data analysis start
            for (int sampleIndex = 0; sampleIndex < src.samples.Length; sampleIndex += splitFrameSize)
            {
                float sum = 0f;
                for (int frameIndex = sampleIndex; frameIndex < sampleIndex + splitFrameSize; frameIndex++)
                {
                    if (src.samples.Length <= frameIndex)
                    {
                        break;
                    }
                    // Use the absolute value, because left and right value is -1 to 1
                    float absValue = Mathf.Abs(src.samples[frameIndex]);
                    if (absValue > 1f)
                    {
                        continue;
                    }

                    // Calculate the amplitude square sum
                    sum += (absValue * absValue);
                }

                // Set volume value
                volumeArr[powerIndex] = Mathf.Sqrt(sum / splitFrameSize);
                powerIndex++;
            }

            // Representing a volume value from 0 to 1
            float maxVolume = volumeArr.Max();

            for (int i = 0; i < volumeArr.Length; i++)
            {
                volumeArr[i] = volumeArr[i] / maxVolume;
            }

            // Create volume diff list
            var diffList = new List <float>();

            for (int i = 1; i < volumeArr.Length; i++)
            {
                diffList.Add(Mathf.Max(volumeArr[i] - volumeArr[i - 1], 0f));
            }

            // Calculate the degree of coincidence in each BPM
            int   index          = 0;
            float splitFrequency = (float)frequency / (float)splitFrameSize;

            for (int bpm = MIN_BPM; bpm <= MAX_BPM; bpm++)
            {
                float sinMatch = 0f;
                float cosMatch = 0f;
                float bps      = (float)bpm / 60f;

                if (diffList.Count > 0)
                {
                    for (int i = 0; i < diffList.Count; i++)
                    {
                        sinMatch += (diffList[i] * Mathf.Cos(i * 2f * Mathf.PI * bps / splitFrequency));
                        cosMatch += (diffList[i] * Mathf.Sin(i * 2f * Mathf.PI * bps / splitFrequency));
                    }

                    sinMatch *= (1f / (float)diffList.Count);
                    cosMatch *= (1f / (float)diffList.Count);
                }

                float match = Mathf.Sqrt((sinMatch * sinMatch) + (cosMatch * cosMatch));

                bpmMatchDatas[index].bpm   = bpm;
                bpmMatchDatas[index].match = match;
                index++;
            }

            // Returns a high degree of coincidence BPM
            int          matchIndex = Array.FindIndex(bpmMatchDatas, x => x.match == bpmMatchDatas.Max(y => y.match));
            var          sort       = bpmMatchDatas.OrderBy(x => x.match).Reverse().ToList();
            List <float> results    = new List <float>();

            for (int i = 0; i < 10; ++i)
            {
                results.Add(sort[i].bpm);
            }

            return(results);
        }
예제 #6
0
 public int CompareTo(QNT_Timestamp other)
 {
     return(tick.CompareTo(other.tick));
 }