#pragma once #include "Pattern/Pattern.h" #include "Pattern/PatternInstance.h" #include "Singleton.h" #include "Pattern/MidiPattern.h" #include "Pattern/SamplePattern.h" #include "Midi/Time/TimePos.h" #include "Midi/Time/TimeSig.h" DECLARE_MULTICAST_DELEGATE_OneParam(FPatternDelegate, FPattern*) DECLARE_MULTICAST_DELEGATE_OneParam(FPatternInstanceDelegate, FPatternInstance*) class FPatternSelector : public TSingleton { public: void SelectPattern(FPattern* Pattern) { Range = TRange(AudioFrame(), Pattern->GetLength()); if (SelectedPattern == Pattern) return; if (SelectedPattern) OnPatternDeselected.Broadcast(SelectedPattern); SelectedPattern = Pattern; OnPatternSelected.Broadcast(SelectedPattern); } void UnSelectPattern() { if (SelectedPattern) OnPatternDeselected.Broadcast(SelectedPattern); SelectedPattern = nullptr; Range = TRange(AudioFrame(), AudioFrame()); } void SelectPatternInstance(FPatternInstance* PatternInstance) { FPattern* Pattern = PatternInstance->GetOwner(); SelectPattern(Pattern); Range = PatternInstance->TimeRange; } FPattern* GetSelectedPattern() const { return SelectedPattern; } TRange GetRange() const { return Range; } FPatternDelegate OnPatternSelected; FPatternDelegate OnPatternDeselected; private: TRange Range; FPattern* SelectedPattern = nullptr; }; class FMidiSequencer : public TSingleton { public: FMidiPattern* NewMidiPattern(); FSamplePattern* NewSamplePattern(FSampler* InSampler); void DeletePattern(FPattern* Pattern); FPattern* FindPattern(uint32 ID); void Process(float SampleRate, uint32 FrameNum); void UpdateTime(); const TMap>& GetPattern() const { return PatternTypeMap; } const TArray& GetPattern(EPatternType Type) { return PatternTypeMap.FindOrAdd(Type); } void ForeachPatternInstance(TFunctionRef Func); FPatternDelegate OnNewPattern; FPatternDelegate OnDeletePattern; FPatternInstanceDelegate OnCreatePatternInstance; FPatternInstanceDelegate OnDeletePatternInstance; double GetTickPos() const { return FramePos.load().Ticks(); } double GetSamplePos() const { return FramePos.load(); } void SetTicksPerQuarter(int32 InTicksPerQuarter) { TicksPerQuarter = FMath::Max(48, InTicksPerQuarter); } int32 GetTicksPerQuarter() const { return TicksPerQuarter; } MidiTick GetBeat() const { return GetTicksPerQuarter() * TimeSig.GetNumerator(); } MidiTick GetBar() const { return GetBeat() * TimeSig.GetDenominator(); } static float FramesPerTick(); // MidiTick转换为Frame的比例 FTimeSig TimeSig = FTimeSig(4, 4); float BPM = 160.f; void TogglePlay() { SetPlaying(!Playing); } void SetPlaying(bool bPlaying); std::atomic FramePos; private: void RegisterPattern(FPattern* InPattern); int32 TicksPerQuarter = 480; // Ticks Per Quarter-Note std::atomic_bool Playing = false; uint32 IDCounter = 0; TArray Patterns; TMap> PatternTypeMap; };