-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for .mid files in songs folder.
- Loading branch information
Showing
19 changed files
with
11,290 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
#include "MidiProcessing.h" | ||
|
||
// Function to calculate the optimal shift for notes | ||
int calculateOptimalShift(const std::vector<uint8_t>& notes) { | ||
ShiftParameters parameters; | ||
int bestShift = 0; | ||
double bestScore = -std::numeric_limits<double>::max(); | ||
|
||
for (int shift = -127; shift <= 127; shift++) { | ||
int playableNotes = 0; | ||
for (uint8_t note : notes) { | ||
if (note + shift >= 40 && note + shift <= 79) { | ||
playableNotes++; | ||
} | ||
} | ||
|
||
// Calculate normalized score | ||
double normalizedPlayableScore = static_cast<double>(playableNotes) / notes.size(); | ||
double score = normalizedPlayableScore * parameters.playableNoteWeight; | ||
|
||
// Apply bonuses for preferred shifts | ||
if (shift == 0) { | ||
score += parameters.noShiftBonus; | ||
} | ||
else if (shift % 12 == 0) { | ||
score += parameters.octaveShiftBonus; | ||
} | ||
|
||
// Penalize larger shifts | ||
score -= std::abs(shift) * parameters.maxShiftPenalty / 127.0; | ||
|
||
if (score > bestScore || (score == bestScore && std::abs(shift) < std::abs(bestShift))) { | ||
bestScore = score; | ||
bestShift = shift; | ||
} | ||
} | ||
|
||
return bestShift; | ||
} | ||
|
||
std::string convertMidiToText(const std::filesystem::path& midiFilePath) { | ||
smf::MidiFile midifile; | ||
if (!midifile.read(midiFilePath.string())) { | ||
throw std::runtime_error("Error reading MIDI file: " + midiFilePath.string()); | ||
} | ||
|
||
midifile.doTimeAnalysis(); | ||
midifile.linkNotePairs(); | ||
|
||
std::vector<SoundEvent> song; | ||
std::vector<uint8_t> allNotes; | ||
|
||
// Process MIDI events | ||
for (int track = 0; track < midifile.getTrackCount(); track++) { | ||
for (int event = 0; event < midifile[track].size(); event++) { | ||
if (midifile[track][event].isNoteOn()) { | ||
int timestamp = static_cast<int>(midifile[track][event].seconds * 1000); | ||
uint8_t noteNumber = midifile[track][event][1]; | ||
|
||
if (!song.empty() && timestamp <= song.back().timestamp + 30) { | ||
song.back().notes.push_back(noteNumber); | ||
} | ||
else { | ||
song.emplace_back(timestamp, noteNumber); | ||
} | ||
allNotes.push_back(noteNumber); | ||
} | ||
} | ||
} | ||
|
||
if (song.empty()) { | ||
throw std::runtime_error("No notes found in MIDI file: " + midiFilePath.string()); | ||
} | ||
|
||
// Calculate optimal shift | ||
int optimalShift = calculateOptimalShift(allNotes); | ||
|
||
// Build the output string | ||
std::stringstream output; | ||
int offsetNotes = song[0].timestamp; | ||
|
||
for (const auto& event : song) { | ||
std::vector<int> shiftedNotes; | ||
for (uint8_t note : event.notes) { | ||
int shiftedNote = note + optimalShift; | ||
if (shiftedNote >= 40 && shiftedNote <= 79) { | ||
shiftedNotes.push_back(shiftedNote); | ||
} | ||
} | ||
|
||
if (!shiftedNotes.empty()) { | ||
// Sort notes in ascending order | ||
std::sort(shiftedNotes.begin(), shiftedNotes.end()); | ||
|
||
output << (event.timestamp - offsetNotes); | ||
for (int note : shiftedNotes) { | ||
output << " " << note; | ||
} | ||
output << "\n"; | ||
} | ||
} | ||
|
||
return output.str(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#pragma once | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <string> | ||
#include <filesystem> | ||
#include <sstream> | ||
#include <algorithm> | ||
#include <cmath> | ||
#include "Midifile/MidiFile.h" | ||
|
||
|
||
struct SoundEvent { | ||
int timestamp; | ||
std::vector<uint8_t> notes; | ||
|
||
SoundEvent(int time, uint8_t note) : timestamp(time) { | ||
notes.push_back(note); | ||
} | ||
|
||
SoundEvent(int time, const std::vector<uint8_t>& tempNotes) | ||
: timestamp(time), notes(tempNotes) { | ||
} | ||
}; | ||
|
||
struct ShiftParameters { | ||
double noShiftBonus = 0.11; | ||
double octaveShiftBonus = 0.15; | ||
double maxShiftPenalty = 0.3; | ||
double playableNoteWeight = 2.2; | ||
}; | ||
|
||
int calculateOptimalShift(const std::vector<uint8_t>& notes); | ||
std::string convertMidiToText(const std::filesystem::path& midiFilePath); |
Oops, something went wrong.