Как мне преобразовать эти данные в файл MIDI? - PullRequest
0 голосов
/ 24 октября 2019

У меня есть индекс нот (каждая октава имеет 12 нот) в зависимости от времени (в ударах) данных. Как я могу преобразовать эти данные в миди-файл?

Example Data:

time = [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]

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]

Sample Image for Data in Graph form

Инструмент - фортепиано.

Также обратите внимание, чтоЕсть несколько заметок одновременно.

Заранее спасибо.

1 Ответ

0 голосов
/ 24 октября 2019

Я создал программу, которая преобразует ваши данные в 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;
}

Некоторые заметки окод:

  1. Вы можете использовать два способа настройки зависимости от барабанной палочки. Предполагая, что вы используете Linux, либо установите пакет drumstick-devel из своего дистрибутива Linux (или скомпилируйте его из исходников), и управляйте зависимостями с помощью pkg-config, либо вы определяете переменные среды DRUMSTICKINCLUDES и DRUMSTICKLIBS в настройках проекта QtCreator,или ваша оболочка.

  2. MIDI-файл похож на партитуру. Есть много музыкальных параметров, которые не указаны в ваших данных, таких как темп, тональность, временная подпись ... Я пытался выбрать разумные значения и предоставить соответствующие комментарии к коду.

  3. Общая стратегия заключается в генерации последовательности событий midi в SMFBuilder :: generate (), которая позже записывается в файл.

После генерации файла "output.mid" это представление Rosegarden:

enter image description here

...