Запись выходов "терменвокса" в MIDI-файл на SD-карте
Я делаю музыкальный инструмент (симулятор терменвокса). Я хочу, чтобы была возможность "записи", которая хранит громкость и ноту, которую играет пользователь, в MIDI-файл. Обратите внимание, что на самом деле это не запись звука, а только данные о частоте и громкости в определенный момент времени.
До сих пор я провел некоторые исследования и нашел, как воспроизводить MIDI-файлы (что полезно, и я планирую взять это на себя) и читать / записывать на SD-карты. Однако я не нашел источника, который позволял бы мне создавать и создавать MIDI-файлы.
Как я должен выполнить эту функцию?
Технические характеристики:
- Я использую Arduino Nano, у меня тоже есть Uno, если это необходимо
- SD-карта представляет собой 64-гигабайтную micro SD, вставленную в SD-адаптер, с припаянными к ней штифтами заголовка. (Я слишком дешев, чтобы купить настоящий щит)
- Ранее это был XCSD, я отформатировал его в FAT32, чтобы он был совместим с Arduino
Также: я записываю с самодельной копии терменвокса, которая в основном представляет собой два ультразвуковых датчика громкости и высоты тона. Является ли это "слишком простым" для типа MIDI-файла? (Правка: я думаю, это слишком продвинуто)
EDIT: Этот проект был заброшен из-за отсутствия хорошего способа играть между полутонами (т.Е. На определенных частотах), что мне нужно для моего терменвокса. Хотя существует функция изгиба высоты тона, величина изгиба высоты тона приводит к различным изменениям частоты в зависимости от приемника.
Вместо этого я создам свой собственный файл для записи / воспроизведения, потому что использование метода pitch bend + note с использованием MIDI - это слишком много работы, и он все равно не будет совместим с другими устройствами.
Ответ Ника Гэммона действительно хорош для тех, кому нужно только записывать / воспроизводить определенные тона и полутона в формате MIDI.
Спасибо
@David Liao, 👍1
Обсуждение2 ответа
Лучший ответ:
Короткий ответ: "да, это возможно". Есть много гаджетов, которые записывают MIDI. В них должны быть микропроцессоры и что-то вроде SD-карты, которая сама по себе похожа на EEPROM.
Возможно, у вас возникли проблемы с сохранением заметок в ОЗУ и их последующей записью на SD-карту достаточно быстро, чтобы не потерять некоторые из них. Знание скорости поступления заметок, скорости SD-карты и типа Arduino поможет ответить, будет ли работать ваш конкретный случай.
Я написал скетч, который читает MIDI, который показывает, что определенно возможно получать данные MIDI и отображать его. Видео его работы находится здесь.
Я лично не знаю формат файлов MIDI, но это хорошо задокументировано.
(Редактировать) Сейчас знаю - см. ниже.
Запись на SD-карты хорошо задокументирована. Для того, чтобы ваша идея заработала, нужно взять вещи (например, мой код для декодирования MIDI), код для записи на SD-карты и знать правильный формат.
(Отредактировано для добавления)
См. Стандартную спецификацию формата MIDI-файлов. 1.1 для примера создания MIDI-данных. Также см. Формат файла MIDI и Стандартный формат MIDI-файла (SMF)
Ваша основная техника должна быть (согласно предложению gabonator):
Запишите фрагмент заголовка в файл, например:
4D 54 68 64 // MThd 00 00 00 06 // длина чанка 00 00 // формат 0 00 01 // одна дорожка 00 60 // 96 импульсов на четверть
Записать фрагмент трека в файл (длина будет обновлена позже)
4D 54 72 6B // МТРк 00 00 00 00 // <-- длина будет обновлена позже
Запишите конфигурации прибора (с нулевыми значениями дельты времени), например, с этой страницы:
00 FF 58 04 04 02 18 08 // 4 байта; 4/4 раза; 24 MIDI-такта/щелчка, // 8 32-х нот/ 24 MIDI-такта // (24 MIDI-такта = 1 кроше = 1 удар) 00 FF 51 03 07 A1 20 // 3 байта: 500 000 мкс/четверть // = 120 ударов в минуту (500000 в шестнадцатеричном формате это 0x07A120) 00 C0 05 // Ch.1 Изменение программы 5 = GM // Патч 6 = Электрическое пианино 2
Когда игрок что-то играет, вычтите время, когда он в последний раз что-то делал, из текущего времени. Это даст вам разницу во времени.
Конвертировать разницу во времени в тики. Из строки заголовка выше мы имеем 96 импульсов на четвертную ноту (PPQ) и 500 000 мкс (500 мс) на четвертную ноту. Таким образом, одна секунда будет двумя четвертными нотами (2 удара в секунду и, следовательно, 120 ударов в минуту), а одна секунда, следовательно, будет 192 такта). Следовательно, количество тиков, основанное на разнице во времени в миллисекундах, будет:
unsigned long ticks = (time_difference * 192) / 1000
Выведите дельту времени в виде числа переменной длины, где старший бит устанавливается для каждого байта по мере необходимости, пока не останется значение <= 0x7F. На странице выше приведен пример кода для этого. Например, для разницы времени в 199 тиков:
Decimal time of 199 ticks -> 0xC7 0xC7 -> 0b11000111 Since that exceeds 127, split into two bytes: 10000001 (the high order bits) ^^^^^^^ <- high order bits (the MSB must be one) 01000111 (the low-order 7 bits) ^^^^^^^ <- low order bits (the MSB must be zero) Result is 0x81 0x47
Более длительное время может потребовать еще больше байтов.
Что-то вроде этого должно сделать это (адаптировано со связанной страницы). Просто измените строку Serial.write для вывода в файл на диске.
void WriteVarLen (unsigned long value) { unsigned long buffer; buffer = value & 0x7f; while ((value >>= 7) > 0) { buffer <<= 8; buffer |= 0x80; buffer |= value & 0x7f; } while (true) { Serial.write (buffer & 0xFF); if (buffer & 0x80) buffer >>= 8; else break; } } // конец WriteVarLen
Немедленно следите за изменением времени по "примечанию" или "примечание выключено" байт (в зависимости от того, начали или остановили воспроизведение ноты). Например, "примечание" для канала 1 будет 0x90, потому что 0x9n — это примечание к команде, а n — номер канала, относительный ноль. "примечание выключено" для канала 1 будет 0x80.
Следуйте "примечанию к" или "примечание выключено" по номеру ноты (другими словами, какая нота была только что сыграна или отпущена) — один байт.
Octave # Note Numbers in decimal C C# D D# E F F# G G# A A# B -1 0 1 2 3 4 5 6 7 8 9 10 11 0 12 13 14 15 16 17 18 19 20 21 22 23 1 24 25 26 27 28 29 30 31 32 33 34 35 2 36 37 38 39 40 41 42 43 44 45 46 47 3 48 49 50 51 52 53 54 55 56 57 58 59 4 60 61 62 63 64 65 66 67 68 69 70 71 5 72 73 74 75 76 77 78 79 80 81 82 83 6 84 85 86 87 88 89 90 91 92 93 94 95 7 96 97 98 99 100 101 102 103 104 105 106 107 8 108 109 110 111 112 113 114 115 116 117 118 119 9 120 121 122 123 124 125 126 127
Обычно средним C считается C4 (C в 4-й октаве), однако очевидно, что это может варьироваться в зависимости от устройства.
Затем выведите скорость (один байт). Скорость (десятичная) 32 будет тихой, 64 — средней, 96 — громкой. Диапазон скоростей составляет от 0 до 127.
Повторяйте вышеописанное (кроме записи фрагментов и конфигурации), пока игрок не решит прекратить игру.
Напишите "конец трека" маркер:
00 FF 2F 00 // Дельта времени ноль, конец трека
Затем вернитесь к началу файла +, где находится длина байтов (18 байтов в файле, судя по всему), и обновите длину байтов (4 байта).
Пример создания MIDI-файла
Этот пример кода записывает MIDI-файл. В качестве устройства ввода я использовал 16-клавишную клавиатуру вот такого вида:
Столбцы были подключены к контактам 0, 1, 2, 3 Arduino Uno, а строки — к контактам 4, 5, 6, 7. Это позволяет ленточному кабелю от клавиатуры удерживать контакты в естественном порядке. Если вы используете другие контакты, просто измените массив keys
в скетче.
Библиотека для клавиатур, которые я использовал, доступна по адресу https://github.com/nickgammon/Keypad_Matrix
.Эта конкретная библиотека поддерживает одновременное нажатие n клавиш, а также события нажатия и нажатия клавиши. Это особенно полезно для музыки, поскольку позволяет одновременно удерживать несколько клавиш, и будут генерироваться соответствующие события. Очевидно, вам нужно событие нажатия клавиши, чтобы сгенерировать "заметку о" и событие нажатия клавиши для создания "отключения примечания".
Я разложил ключи так:
C C# D D#
E F F# G
G# A A# B
Down Soft Up Record
Есть "запись" Светодиод, подключенный к A0, который загорается, когда вы начинаете запись (нажимая кнопку записи).
Нажмите кнопку "Запись" еще раз, чтобы остановить запись и завершить запись файла. Важно правильно остановить запись, а не просто выключить устройство, потому что длина файла должна быть записана рядом с началом.
Нажмите (и удерживайте) кнопку "Вниз" рядом с другой нотой, чтобы играть на октаву ниже. Нажмите (и удерживайте) кнопку "Вверх" рядом с другой нотой, чтобы сыграть на октаву выше.
Нажмите (и удерживайте) кнопку Soft рядом с другой нотой, чтобы играть с меньшей скоростью.
Использование матрицы клавиатуры логично, потому что большинство клавиатурных устройств используют матрицу, чтобы уменьшить количество проводов, которые вам нужно проложить.
Обратите внимание, что матрице нужны диоды, подключенные к каждому переключателю, как описано здесь, поскольку в противном случае, если вы нажмете 3 и более ключей одновременно вы получите «призрак»; ключи.
В моем случае я вскрыл стандартную клавиатуру и впаял туда диоды.
Я также использовал переходник Micro-SD, подключенный к контактам SPI Arduino, вот так:
Библиотека SDFat была заархивирована и хранится здесь: http://gammon.com.au/Arduino. /SdFat-master.zip (2,2 Мб)
Разархивируйте этот файл и из папок внутри него скопируйте папку SdFat в ваши "библиотеки" папка (которая находится в папке вашего альбома для рисования Arduino, а не в папке приложения Arduino). Затем перезапустите Arduino IDE.
Код определенно компилируется с этим (с несколькими предупреждениями). Более поздняя библиотека из https://github.com/greiman/SdFat может быть лучше, но я ее не проверял.< /p>
Для отслеживания номеров файлов я использовал библиотеку EEPROMAnything, копию которой можно найти по адресу http://gammon. .com.au/Arduino/EEPROMAnything.zip
Каждый раз, когда вы начинаете новую запись, к номеру файла добавляется единица, которая после достижения 9999 увеличивается до 0000.
Если возникла ошибка при инициализации файловой системы (например, нет SD-карты), светодиод медленно мигает. Если файл не может быть создан, светодиод быстро мигает.
Вы можете выбрать MIDI-инструмент, изменив строку:
const byte PATCH_NUMBER = 4; // электрическое пианино 1
Код
/*
Скетч для записи в MIDI-файл с инструмента.
Автор: Ник Гэммон
Дата: 10 мая 2018 г.
Copyright 2018 Ник Гэммон.
РАЗРЕШЕНИЕ НА РАСПРОСТРАНЕНИЕ
Настоящим предоставляется бесплатное разрешение любому лицу, получающему копию этого программного обеспечения.
и связанные с ним файлы документации («Программное обеспечение») для работы с Программным обеспечением без ограничений,
включая, помимо прочего, права на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование,
и/или продавать копии Программного обеспечения, а также разрешать лицам, которым предоставляется Программное обеспечение, делать это,
при соблюдении следующих условий:
Вышеприведенное уведомление об авторских правах и это уведомление о разрешении должны быть включены в
все копии или существенные части Программного обеспечения.
ОГРАНИЧЕНИЕ ОТВЕТСТВЕННОСТИ
Программное обеспечение предоставляется «как есть», без каких-либо явных или подразумеваемых гарантий.
включая, но не ограничиваясь гарантиями товарного состояния, пригодности для конкретного
цель и ненарушение. Ни при каких обстоятельствах авторы или правообладатели не несут ответственности
за любые претензии, убытки или другую ответственность, будь то в действии контракта,
деликта или иным образом, вытекающие из, из или в связи с программным обеспечением
или использование или другие операции с программным обеспечением.
*/
#include <Keypad_Matrix.h>
#include <SdFat.h>
#include <EEPROM.h>
#include <EEPROMAnything.h>
// Номера MIDI см.: http://www.pjb.com.au/muscript/gm.html
const byte PATCH_NUMBER = 4; // электрическое пианино 1
const byte RECORDING_LED = A0;
// нижняя строка меняет дело
const char DOWN_OCTAVE = '-';
const char SOFT = 's';
const char UP_OCTAVE = '+';
const char RECORD = 'r';
// как на клавиатуре расположены клавиши
// заглавные буквы для диезов
const byte ROWS = 4;
const byte COLS = 4;
const char keys[ROWS][COLS] = {
{'c', 'C', 'd', 'D'},
{'e', 'f', 'F', 'g'},
{'G', 'a', 'A', 'b'},
{DOWN_OCTAVE, SOFT, UP_OCTAVE, RECORD}, // - = октава вниз, s = мягко, + = октава вверх, r = начать/остановить запись
};
const byte colPins[COLS] = {0, 1, 2, 3}; //подключаем к колонке распиновку клавиатуры
const byte rowPins[ROWS] = {4, 5, 6, 7}; //подключаемся к распиновке ряда клавиатуры
// Создаем клавиатуру
Keypad_Matrix kpd = Keypad_Matrix( makeKeymap (keys), rowPins, colPins, ROWS, COLS );
// объект файловой системы
SdFat sd;
const byte chipSelect = SS;
// текущий файл, в который мы записываем
SdFile myFile;
// мы записываем прямо сейчас?
bool recording = false;
const byte MIDI_META_EVENT = 0xFF;
const byte MIDI_SET_TEMPO = 0x51;
const byte MIDI_TIME_SIGNATURE = 0x58;
const byte MIDI_END_OF_TRACK = 0x2F;
const byte MIDI_PROGRAM_CHANGE = 0xC0;
const byte MIDI_NOTE_ON = 0x90; // канал 1
const byte MIDI_NOTE_OFF = 0x80; // канал 1
struct
{
char magic [4]; // МТд
uint32_t length;
uint16_t format;
uint16_t tracks;
uint16_t PPQ; // импульсов на четвертную ноту
} chunkHeader;
struct
{
char magic [4]; // МТРк
uint32_t length;
} trackHeader;
unsigned long trackHeaderPosition;
unsigned long timeLastAction;
int nextFileNumber;
uint32_t changeEndianness32(uint32_t val)
{
return (val << 24) |
((val << 8) & 0x00ff0000) |
((val >> 8) & 0x0000ff00) |
((val >> 24) & 0x000000ff);
} // конец измененияEndianness32
uint16_t changeEndianness16(uint16_t val)
{
return (val << 8) | ((val >> 8) & 0x00ff);
} // конец измененияEndianness16
void showError (const int delayTime)
{
while (true)
{
digitalWrite (RECORDING_LED, HIGH);
delay (delayTime);
digitalWrite (RECORDING_LED, LOW);
delay (delayTime);
}
} // конец showError
void startRecording ()
{
EEPROM_readAnything (0, nextFileNumber);
if (nextFileNumber > 9999 || nextFileNumber < 0)
nextFileNumber = 0;
char name[15];
sprintf (name, "SONG%04d.MID", nextFileNumber++);
// открываем файл для записи
if (!myFile.open(name, O_WRITE | O_CREAT | O_TRUNC))
showError (100); // быстрая вспышка - никогда не возвращается
// обновить EEPROM для следующего раза
EEPROM_writeAnything (0, nextFileNumber);
recording = true;
digitalWrite (RECORDING_LED, HIGH);
memcpy (chunkHeader.magic, "MThd", sizeof (chunkHeader.magic));
chunkHeader.length = changeEndianness32 (
sizeof (chunkHeader.format) +
sizeof (chunkHeader.tracks) +
sizeof (chunkHeader.PPQ));
chunkHeader.format = changeEndianness16 (0);
chunkHeader.tracks = changeEndianness16 (1);
chunkHeader.PPQ = changeEndianness16 (96);
myFile.write (&chunkHeader, sizeof (chunkHeader));
memcpy (trackHeader.magic, "MTrk", sizeof (trackHeader.magic));
trackHeader.length = 0; // для заполнения позже
trackHeaderPosition = myFile.curPosition ();
myFile.write (&trackHeader, sizeof (trackHeader));
struct
{
byte deltaTime = 0;
byte metaCode [2] = { MIDI_META_EVENT, MIDI_TIME_SIGNATURE };
byte length = 4;
byte sig [2] = { 4, 2 }; // время 4/4 (знаменатель 2 равен 2^2)
byte clocksPerClick = 24; // MIDI часы/щелчок
byte clocksPerBeat = 8;
} timeSignature;
struct
{
byte deltaTime = 0;
byte metaCode [2] = { MIDI_META_EVENT, MIDI_SET_TEMPO };
byte length = 3;
byte usPerQuarterNote [3] = { 0x07, 0xA1, 0x20 }; // то есть: 500000 мкс на такт
} tempo;
struct
{
byte deltaTime = 0;
byte message = MIDI_PROGRAM_CHANGE;
byte patchNumber = PATCH_NUMBER;
} programChange;
myFile.write (&timeSignature, sizeof timeSignature);
myFile.write (&tempo, sizeof tempo);
myFile.write (&programChange, sizeof programChange);
timeLastAction = millis ();
} // конец начала записи
void stopRecording ()
{
struct
{
byte deltaTime = 0;
byte metaCode [2] = { MIDI_META_EVENT, MIDI_END_OF_TRACK };
byte length = 0;
} endOfTrack;
// записываем "конец трека" сообщение
myFile.write (&endOfTrack, sizeof endOfTrack);
// найти, где мы находимся
unsigned long finalPosition = myFile.curPosition ();
// вернуться туда, где находится заголовок трека
myFile.seekSet (trackHeaderPosition);
// вычисляем длину данных (исключая заголовки)
trackHeader.length = changeEndianness32 (finalPosition - trackHeaderPosition - sizeof (trackHeader));
// обновляем длину файла
myFile.write (&trackHeader, sizeof (trackHeader));
myFile.close ();
// обеспечим сброс на диск
myFile.SdBaseFile::sync();
// запись больше не ведется, выключите светодиод записи
recording = false;
digitalWrite (RECORDING_LED, LOW);
} // конец остановки записи
void writeVarLen(unsigned long value)
{
unsigned long buffer;
buffer = value & 0x7f;
while ((value >>= 7) > 0)
{
buffer <<= 8;
buffer |= 0x80;
buffer |= value & 0x7f;
} // конец времени
while (true)
{
myFile.write ((byte) (buffer & 0xFF));
if (buffer & 0x80)
buffer >>= 8;
else
break;
} // конец времени
} // конец записиVarLen
// превращаем код клавиатуры в номер ноты
byte codeToNote (const char which)
{
char notes [] = "cCdDefFgGaAb";
char * pos = strchr (notes, which);
if (pos == NULL)
return 0;
return pos - notes + 60; // начинаем с середины C (C4)
} // конец codeToNote
// делаем пометку включенной или выключенной
void doNoteAction (const char which, const byte action)
{
// нам нужно вычислить, сколько миллисекунд прошло
unsigned long now = millis ();
unsigned long deltaTime = now - timeLastAction;
// помним, когда мы это делали
timeLastAction = now;
// записываем дельту времени
writeVarLen ((deltaTime * 192) / 1000);
// примечание к информации
struct
{
byte noteOn;
byte whichNote;
byte velocity;
} playNote;
playNote.noteOn = action;
playNote.whichNote = codeToNote (which);
if (kpd.isKeyDown (DOWN_OCTAVE))
playNote.whichNote -= 12; // на одну октаву вниз
else if (kpd.isKeyDown (UP_OCTAVE))
playNote.whichNote += 12; // на одну октаву вверх
playNote.velocity = 64; // средняя скорость
if (kpd.isKeyDown (SOFT))
playNote.velocity = 32; // мягче
myFile.write (&playNote, sizeof (playNote));
} // конец doNoteAction
void startPlaying (const char which)
{
doNoteAction (which, MIDI_NOTE_ON);
} // конец начала воспроизведения
void stopPlaying (const char which)
{
doNoteAction (which, MIDI_NOTE_OFF);
} // конец остановки воспроизведения
void keyDown (const char which)
{
switch (which)
{
case 'c':
case 'C':
case 'd':
case 'D':
case 'e':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
case 'b':
startPlaying (which);
break;
default:
break;
} // конец переключателя, на какой клавише
} // конец keyDown
void keyUp (const char which)
{
switch (which)
{
case RECORD:
if (recording)
stopRecording ();
else
startRecording ();
break;
case 'c':
case 'C':
case 'd':
case 'D':
case 'e':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
case 'b':
stopPlaying (which);
break;
default:
break;
} // конец переключателя, на какой клавише
} // конец keyUp
void setup()
{
kpd.begin ();
kpd.setKeyDownHandler (keyDown);
kpd.setKeyUpHandler (keyUp);
pinMode (RECORDING_LED, OUTPUT);
if (!sd.begin (chipSelect, SPI_HALF_SPEED))
showError (500); // никогда не возвращается
} // конец настройки
void loop()
{
kpd.scan ();
} // конец цикла
Я записываю с самодельной копии терменвокса, который в основном представляет собой показания двух ультразвуковых датчиков для громкости и высоты тона. Это "слишком просто"? для типа файла MIDI?
Похоже, это возможно с помощью MIDI "изменения высоты тона" сообщение. Вам нужно будет определить, где находится значение вашей ноты (предположительно большую часть времени между естественными нотами, такими как C и C#), а затем отправить сообщение о бэнде или "Изменение колеса высоты тона" (0xE0 для канала 1), чтобы изменить основную ноту. Основную ноту нужно будет обновить, если вы переместите ее более чем на пару полутонов.
См.:
- https://en.wikipedia.org/wiki/Pitch_wheel
- http://sites.uci.edu/camp2014/2014/ 30 04/managing-midi-pitchbend-messages/
В моем проекте на самом деле ничего не подключено к MIDI-порту; это просто чтение/запись в файлы., @David Liao
Хотя то, что ты сделал, действительно круто, @David Liao
Ах хорошо. Так что мой ответ на самом деле не касается вашего вопроса. Мне пришла в голову мысль, что ваше устройство выводит MIDI-коды и вы хотите превратить их в файл на диске., @Nick Gammon
Я обновил свой ответ, чтобы дать более подробную информацию о том, как вы можете это сделать., @Nick Gammon
Я обновил свой ответ, чтобы привести пример полного скетча, который делает это., @Nick Gammon
Ух ты. Такого развёрнутого ответа я никак не ожидал. Дайте мне несколько дней, чтобы все понять. У меня ограниченный опыт во всем этом, но я постараюсь понять, смогу ли я это понять!, @David Liao
Итак, я думаю, что понимаю, как работает синтаксис MIDI. На терменвоксе нота всегда будет включена, так что байт всегда будет равен 0x90, верно?, @David Liao
Я читаю об изменении высоты тона, а также пытаюсь понять, как преобразовать значение частоты в ноту + изменение высоты тона ... также где мне написать байт изменения высоты тона - после байта номера ноты?, @David Liao
Нет, это две разные команды. 0x90 - "примечание". Не было бы смысла отправлять много «заметок», если бы они не предназначались для разных заметок. А питчбенд — это отдельная команда. Если бы диапазон высоты тона был достаточно большим, вы могли бы просто сделать одну «ноту на», а затем просто подтянуть ее вверх или вниз по мере движения руки, но диапазон может быть слишком мал для этого. Он может просто изгибаться от C к D. Я думаю, вам придется поэкспериментировать с этим. Планируете ли вы воспроизвести это на другом MIDI-устройстве или просто воспроизвести на своем терамине?, @Nick Gammon
Ультразвуковые датчики не очень хороши, поэтому нота постоянно колеблется, даже если рука, управляющая ею, не двигается. Я все равно хочу, чтобы он все равно их записывал, так почему же не уместно отправлять много «примечаний»? Частота обновления ультразвуковых датчиков довольно высокая, я бы предположил, что около 15 мс на ноту., @David Liao
Во второй ссылке, которую вы мне дали, говорится: «Величина изменения высоты тона, вызванная значением высоты тона, определяется принимающим устройством». Так что, я думаю, мне придется поэкспериментировать, чтобы увидеть, насколько одно значение высоты тона изменяет частоту, но, вероятно, оно не сможет продвинуться на несколько октав, на что я надеялся., @David Liao
Если значение изменения высоты тона преобразуется в различные изменения частоты в зависимости от типа воспроизводимого приемника, то это противоречит цели использования MIDI для записи и воспроизведения. Я думаю, что мог бы просто использовать какой-то пользовательский формат файла, так как он все равно не будет совместим с другими инструментами..., @David Liao
Да, для записи и воспроизведения на вашем собственном устройстве пользовательский формат файла был бы намного проще. Вы могли бы просто записать необработанные данные с датчиков (и разницу во времени). Затем, когда они переходят к воспроизведению, заставьте код думать, что эти необработанные данные только что были получены, и он должен воспроизвести их идеально., @Nick Gammon
Этот ответ - чистое золото. Я только что построил сквозной MIDI-рекордер, который находится между устройством с миди-выходом (например, новаторским ключом запуска) и устройством с миди-входом (например, аудиоинтерфейсом ASIO), так что даже если вы не используете что-то, что приходит с «постоянной записью», такой как FL Studio или Ableton, вы не потеряете тот сладкий рифф, который вы дурачили час назад. Для простоты я записывал данные в виде файла CSV, а не в формате .mid, но благодаря вашему ответу, который сегодня изменился., @Mike 'Pomax' Kamermans
Да, прочитайте спецификацию формата MIDI-файла. Создайте midi-файл с одной дорожкой и добавьте только команды note press / release / pitch или volume changes и delta times (здесь объясняется обратное преобразование delta times в миллисекунды). Когда вы закончите запись, просто найдите начало файла и обновите значение длины дорожки (общий размер добавленного буфера midi-команд).
- Как увеличить скорость записи на SD-карту в Ардуино
- Какие контакты можно использовать для выбора микросхемы (CS, CC) на Arduino Nano Every?
- Сделать CS (chip select) для работаты в SPI (для и SD карты и OLED)
- Использование экрана SD-карты на Arduino Due
- Загрузить сайт с SD-карты
- Отобразить изображение с SD-карты на ЖК-дисплей ILI9486
- `.readFileCounts()` не работает в мини-модуле DFPlayer
- DFPlayer для записи на SD-карту
Изучите синтаксис правильно сконструированного MIDI-файла и напишите скетч Arduino для создания файла на SD-карте. Возможно, это легче сказать, чем сделать, поскольку я подозреваю, что артист редко воспроизводит музыкальную фразу одним и тем же способом дважды., @st2000
@st2000 Какая разница, если в другой раз они сыграют по-другому? Запишите, что они на самом деле играли. То, что они могли бы сыграть в другой раз, не имеет значения., @Mark Smith
Какой Arduino вы имеете в виду? В наши дни их довольно много., @Nick Gammon
Вы правы, @MarkSmith. Что касается MIDI-записей, я всегда хочу записать запись на ноты. Поэтому я постоянно думаю о том, "что такое четвертная нота". Подобно тому, как GPS вашего автомобиля привязывает вас к ближайшей дороге, несмотря на ваше фактическое местоположение. Если это здесь не нужно - вы правы - просто запишите то, что воспроизводится, и воспроизведите его., @st2000
Обновил вопрос спецификациями., @David Liao
Я просто хочу записать все, что воспроизводится., @David Liao
@st2000 Я попытался изучить синтаксис, загрузив MIDI-файл из Интернета и прочитав его с помощью текстового редактора. Текст состоял даже не из букв, так что я просто махнул на это рукой., @David Liao
Если кто-нибудь делал это раньше, я думаю, я могу потратить время, чтобы прочитать о синтаксисе MIDI. Если музыка простая, можно ли сделать это без каких-либо закодированных символов?, @David Liao
Я делал это раньше, и это совсем не сложно. Единственное, что мне потребовалось некоторое время, чтобы понять, - это то, как "время" кодируется в midi-файлах. Взгляните на это [here](https://stackoverflow.com/questions/44142513/writing-midi-file-from-scratch-using-hexadecimal) , кто-то создавал простой midi-файл с нуля в шестнадцатеричном редакторе, @gabonator
Если у вас нет интерфейса SD-карты, то я надеюсь, что вы используете переключатели уровней для линий передачи данных. Вот так: http://www.gammon.com.au/images/Arduino/Micro_SD_breakout.png Фишка справа выполняет сдвиг уровня., @Nick Gammon
@NickGammon Что именно делает сдвиг уровня? Я использую интерфейс SPI для связи и резисторы в регуляторах напряжения, чтобы снизить его до 3,3 В. Я в основном сделал это из этого: https://nathan.chantrell.net/20111128/diy-micro-sd-shield-for-arduino/, @David Liao
Сдвиг уровня предназначен для (скажем) запуска устройства 3,3 В от устройства 5 В. Ваши резисторы, действующие как делители напряжения, уменьшат напряжение 5 В до 3,3 В. Это устройство полагается на то, что Arduino распознает 3,3 В как ВЫСОКОЕ, что является небольшим пределом (минимум для ВЫСОКОГО - 3 В, если вы работаете на 5 В). Правильный регулятор уровня фактически сдвигает напряжение 3,3 В до 5 В и 5 В до 3,3 В в зависимости от того, в какую сторону распространяется сигнал. Вы можете купить их довольно дешево или приготовить их: http://www.hobbytronics.co.uk/mosfet-voltage-level-converter, @Nick Gammon
Спасибо за объяснение, я тоже займусь этим вопросом, хотя на данный момент того, что у меня есть, кажется достаточным., @David Liao