Implement midi player using Windows API

This commit is contained in:
Sylvain Schneider
2026-02-27 22:52:12 +01:00
parent 8d27ad545d
commit 0e78926833
2 changed files with 123 additions and 0 deletions

85
src/midiPlayer.cpp Normal file
View File

@@ -0,0 +1,85 @@
#include "midiPlayer.h"
#include <cmath>
#include <thread>
//--------------------------------------------------------------
/* Default constructor */
MidiPlayer::MidiPlayer()
{
init();
}
//--------------------------------------------------------------
/* Constructor */
MidiPlayer::MidiPlayer(const std::span<const pitches::Note> 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<const pitches::Note> 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<int>(std::round(A4_MIDI_NOTE + SEMITONES_PER_OCTAVE * log2(static_cast<double>(frequency) / A4_FREQUENCY)));
}
//--------------------------------------------------------------

38
src/midiPlayer.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include "pitches/pitches.h"
#include <cstdint>
#include <span>
#include <windows.h>
// Include the Windows Multimedia API header for MIDI functions
#include <mmsystem.h>
//--------------------------------------------------------------
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<const pitches::Note> notes); // Constructor
void setInstrument(uint8_t instrument) const; // Set MIDI instrument
void playBuffer(std::span<const pitches::Note> 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
};
//--------------------------------------------------------------