Code Golf — Отображение Midi-Трека

  • Автор темы Infomancer
  • Обновлено
  • 22, Oct 2024
  • #1

Фон

MIDI-файлы сильно отличаются от аудиофайлов WAV или MP3. Файлы MP3 и WAV содержат байты, представляющие «запись» звука, в то время как MIDI-файлы содержат серию MIDI-сообщений, хранящихся в MIDI-событиях, информирующих MIDI-синтезатор, на каком виртуальном инструменте играть, или MIDI-секвенсор, какой темп воспроизведения следует использовать. Эти сообщения хранятся в треках, а набор треков составляет MIDI-последовательность, события которой могут анализироваться секвенсором и передаваться сообщения от секвенсора к приемнику синтезатора.

Большую часть времени MIDI-сообщения, хранящиеся в MIDI-событиях, представляют собой сообщения Note On, которые сообщают синтезатору воспроизвести определенную ноту, или сообщения Note Off, которые сообщают синтезатору прекратить воспроизведение ноты. Эти сообщения содержат два байта данных, первый из которых сообщает синтезатору скорость ноты (более высокая скорость приводит к более громкой ноте), а второй из которых сообщает синтезатору, какую ноту нужно сыграть (т. е. среднюю до). Сами события также содержат метки, которые сообщают секвенсору, когда отправлять сообщения.

Вызов

Задача состоит в том, чтобы написать полную программу или функцию, которая анализирует серию MIDI-сообщений Note On и Note Off в однодорожечной MIDI-последовательности и выводит на STDOUT диаграмму, показывающую, когда определенные ноты включены, когда они выключены и скорость этих нот. Вертикальная ось диаграммы представляет значение ноты и должна быть помечена, как описано ниже, а горизонтальная ось представляет время в тактах MIDI (хотя она не должна быть помечена, чтобы уменьшить сложность и проблемы с интервалами).

Ваши входные данные могут представлять собой четыре отдельных массива или списка, каждый из которых содержит серию целочисленных значений; двумерный массив или список, содержащий четыре подмассива/подсписка с серией целочисленных значений; или любым другим удобным способом; это представляет коллекцию MIDI-событий с сообщениями Note On и Note Off на треке. Значения в первом из этих массивов определяют ноту, второй — скорость, третий — ноту при тике события, а четвертый — тик события при выключении ноты. Например, даны четыре таких массива:

 
 
 {48, 55, 64, 64, 65, 67, 55, 67, 65, 64, 62, 52, 55,  60,  60,  62,  64,  55, 64, 62, 62}
{45, 45, 63, 63, 63, 63, 89, 66, 66, 66, 66, 30, 30, 103, 103, 103, 103, 127, 55, 55, 55}
{ 0,  0,  0,  4,  8, 12, 16, 16, 20, 24, 28, 32, 32,  32,  36,  40,  44,  48, 48, 54, 56}
{16, 16,  2,  6, 10, 14, 32, 18, 22, 26, 30, 48, 48,  34,  38,  42,  46,  64, 50, 55, 64}

127|
...
67 |            --  @@
66 |
65 |        --          @@
64 |--  --                  @@                  00  --
63 |
62 |                            @@          00            - --------
61 |
60 |                                00  00
59 |
58 |
57 |
56 |
55 |################++++++++++++++++================****************
54 |
53 |
52 |                                ================
51 |
50 |
49 |
48 |################
...
0  |
 

Анализ первого элемента каждого массива дает два события: событие на тике 0 с сообщением, имеющим команду Note On, ноту 60 (средняя C) и скорость ноты 20; и событие на тике 2 с сообщением, содержащим команду Note Off с той же нотой и скоростью.

Правила

На диаграмме должны быть показаны числа от 0 до 127, отображаемые в порядке убывания слева (представляющие значение ноты), время начала ноты, продолжительность каждой ноты (галочка «Нота выключена» минус галочка «Нота включена») и скорость ноты. Символы, обозначающие ноты, зависят от их скорости:

  • 0-15: {60, 62, 64, 65, 67} {20, 40, 60, 80, 100} { 0, 4, 8, 12, 16} { 2, 6, 10, 14, 18} 127| 126| 125| ... 67 | 00 66 | 65 | ++ 64 | -- 63 | 62 | ## 61 | 60 |== 59 | ... 2 | 1 | 0 | {60, 48, 62, 47, 64, 45, 65, 43, 67, 41, 65, 43, 64, 45, 62, 47, 60, 48} {63, 31, 75, 90, 12, 23, 122, 104, 33, 19, 57, 42, 5, 82, 109, 86, 95, 71} {0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16} {2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 18} 127| 126| ... 68 | 67 | ## 66 | 65 | ** -- 64 | OO OO 63 | 62 | @@ 00 61 | 60 |-- ++ 59 | ... 49 | 48 |== @@ 47 | ++ ++ 46 | 45 | == ++ 44 | 43 | 00 ## 42 | 41 | == 40 | ... 1 | 0 |
  • 16-31: *
  • 32-47: 0
  • 48-63: +
  • 64-79: @
  • 80-95: -
  • 96-111: #
  • 112-127: =

Вы можете предположить следующее:

  • Значения ноты и скорости будут находиться в диапазоне [0, 127].
  • Длины каждого из четырех массивов всегда будут равны друг другу.

Вот несколько примеров:

O

Вот пример, отображающий первые несколько нот «Оды к радости»:

{60, 62, 64, 65, 67} {20, 40, 60, 80, 100} { 0, 4, 8, 12, 16} { 2, 6, 10, 14, 18}

Ты можешь уменьшить свой счет на 25% если ваша заявка принимает фактическую последовательность MIDI в качестве входных данных, анализирует сообщения Note On и Note Off любой дорожки по вашему выбору, при условии, что она содержит как минимум четыре события с сообщениями Note On и Note Off, и выводит диаграмму, как описано выше.

Это кодовый гольф, поэтому выигрывает самый короткий код. Удачи!

#code-golf #ascii-art #music

Infomancer


Рег
18 Apr, 2007

Тем
77

Постов
190

Баллов
595
  • 26, Oct 2024
  • #2

PHP, 127 + 571 = 698 общий балл*

Хорошо, я забираю бонус. :) Это возьмет стандартный MIDI-файл и отобразит результат.

Я разбил приведенную выше партитуру на основную задачу (включение/выключение нот и отображение в виде диаграммы) и дополнительную задачу (чтение входных данных из стандартного MIDI), чтобы сделать оценки более сопоставимыми.

Основной: 170 байт — 25% = 127

В основном функция

 
 
 
 
 
 
 
 
 ®Æ"O=#-@+0*"gXzG          #Gets the velocity character to use for each note
®                         # For each note in the input

Æ                        # Replace the last item X with:

XzG          #  Integer divide X by 16

"O=#-@+0*"g             #  Get the character at that index in the string "O=#-@+0*"

#€Çs ú3 +'|ÃúUmg2 rÔ+5    #Generate the blank chart
#€Ç        à              # For each number X in the range [0...127]:

s                      #  Turn X into a string

ú3                   #  Right-pad with spaces until it is 3 characters long

+'|               #  Add "|" to the end

ú             # Right pad each of those with spaces to this length:

Umg2         #  Get all the end_tick values

rÔ      #  Find the largest one

+5    #  Add 5

£VhXÎVgXv)hXÎ+4Xo pXra    #Put the notes into the chart
£                         # For each note:

VgXv)                #  Get a line from the chart based on the note's pitch

h               #  Overwrite part of that line:

XÎ+4           #   Starting at index start_tick +4

Xo         #   Overwrite characters with the velocity character

pXra    #   For the next end_tick - start_tick characters

VhXÎ                     #  Put the modified line back into the chart

Vw                        #Print the chart
V                         # Get the chart

w                        # Reverse it (so 127 is the first line)

# Implicitly print it
 
takes the required array and displays the ASCII output. Included are all tests and output of test MIDI file below.

[pitch, start_tick, end_tick, velocity]

Попробуйте онлайн!

Бонус: 761 байт — 25% = 571

Функция ®Æ"O=#-@+0*"gXzG #€Çs ú3 +'|ÃúUmg2 rÔ+5 £VhXÎVgXv)hXÎ+4Xo pXra Vw will load a standard MIDI file (either locally or by URL) and return an array of tracks, each containing an array in the specified note format for all of the MIDI file tracks.

a=input();z=[" "*max(a[3])]*128 for n,v,b,e in zip(*a):z[n]=z[n][:b]+"O=#-@+0*"[v/16]*(e-b)+z[n][e:] for i in range(128)[::-1]:print"%3d|"%i+z[i]

Смотрите онлайн! Очевидно, что TIO изолирован в песочнице, чтобы не разрешать удаленные запросы или локальные файлы, поэтому вам придется запустить этот код локально, чтобы увидеть его в действии. Первый [tests][TIO-jrwa60tu] в функции отображения включает в себя результат массива из тестового MIDI-файла.

Процедура загрузки MIDI-файла отключена:

9e9

Тестовый MIDI-файл «Оды к радости», который можно использовать, можно скачать здесь. Пример использования:

1e4

Вывод MIDI-файла «Ода к радости»

less

Примечания

В формате MIDI события Note On/Note Off являются атомарными, что означает, что вы видите событие Note On в определенное время для данной ноты (скажем, E5), и подразумевается, что оно будет воспроизводиться до тех пор, пока не произойдет событие Note Off для другой ноты E5. видно. Таким образом, необходимо проанализировать MIDI-события и сопоставить данную ноту с ее выключенной нотой, код которой следующий: 297 184 байта. Еще больше усложняет ситуацию то, что в стандартном формате MIDI довольно часто можно увидеть последующее включение ноты со скоростью 0, представляющей то же самое, что и выключение ноты.

Теперь это будет правильно читать файлы, у которых нота включена с нулевой скоростью вместо ноты выключена, поэтому должно открываться большинство стандартных файлов.

Предостережения

Это ни в коем случае не полная реализация формата MIDI, однако я протестировал его с довольно обширной коллекцией MIDI-файлов, и он все их прекрасно читает.

Это представление еще не доведено до крайности, поэтому вполне вероятно, что его можно уменьшить. Я действительно думаю, что очень маловероятно, что бонус за снижение баллов на 25% компенсирует код, необходимый для чтения стандартного MIDI-файла. Поскольку (текущая) наименьшая заявка, которая просто отображает ASCII, 106 65 байт, это потребует реализации процедур MIDI-файлов в 25 21 байт, чтобы победить. Я бы посоветовал любому сделать это (без использования встроенного языка или модуля). :)

 

Nishitov


Рег
20 Nov, 2019

Тем
68

Постов
207

Баллов
567
  • 26, Oct 2024
  • #3

Рубин, 106 байт

Это было весело. Я не уверен, почему никто не попытался это сделать.

Эта функция принимает входные данные в виде четырех аргументов массива и возвращает массив строк, по одной для каждой строки диаграммы.

->a,*r{q=(0..z=127).map{|i|"%3d|"%(z-i)+" "*1e4} a.zip(*r){|n,v,o,f|q[z-n][o+4]="O=#-@+0*"[v/16]*(f-o)} q}

Примечание: Это условно предполагает, что тиков будет не более 10 000. Если вы запустите его в своем терминале, я предлагаю передать его по конвейеру 67 | 0000++++ 00000000 00000000 66 | 65 | 0000 ++++ 0000 0000 @@ @@ 0000 ++++ 64 |++++++++ ++++ 0000000000 00000000 0000 0000 @@@@ @@ ---- @@ ---- ++++++++++++ ++++ 0000 63 | 62 | ++++ 0000 00++++++++ ++++ 0000 000000 @@@@---- ---- @@@@ ---- ---- ++++ 0000 000000 61 | 60 |++++ ++++0000 0000 ++++0000 ++00000000 ---- ---- ---- 00000000 ++++0000 **** ++00000000 59 | ++++++++ 58 | 00000000 57 | ---- ++++++++ 56 | -------- 55 |++++++++++++++++++++++++00000000000000000000000000000000++++++++00000000000000000000000000000000000000000000000000000000 ---------------------------------------- -------- 0000 ++++++++00000000 54 | ---- 53 | ++++++++ 52 | 0000000000000000 0000000000000000 ++++0000 00000000 51 | 50 | 49 | 48 |++++++++++++++++ 0000000000000000 0000000000000000 0000000000000000 ++++++++ 00000000 so you can scroll horizontally. You can change $d( $m( 'beethoven_ode_to_joy.mid' )[0] ); // display first track $d( $m( 'https://www.8notes.com/school/midi/piano/beethoven_ode_to_joy.mid' )[0] ); foreach( $m( 'multi_track_song.mid' ) as $t ) { // display all tracks $d( $t ); } если вы хотите больше тиков, вплоть до $m=fopen($f,'r'); // m = midi file handle while($b=fread($m,8)){ // read chunk header $z=unpack('N2',$b)[2]; // z = current chunk size if($b[3]=='d'){ // is a header chunk? $k=unpack('n3',fread($m,$z))[3]/4; // k = ticks per quarter note (you can change the 4 to 8 or 16 to "zoom in" so each char represents eights or sixteenth notes) }else{ // is a track chunk? $t=0; // track/chunk time offset starts at 0 $d=ftell($m)+$z; // d = end of chunk file pos while(ftell($m)<$d){ // q = current file pos $t+=$a($m); // decode var length for event offset and add to current time if(($e=unpack('C',fread($m,1))[1])==255){ // is a META event fread($m,1); // read and discard meta event type if($w=$a($m)) fread($m,$w); }else{ // is a MIDI event if($e>127) { // is a new event type list(,$e,$h)=unpack('C*', // if is a prog change (0x0c), event is 1 byte fread($m,($y=(240&$e)>>4)==12?1:2)); // otherwise read 2 bytes } else { // is a MIDI "streaming" event, same type as last $h=unpack('C',fread($m,1))[1]; } if($y==9|$y==8) // if is a Note On or Note Off $n[]=[$e,$h,(int)round($t/$k),0,$y]; // add note to output } } if($n) // if this track has notes, $u[]=$r($n); // add to array of output tracks ($u) } } fclose($m); // yes, could golf this out and rely on PHP GC to close it , but that will take a terabyte or two of RAM.

Посмотрите это на repl.it: https://repl.it/Cx4I/1

 

SauttcratoRer


Рег
06 May, 2014

Тем
61

Постов
216

Баллов
531
  • 26, Oct 2024
  • #4

Python 2, 163 160 156 145 байт

Это не самый простой способ сделать это, но один из самых простых. Если бы я мог придумать, как заменять части строк, не превращая их в списки, не заменяя и не превращая их обратно в строки, это было бы здесь очень полезно. Предложения по игре в гольф приветствуются.

Редактировать: 18 байт благодаря Leaky Nun. Попробуйте на Ideone!

$m=function($f){$a=function($f){do$s=($s<<7)+(($c=unpack(C,fread($f,1))[1])&127);while($c&128);return$s;};$r=function($n){foreach($n as$e){if($e[4]==9&&$e[1]>0)foreach($n as$y=>$f)if($f[0]==$e[0]&&($f[4]==8||($f[4]==9&&$f[1]==0))){$o[0][]=$e[0];$o[1][]=$e[1];$o[2][]=$e[2];$o[3][]=$f[2];$n[$y][4]=0;break;}}return$o;};$m=fopen($f,r);while($b=fread($m,8)){$z=unpack(N2,$b)[2];if($b[3]==d){$k=unpack(n3,fread($m,$z))[3]/4;}else{$t=0;$n=[];$d=ftell($m)+$z;while(ftell($m)<$d){$t+=$a($m);if(($e=unpack(C,fread($m,1))[1])==255){fread($m,1);if($w=$a($m))fread($m,$w);}else{if($e>127)list(,$e,$h)=unpack('C*',fread($m,($y=(240&$e)>>4)==12?1:2));else$h=unpack(C,fread($m,1))[1];if($y==9|$y==8)$n[]=[$e,$h,(int)round($t/$k),0,$y];}}if($n)$u[]=$r($n);}}fclose($m);return$u;}; ||answer||

Япт, 65 байт

$m()

Попробуйте онлайн!

Принимает ввод в виде списка заметок в формате $d=function($a){for($l=max($n=$a[0]);$l>=min($n);){$r=' |';foreach($n as$c=>$e)while($e==$l&$a[2][$c]<$a[3][$c])$r[++$a[2][$c]+1]='O=#-@+0*'[$a[1][$c]/16];echo$l--,$r," ";}} . If taking input as separate lists is mandatory (i.e. one list containing all the pitches, one containing all the velocities etc.), that can be accomplished at the cost of 1 байт.

Объяснение:

$d()
 

Mishania90


Рег
10 Jan, 2007

Тем
80

Постов
209

Баллов
659
Тем
403,760
Комментарии
400,028
Опыт
2,418,908

Интересно