diff --git a/src/midiPlayer.cpp b/src/midiPlayer.cpp new file mode 100644 index 0000000..1aa574b --- /dev/null +++ b/src/midiPlayer.cpp @@ -0,0 +1,85 @@ +#include "midiPlayer.h" + +#include +#include + +//-------------------------------------------------------------- +/* Default constructor */ +MidiPlayer::MidiPlayer() +{ + init(); +} +//-------------------------------------------------------------- +/* Constructor */ +MidiPlayer::MidiPlayer(const std::span notes) +{ + init(); + playBuffer(notes); +} +//-------------------------------------------------------------- +/* Set MIDI instrument */ +void MidiPlayer::setInstrument(uint8_t instrument) const +{ + if (instrument > 127) + instrument = 0; // Default to Acoustic Grand Piano if out of range + + const DWORD message = 0xC0 | (instrument << 8); // Program Change + midiOutShortMsg(m_midiOut, message); +} +//-------------------------------------------------------------- +/* Default destructor */ +MidiPlayer::~MidiPlayer() +{ + release(); +} +//-------------------------------------------------------------- +/* Play a MIDI buffer */ +void MidiPlayer::playBuffer(const std::span notes) const +{ + for (const auto note : notes) + { + const auto noteNumber = frequencyToMidiNote(note.frequency); + + // Send Note On message (0x90) with the note number and velocity + DWORD message = 0x90 | (noteNumber << 8) | (m_velocity << 16); // Note On + midiOutShortMsg(m_midiOut, message); + + // Wait for the duration of the note + std::this_thread::sleep_for(note.duration); + + // Send Note Off message (0x80) with the note number + message = 0x80 | (noteNumber << 8); // Note Off + midiOutShortMsg(m_midiOut, message); + } +} +//-------------------------------------------------------------- +/* Initialize MIDI output */ +void MidiPlayer::init() +{ + midiOutOpen(&m_midiOut, // phmo + 0, // uDeviceID + 0, // dwCallback + 0, // dwInstance + CALLBACK_NULL); // fdwOpen +} +//-------------------------------------------------------------- +/* Release MIDI output */ +void MidiPlayer::release() +{ + midiOutClose(m_midiOut); + m_midiOut = nullptr; +} +//-------------------------------------------------------------- +/* Convert frequency to MIDI note number */ +int MidiPlayer::frequencyToMidiNote(const uint16_t frequency) const +{ + constexpr double A4_FREQUENCY = 440.0; + constexpr double A4_MIDI_NOTE = 69.0; + constexpr double SEMITONES_PER_OCTAVE = 12.0; + + if (frequency == 0) + return 0; // Silent + + return static_cast(std::round(A4_MIDI_NOTE + SEMITONES_PER_OCTAVE * log2(static_cast(frequency) / A4_FREQUENCY))); +} +//-------------------------------------------------------------- diff --git a/src/midiPlayer.h b/src/midiPlayer.h new file mode 100644 index 0000000..7a31460 --- /dev/null +++ b/src/midiPlayer.h @@ -0,0 +1,38 @@ +#pragma once + +#include "pitches/pitches.h" + +#include +#include +#include + +// Include the Windows Multimedia API header for MIDI functions +#include + +//-------------------------------------------------------------- +class MidiPlayer +{ + public: + MidiPlayer(); // Default constructor + virtual ~MidiPlayer(); // Default destructor + MidiPlayer(const MidiPlayer &obj) = delete; // Copy constructor + MidiPlayer(MidiPlayer &&obj) noexcept = delete; // Move constructor + MidiPlayer &operator=(const MidiPlayer &obj) = delete; // Copy assignment operator + MidiPlayer &operator=(MidiPlayer &&obj) noexcept = delete; // Move assignment operator + + explicit MidiPlayer(std::span notes); // Constructor + + void setInstrument(uint8_t instrument) const; // Set MIDI instrument + void playBuffer(std::span notes) const; // Play a MIDI buffer + + protected: + HMIDIOUT m_midiOut = nullptr; + uint16_t m_velocity = 127; // MIDI velocity (0-127) + + private: + void init(); // Initialize MIDI output + void release(); // Release MIDI output + + [[nodiscard]] int frequencyToMidiNote(uint16_t frequency) const; // Convert frequency to MIDI note number +}; +//--------------------------------------------------------------