Files
UnrealEngine/Engine/Plugins/Runtime/Harmonix/Source/HarmonixMidiTests/Private/MidiTestUtility.cpp
2025-05-18 13:04:45 +08:00

130 lines
5.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MidiTestUtility.h"
namespace Harmonix::Testing::Utility::MidiTestUtility
{
UMidiFile* CreateAndInitializaMidiFile(float FileLengthBars, int32 InNumTracksIncludingConductor, int32 InTimeSigNum, int32 InTimeSigDenom, int32 InTempo, bool PutTextEventOnLastTick)
{
UMidiFile* TheMidiFile = NewObject<UMidiFile>();
//some default values for creating midi events
constexpr int32 DefaultNoteNumber = 60;//C4
constexpr int32 DefaultNoteVelocity = 90;
constexpr uint8 DefaultControllerID = 69; //Hold Pedal
constexpr uint8 DefaultControlValue = 127;
constexpr uint8 DefaultPitchBendValueLSB = 64;
constexpr uint8 DefaultPitchBendValueMSB = 127;
constexpr uint8 DefaultPolyPresValue = 127;
// Set initial tempo and time signature
TheMidiFile->GetSongMaps()->AddTempoInfoPoint(Midi::Constants::BPMToMidiTempo(InTempo), 0);
TheMidiFile->GetSongMaps()->AddTimeSignatureAtBarIncludingCountIn(0, InTimeSigNum, InTimeSigDenom);
// Whenever you change the song maps you need to either:
// a) Add corresponding midi events to the conductor track. Or...
// b) Build the conductor track from scratch.
// (b) is easier...
TheMidiFile->BuildConductorTrack();
// now we can calculate the tick length since the midi file now has a conductor track...
int32 TickLength = TheMidiFile->GetSongMaps()->FractionalBarIncludingCountInToTick(FileLengthBars);
if (TickLength <= 0)
{
// MIDI Files must be at least one tick long. Even the initial tempo and time signature are specified on tick 0!
TickLength = 1;
}
//Add tracks to Midi file according to input argument
for (int32 Track = 1; Track < InNumTracksIncludingConductor; ++Track)
{
//Add midi tracks 1 - InNumTracks to file, Track 0 is the conductor track
FMidiTrack* TheNewTrack = TheMidiFile->AddTrack(FString::Printf(TEXT("TestMidiTrack%i"), Track));
if (TheNewTrack && PutTextEventOnLastTick)
{
uint16 TextIndex = TheNewTrack->AddText(FString::Printf(TEXT("TheEndOfTrack%i"),Track));
FMidiEvent TextEventAtTheEnd(TickLength - 1, FMidiMsg::CreateText(TextIndex, Midi::Constants::GMeta_Text));
TheNewTrack->AddEvent(TextEventAtTheEnd);
}
}
//update song length information
TheMidiFile->ConformToLength(TickLength);
return TheMidiFile;
}
void AddNoteOnNoteOffPairToFile(UMidiFile* InFile, int32 InNoteNumber, int32 InNoteVelocity, int32 InTrackIndex, int32 InChannel, int32 AtTick, int32 DurationTicks)
{
if (InTrackIndex >= InFile->GetTracks().Num() || InTrackIndex <= 0)
{
UE_LOG(LogMIDI, Error, TEXT("AddNoteOnNoteOffPairToFile: Bad Track Index: %d"), InTrackIndex);
return;
}
FMidiTrack* CurrentTrack = InFile->GetTrack(InTrackIndex);
//Note On / Note Off pair
FMidiEvent CurrentNoteOnEvent(AtTick,
FMidiMsg::CreateNoteOn(InChannel, InNoteNumber, InNoteVelocity));
//Note Offs are 1 beat away from Note Ons (ticks per quarter note)
FMidiEvent CurrentNoteOffEvent(AtTick + DurationTicks,
FMidiMsg::CreateNoteOff(InChannel, InNoteNumber));
CurrentTrack->AddEvent(CurrentNoteOnEvent);
CurrentTrack->AddEvent(CurrentNoteOffEvent);
}
void AddCCEventToFile(UMidiFile* InFile, uint8 InControllerID, uint8 InControlValue, int32 InTrackIndex, int32 InChannel, int32 AtTick)
{
if (InTrackIndex >= InFile->GetTracks().Num() || InTrackIndex <= 0)
{
UE_LOG(LogMIDI, Error, TEXT("AddCCEventToFile: Bad Track Index: %d"), InTrackIndex);
return;
}
FMidiTrack* CurrentTrack = InFile->GetTrack(InTrackIndex);
FMidiEvent CurrentCCEvent(AtTick,
FMidiMsg(Midi::Constants::GControl | InChannel, InControllerID, InControlValue));
CurrentTrack->AddEvent(CurrentCCEvent);
}
void AddTextEventToFile(UMidiFile* InFile, FString InText, int32 InTrackIndex, int32 AtTick)
{
if (InTrackIndex >= InFile->GetTracks().Num() || InTrackIndex <= 0)
{
UE_LOG(LogMIDI, Error, TEXT("AddTextEventToFile: Bad Track Index: %d"), InTrackIndex);
return;
}
FMidiTrack* CurrentTrack = InFile->GetTrack(InTrackIndex);
uint16 TextIndex = CurrentTrack->AddText(InText);
FMidiEvent CurrentTextEvent(AtTick, FMidiMsg::CreateText(TextIndex, Midi::Constants::GMeta_Text));
CurrentTrack->AddEvent(CurrentTextEvent);
}
void AddPitchEventToFile(UMidiFile* InFile, uint8 InPitchValueLSB, uint8 InPitchValueMSB, int32 InTrackIndex, int32 InChannel, int32 AtTick)
{
if (InTrackIndex >= InFile->GetTracks().Num() || InTrackIndex <= 0)
{
UE_LOG(LogMIDI, Error, TEXT("AddPitchEventToFile: Bad Track Index: %d"), InTrackIndex);
return;
}
FMidiTrack* CurrentTrack = InFile->GetTrack(InTrackIndex);
FMidiEvent CurrentPitchEvent(AtTick,
FMidiMsg(Midi::Constants::GPitch | InChannel, InPitchValueLSB, InPitchValueMSB));
CurrentTrack->AddEvent(CurrentPitchEvent);
}
void AddPolyPresEventToFile(UMidiFile* InFile, uint8 InNoteNumber, uint8 InPolyPresValue, int32 InTrackIndex, int32 InChannel, int32 AtTick)
{
if (InTrackIndex >= InFile->GetTracks().Num() || InTrackIndex <= 0)
{
UE_LOG(LogMIDI, Error, TEXT("AddPolyPresEventToFile: Bad Track Index: %d"), InTrackIndex);
return;
}
FMidiTrack* CurrentTrack = InFile->GetTrack(InTrackIndex);
FMidiEvent CurrentPolyPresEvent(AtTick,
FMidiMsg(Midi::Constants::GPolyPres | InChannel, InNoteNumber, InPolyPresValue));
CurrentTrack->AddEvent(CurrentPolyPresEvent);
}
}