Last active
May 20, 2018 13:19
-
-
Save ixsiid/bf7b57d1a5637f52ba5a089cac8c03c2 to your computer and use it in GitHub Desktop.
SMF (Standard Midi Format)を解析し、MIDIインターフェースに流し込む(クラス編) ref: https://qiita.com/ixsiid/items/0d45cbeffeacd7739bc9
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
$ amixer cset numid=3 1 | |
numid=3,iface=MIXER,name='PCM Playback Route' | |
; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0 | |
: values=1 | |
$ fluidsynth -a alsa -g 6 /usr/share/sounds/sf2/FluidR3_GM.sf2 | |
FluidSynth version 1.1.6 | |
Copyright (C) 2000-2012 Peter Hanappe and others. | |
Distributed under the LGPL license. | |
SoundFont(R) is a registered trademark of E-mu Systems, Inc. | |
fluidsynth: warning: Failed to pin the sample data to RAM; swapping is possible. | |
fluidsynth: warning: Requested a period size of 64, got 256 instead | |
fluidsynth: warning: Failed to set thread to high priority | |
fluidsynth: warning: Failed to set thread to high priority | |
Type 'help' for help topics. | |
> |
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
$ ./play.o | |
parse: foobar.mid | |
smf_parser library v1.01 | |
Created by Joan Quintana Compte (joanillo) | |
Licensed under GPL v.3 | |
ABSTRACT: | |
--------- | |
SMF: foobar.mid | |
Format type: 1 | |
Time division: 0 (ticks_per_beat) | |
Number of tracks: 18 | |
ticks_per_beat: 480 | |
Time Signature: 4 / 4 | |
Tempo: 185 | |
Number of bars: 104.000000 | |
ticks per beat: 480 | |
notes: 0xfc8d40 | |
tempo 185, tpb 480, k=0.675676 | |
count: 2138 | |
> p | |
> send: 144 37 51 | |
send: 144 56 58 | |
send: 144 59 58 | |
send: 144 63 58 | |
... |
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
#include "RtMidi.h" | |
#include "midi.hpp" | |
#include "smf.hpp" | |
#include <algorithm> | |
#include <cstdlib> | |
#include <iostream> | |
int main() { | |
MidiInterface *port = new MidiInterface("Player"); | |
// 後述のFluidsynthと接続する指定をします。 | |
RtMidiOut *midiout = (RtMidiOut *)port->connect("FLUID", MidiDirection::OUT); | |
if (midiout == NULL) { | |
std::cout << "Not found target port name" << std::endl; | |
exit(1); | |
} | |
// 再生用ファイルは各自で用意 | |
Smf *smf = new Smf("foobar.mid", midiout); | |
// start で再生用スレッドが準備されます。 | |
smf->start(); | |
while (true) { | |
std::cout << "\r> "; | |
fflush(stdout); | |
int c = std::cin.get(); | |
if (c == 'p') { | |
// 再生を開始します | |
smf->play(); | |
} else if (c == 's') { | |
// 一時停止 | |
smf->pause(); | |
} else if (c == 'r') { | |
// 最初から再生する | |
smf->seek(0); | |
} else if (c == 'q' || std::cin.eof()) { | |
break; | |
} | |
} | |
delete midiout; | |
delete port; | |
return 0; | |
} |
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
#include "smf.hpp" | |
#include "RtMidi.h" | |
#include "SmfParser.hpp" | |
#include "midi.hpp" | |
#include <algorithm> | |
#include <chrono> | |
#include <cstdlib> | |
#include <iostream> | |
#include <smf.h> | |
#include <thread> | |
#include <unistd.h> | |
std::ostream &operator<<(std::ostream &Str, const Note ¬e) { | |
if (note.message[0] == 144) { | |
printf("timing %d[msec], note %d on, velocity = %d", note.time, note.message[1], note.message[2]); | |
} else if (note.message[0] == 128) { | |
printf("timing %d[msec], note %d off", note.time, note.message[1]); | |
} else { | |
printf("unknown note; %d %d %d %d", note.time, note.message[0], note.message[1], note.message[2]); | |
} | |
return Str; | |
} | |
bool compare_event(const ch_event &left, const ch_event &right) { | |
if (left.acumulate_time == right.acumulate_time) { | |
if (left.event_type == right.event_type) { | |
return left.par1 < right.par2; | |
} | |
return left.event_type < right.event_type; | |
} | |
return left.acumulate_time < right.acumulate_time; | |
} | |
Smf::Smf(const char *_filepath, RtMidiOut * _midi) { | |
midi = _midi; | |
filepath = _filepath; | |
std::cout << "parse: " << filepath << std::endl; | |
SmfParser smf = SmfParser(filepath); | |
smf.printcredits(); | |
smf.parse(0); | |
smf.abstract(); | |
std::cout << std::endl << "ticks per beat: " << smf.getTicks_per_beat() << std::endl; | |
int noteCount = 0; | |
for (auto x : smf.events) | |
if (x.event_type == 9 || x.event_type == 8) noteCount++; | |
std::sort(smf.events.begin(), smf.events.end(), compare_event); | |
notes = new Note[noteCount]; | |
std::cout << "notes: " << notes << std::endl; | |
int tempo = smf.getBpm(); | |
int tickPerBeat = smf.getTicks_per_beat(); | |
float k = 60000.0f / (tempo * tickPerBeat); | |
std::cout << "tempo " << tempo << ", tpb " << tickPerBeat << ", k=" << k << std::endl; | |
int count = 0; | |
for (auto x : smf.events) { | |
if (x.event_type == 9) { | |
notes[count].time = (int)(x.acumulate_time * k); | |
notes[count].message[0] = 144; | |
notes[count].message[1] = x.par1; | |
notes[count].message[2] = x.par2; | |
count++; | |
} else if (x.event_type == 8) { | |
notes[count].time = (int)(x.acumulate_time * k); | |
notes[count].message[0] = 128; | |
notes[count].message[1] = x.par1; | |
notes[count].message[2] = 0; | |
count++; | |
} | |
} | |
endpoint = notes + count; | |
status = SmfStatus::STOP; | |
startTime = std::chrono::system_clock::now(); | |
std::cout << "count: " << count << std::endl; | |
} | |
bool Smf::start() { | |
if (status != SmfStatus::STOP) return false; | |
std::thread thr([&](RtMidiOut *midi, Note *begin, Note *end) { | |
current = begin; | |
int pausedTime = -1; | |
while (status != SmfStatus::STOP) { | |
int acumulate = std::chrono::duration_cast<std::chrono::milliseconds> ( | |
std::chrono::system_clock::now() - startTime | |
).count(); | |
if (status == SmfStatus::PAUSE) { | |
// Pause直後は、現在の経過時間を保存する | |
if (pausedTime < 0) pausedTime = acumulate; | |
usleep(5000); | |
continue; | |
} else { | |
// Play直後は、Pauseしていた時間を再生開始時間に継ぎ足す | |
if (pausedTime >= 0) { | |
startTime += std::chrono::milliseconds(acumulate - pausedTime); | |
acumulate = pausedTime; | |
pausedTime = -1; | |
} | |
} | |
while (current < end) { | |
if (current->time <= acumulate) { | |
if (status == SmfStatus::PLAY) { | |
midi->sendMessage(current->message, 3); | |
printf("send: %d %d %d\n", current->message[0], current->message[1], current->message[2]); | |
} | |
current++; | |
} else { | |
break; | |
} | |
} | |
usleep(1500); | |
} | |
std::cout << "Thread destroy" << std::endl; | |
if (status != SmfStatus::STOP) std::cout << "Unknown reason for stopped" << std::endl; | |
return; | |
}, midi, notes, endpoint); | |
thr.detach(); | |
status = SmfStatus::PAUSE; | |
return true; | |
} | |
bool Smf::play() { | |
if (status == SmfStatus::STOP) Smf::start(); | |
status = SmfStatus::PLAY; | |
return true; | |
} | |
bool Smf::pause() { | |
if (status == SmfStatus::STOP) return false; | |
status = SmfStatus::PAUSE; | |
return true; | |
} | |
bool Smf::stop() { | |
if (status == SmfStatus::STOP) return false; | |
status = SmfStatus::STOP; | |
return true; | |
} | |
bool Smf::seek(int _milliseconds) { | |
startTime = std::chrono::system_clock::now() - std::chrono::milliseconds(_milliseconds); | |
current = notes; | |
while (current < endpoint) { | |
if (current->time >= _milliseconds) return true; | |
current++; | |
} | |
return false; | |
} | |
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
#include <chrono> | |
#include "RtMidi.h" | |
#include "midi.hpp" | |
typedef struct _Note | |
{ | |
int time; | |
unsigned char message[3]; | |
} Note; | |
enum struct SmfStatus { | |
START, PLAY, STOP, PAUSE, | |
}; | |
using std::chrono::system_clock; | |
class Smf { | |
private: | |
const char * filepath; | |
Note * notes; | |
Note * endpoint; | |
Note * current; | |
SmfStatus status; | |
system_clock::time_point startTime; | |
RtMidiOut * midi; | |
public: | |
Smf(const char * filepath, RtMidiOut * midi); | |
bool start(); | |
bool play(); | |
bool pause(); | |
bool stop(); | |
bool seek(int milliseconds); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment