Implement midi player using Windows API
This commit is contained in:
85
src/midiPlayer.cpp
Normal file
85
src/midiPlayer.cpp
Normal 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
38
src/midiPlayer.h
Normal 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
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
Reference in New Issue
Block a user