Я создал программу, которая преобразует ваши данные в MIDI-файл на C ++, прежде чем читать, что вы предпочитаете Python. В любом случае, я предпочитаю C ++ и Qt, поэтому в этом примере для работы используется библиотека файлов барабанных палочек . Отказ от ответственности: я автор drumstick, на всякий случай, если кто-то жалуется на спам или саморекламу. Вот код:
smfbuilder.pro
TEMPLATE = app
CONFIG += c++11 console link_pkgconfig
packagesExist(drumstick-file) {
message("using pkg-config")
PKGCONFIG += drumstick-file
} else {
message("using environment variables")
INCLUDEPATH += $$(DRUMSTICKINCLUDES)
LIBS += -L$$(DRUMSTICKLIBS) -ldrumstick-file
}
HEADERS += smfbuilder.h
SOURCES += smfbuilder.cpp
smfbuilder.h
#include <QObject>
#include <drumstick/qsmf.h>
class MidiEvent
{
public:
long absTime;
int status;
int data1;
int data2;
};
class Sequence : public QList<MidiEvent>
{
public:
virtual ~Sequence();
void sort();
};
class SMFBuilder : public QObject
{
Q_OBJECT
public:
SMFBuilder();
void run(QString fileName);
void generate();
public slots:
void errorHandler(const QString& errorStr);
void trackHandler(int track);
private:
drumstick::QSmf *m_engine;
Sequence m_sequence;
};
smfbuilder.cpp
#include <QCoreApplication>
#include <QDebug>
#include <QTextCodec>
#include <drumstick/qsmf.h>
#include "smfbuilder.h"
static inline bool eventLessThan(const MidiEvent& s1, const MidiEvent& s2)
{
return s1.absTime < s2.absTime;
}
void Sequence::sort()
{
std::stable_sort(begin(), end(), eventLessThan);
}
Sequence::~Sequence()
{
clear();
}
SMFBuilder::SMFBuilder() : QObject()
{
m_engine = new drumstick::QSmf(this);
m_engine->setTextCodec(QTextCodec::codecForName("UTF-8"));
connect(m_engine, SIGNAL(signalSMFError(const QString&)),
this, SLOT(errorHandler(const QString&)));
connect(m_engine, SIGNAL(signalSMFWriteTrack(int)),
this, SLOT(trackHandler(int)));
}
void SMFBuilder::errorHandler(const QString& errorStr)
{
qWarning() << errorStr;
exit(1);
}
void SMFBuilder::trackHandler(int )
{
// meta events
m_engine->writeBpmTempo(0, 100); // m.m.=100 (andante)
m_engine->writeTimeSignature(0, 4, 2, 18, 8); // ts = 4/4
m_engine->writeKeySignature(0, 0, major_mode); // C major
m_engine->writeMidiEvent(0, program_chng, 0, 0); // grand piano
// note events
long last_time = 0;
for(auto ev : m_sequence)
{
long delta = ev.absTime - last_time;
last_time = ev.absTime;
m_engine->writeMidiEvent(delta, ev.status, 0, ev.data1, ev.data2);
}
// final event
m_engine->writeMetaEvent(0, end_of_track);
}
void SMFBuilder::run(QString fileName)
{
m_engine->setDivision(120); // ticks per quarter note
m_engine->setFileFormat(0); // single track
m_engine->setTracks(1);
m_engine->writeToFile(fileName);
}
void SMFBuilder::generate()
{
QList<float> times = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 9.0, 9.0, 9.0, 10.0, 10.0, 10.0, 11.0, 11.0};
QList<float> notes = {57.0, 59.0, 60.0, 62.0, 64.0, 65.0, 67.0, 57.0, 60.0, 64.0, 65.0, 62.0, 59.0, 64.0, 67.0};
long quarter_length = 120; // quarter note duration in ticks
int velocityOn = 100; // forte
for ( int i=0; i<times.length(); ++i) {
long event_time = static_cast<long>(times[i] * quarter_length);
int midi_note = static_cast<int>(notes[i]);
// insert note on event
MidiEvent noteOn;
noteOn.absTime = event_time;
noteOn.status = note_on;
noteOn.data1 = midi_note;
noteOn.data2 = velocityOn;
m_sequence.append(noteOn);
// insert note off event
MidiEvent noteOff;
noteOff.absTime = event_time + quarter_length;
noteOff.status = note_off;
noteOff.data1 = midi_note;
noteOff.data2 = 0; // note off
m_sequence.append(noteOff);
}
m_sequence.sort();
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
SMFBuilder builder;
builder.generate();
builder.run("output.mid");
return 0;
}
Некоторые заметки окод:
Вы можете использовать два способа настройки зависимости от барабанной палочки. Предполагая, что вы используете Linux, либо установите пакет drumstick-devel из своего дистрибутива Linux (или скомпилируйте его из исходников), и управляйте зависимостями с помощью pkg-config, либо вы определяете переменные среды DRUMSTICKINCLUDES и DRUMSTICKLIBS в настройках проекта QtCreator,или ваша оболочка.
MIDI-файл похож на партитуру. Есть много музыкальных параметров, которые не указаны в ваших данных, таких как темп, тональность, временная подпись ... Я пытался выбрать разумные значения и предоставить соответствующие комментарии к коду.
Общая стратегия заключается в генерации последовательности событий midi в SMFBuilder :: generate (), которая позже записывается в файл.
После генерации файла "output.mid" это представление Rosegarden: