Музыкальный Чирикать


37

Это аудио версия задачи кодирования изображений в Twitter .

Разработайте формат сжатия звука, который может представлять по меньшей мере одну минуту музыки в 140 байтах или менее для печатаемого текста в кодировке UTF-8.

Реализуйте это, написав программу командной строки, которая принимает следующие 3 аргумента (после имени самой программы):

  1. Строка encodeили decode.
  2. Имя входного файла.
  3. Выходное имя файла.

(Если в вашем предпочтительном языке программирования отсутствует возможность использовать аргументы командной строки, вы можете использовать альтернативный подход, но должны объяснить это в своем ответе.)

encodeОперация преобразования из выбранного аудио формата в вашем сжатом формате «твит», а decodeоперация преобразования из вашего формата «чирикать» в оригинальном формате аудио. (Конечно, вы должны реализовать сжатие с потерями, поэтому выходной файл не обязательно должен быть идентичным входному, только в том же формате.)

Включите в свой ответ:

  • Исходный код вашей программы, в полном объеме. (Если она слишком длинная для этой страницы, вы можете разместить ее в другом месте и опубликовать ссылку на нее.)
  • Объяснение того, как это работает.
  • По меньшей мере, один пример со ссылкой на исходный аудиофайл (ы), текст «твита», в который он сжимается, и аудиофайл, полученный путем декодирования твита. (Ответчик несет ответственность за утверждения о добросовестном использовании авторских прав.)

правила

  • Я оставляю за собой право закрыть любые лазейки в правилах конкурса в любое время.
  • [Отредактировано 24 апреля] Для ввода вашей encodeфункции (и вывода вашей decodeфункции) вы можете использовать любой приемлемый, распространенный аудиоформат, будь то:
    • Несжатый сигнал, как WAV.
    • Сжатый сигнал, как MP3.
    • Стиль «Ноты», например, MIDI.
  • Ваш сжатый «твит» формат должен фактически кодировать звуки во входном файле. Итак, следующие типы вывода не учитываются:
    • URI или путь к файлу, указывающий место, где хранится фактический вывод.
    • Ключ к таблице базы данных, где фактический вывод хранится в виде большого двоичного объекта.
    • Ничего подобного.
  • Ваша программа должна быть разработана для сжатия общих музыкальных файлов, поэтому не делайте вещи, которые слишком явно связаны с вашей конкретной песней-примером. Например, если вы демонстрируете «Twinkle, Twinkle, Little Star», ваша процедура сжатия не должна жестко кодировать конкретный символ для последовательности «сделай сам».
  • Вывод вашей программы должен быть в состоянии проходить через Twitter и выходить невредимым. У меня нет списка точных поддерживаемых символов, но я стараюсь придерживаться букв, цифр, символов и знаков препинания; и избегайте управляющих символов, сочетания символов, маркеров BIDI или других странных вещей, подобных этому.
  • Вы можете подать более одной заявки.

Критерии оценки

Это конкурс популярности (т. Е. Выигрывает большинство чистых голосов), но избирателям настоятельно рекомендуется учитывать следующее:

точность

  • Можете ли вы распознать песню после того, как она была сжата?
  • Звучит хорошо?
  • Можете ли вы узнать, на каких инструментах играют?
  • Вы все еще можете узнать текст? (Это, вероятно, невозможно, но было бы впечатляющим, если бы кто-то достиг этого.)

сложность

Выбор песни примера имеет значение здесь.

  • [Добавлено 24 апреля] Эта задача будет проще всего с MIDI или аналогичными форматами. Однако, если вы приложите дополнительные усилия, чтобы заставить его работать с форматами типа волны, это заслуживает дополнительной оценки.
  • Какова структура? Конечно, вы можете выполнить требование в одну минуту, просто повторяя те же 4 измерения произвольное количество раз. Но более сложные песенные структуры заслуживают большего количества очков.
  • Может ли формат обрабатывать много нот, играемых одновременно?

Код

  • Держите это как можно более коротким и простым. Однако это не кодовый гольф, поэтому читаемость важнее, чем количество символов.
  • Умные, сложные алгоритмы тоже подойдут, если они оправданы улучшенным качеством результатов.

9
Использование MIDI по сравнению с WAV - это совершенно другая задача. Я думаю, что вы должны ограничить форматы только WAV.
grovesNL

10
Мне бы хотелось увидеть любые решения, но, если честно: упаковка 60-тизначного звука в 140 байт означает, что у вас доступно менее 19 бит в секунду. Существует несколько сверхэффективных речевых кодеров, которые работают со скоростью 300 бит / с, но они способны декодировать только на синтезированные фонемы с целью создания понятной речи и никак не могут кодировать музыку.
jarnbjo

2
Вы запрашиваете программное обеспечение с коэффициентами сжатия, которые на много порядков превышают текущее состояние. Если вы хотите получить разумные ответы (т.е. без участия таких композиций, как 4'33 " или" Похоронный марш для поводов для глухого " ), я бы посоветовал вам сократить временное ограничение до 1 секунды.
брезгливое оссифраж

3
@squeamishossifrage он не сказал, что это должно звучать узнаваемо, хотя.
cjfaure

5
В чате (и на следующий день) есть спор о том, имеете ли вы в виду 140 байтов или 140 символов размером с твит .
Питер Тейлор

Ответы:


26

Scala

Конечно, было бы проще кодировать MIDI-файлы, но у кого есть куча MIDI-файлов? Это не 1997!

Перво-наперво: я решил интерпретировать «байт Юникода» как «символ Юникода» и использовать символы CJK, потому что:

  • Это соответствует требованию изображения
  • Твиттер крут с этим
  • Мне действительно нужны эти биты

Есть несколько приемов, которые я использую, чтобы выжать каждую последнюю каплю энтропии из источников:

Во-первых, музыка сделана с нотами. Более того, мы обычно рассматриваем одну и ту же ноту в другой октаве как одну и ту же ноту (именно поэтому 12-струнная гитара звучит правильно), поэтому у нас есть только 12 возможностей для кодирования. (например, когда я вывожу B, я фактически вырабатываю аккорд, состоящий исключительно из B во всех октавах, что-то вроде 12-струнной гитары).

Далее, я помню из школьного музыкального класса, что большинство переходов нот маленькие (вверх или вниз на одну ноту). Прыжки встречаются реже. Это говорит нам о том, что, вероятно, энтропия меньше в размерах прыжков, чем в самих нотах.

Итак, наш подход состоит в том, чтобы разбить наш источник на несколько блоков - я обнаружил, что 14 блоков в секунду работают хорошо (примечание: мне всегда было интересно, почему аудио кодировалось с частотой 44100 Гц. Оказывается, что 44100 имеет много факторов, так что я мог бы выбрать 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 14, 15, 18, 20, 21, 25, 28 или 30 блоков в секунду, и он бы четко разделился ). Затем мы БПФ эти блоки (ну, технически не быстро, так как библиотека, которую я использовал, не быстро для блоков не степени 2. И технически я использовал преобразование Хартли , а не Фурье).

Затем мы находим заметку, которая звучит громче (я использовал A-взвешивание с высокими и низкими отсечками, в основном потому, что ее легче всего реализовать), и либо кодируем эту заметку, либо кодируем тишину (обнаружение молчания основано на SNR - низком SNR это тишина).

Затем мы переводим наши закодированные заметки в скачки и передаем их адаптивному арифметическому кодеру. Процесс перевода в текст похож на вопрос о сжатии изображений (но включает в себя некоторое злоупотребление BigInteger).

Пока все хорошо, но что, если в образце слишком много энтропии? Мы используем грубую психоакустическую модель, чтобы удалить некоторые из них. Самый низкий энтропийный скачок - «без изменений», поэтому мы смотрим на наши данные FFT, чтобы попытаться найти блоки, где слушатель, вероятно, не заметит, если мы продолжим воспроизведение предыдущей ноты, ища блоки, где нота из предыдущего блока почти так же громко, как самая громкая нота (где «почти» контролируется параметром качества).

Итак, у нас есть цель 140 символов. Мы начнем с кодирования с качеством 1.0 (максимальное качество) и посмотрим, сколько символов это. Если это слишком много, мы снижаемся до 0,95 и повторяем, пока не получим 140 символов (или не сдадимся после качества 0,05). Это делает кодировщик n-проходным кодером для n <= 20 (хотя в других областях он также крайне неэффективен, так что м'е).

Кодер / декодер ожидает аудио в монофоническом формате s16be. Это может быть достигнуто с помощью avconv как:

#decoding ogg to s16be, and keeping only the first 60s
avconv -i input.ogg -ac 1 -ar 44100 -f s16be -t 60s input.raw
#encoding s16be to mp3
avconv -f s16be -ac 1 -ar 44100 -i output.raw output.mp3

Чтобы запустить кодировщик:

sbt "run-main com.stackexchange.codegolf.twelvestring.TwelveString encode input.raw encoded.txt"
sbt "run-main com.stackexchange.codegolf.twelvestring.TwelveString decode encoded.txt output.raw"

Полный код на https://github.com/jamespic/twelvestring .

Заметьте: вам понадобится библиотека арифметического кодирования nayuki, в которой в настоящее время нет доступных артефактов Maven. Вместо этого вам нужно будет локально собрать и установить ветвь разработчика .

А вот несколько примеров. Они звучат ужасно, но почти узнаваемо

  • 5-е Бетховена: оригинал , закодированный - 刲 檁 囉 罓 佖 镱 皌 蝩 蔲 恁 峕 逊 呯 呯姑椻趕挍呪白鸞盙宠埘謭擆闯脲誜忘椐笸囃庣稨俖咛脔湙弻籚砌鍖裏橈镙訁鹻塿骱踠筟七趇杅峇敖窈裞瘫峦咰呹櫬茏蛏姆臸胝婁遼憀麁黦掏毈喙眝綄鴀耢椚筤菮蟞斗俼湛营筬禴籙嬧窻丄
  • Мех Elise: оригинал , закодированный - 訖 忢 擫 鏝 拪 铇 铇 鉰 鉰 暱 埛 痏 痏 僌 莻 暆苯劺誺軩忇穤锳婁伉巠桭晘酉筟緵俅怚尵鎽蜓崁飿嘔但鈐謽酝屮呤誥俊覊鶮龔癸埙煂臙牎繬肜摲炚雗頨款驦燈菩凧咁楾媡夥俰欓焵韀冊嗥燠鱧駟髉
  • Twinkle Twinkle Little Star: оригинальная , закодированная - 欠 悺 矜 莳 錥 鷗 谴 裴 鳺 憝 漿 箔 皇 殤 殤 猻 猻 丱
  • Интересный чиптюн: оригинальный , закодированный - 简 詐 諥 尘 扫 鲑 箫 颫 齰 騁 騁 煟 靸 阒 萎 囦灼攲陇亄鹘琾業纟鵼牤棌匩碆踬葫鶙懲欃铳樯柇鋡疡衻澯伅墇搢囻荸香貱夹咽蟽籐屈锂蛉袒貊屨鈦夜镤沄鍡唦魮骬乔蚖醶矕咻喸碋利褼裊匎嶮窢幘六沧鼝瞮坡葍帷锆邵旻符琨鱴郤栱烇仾椃騋荄嘵統篏珆罽

Обновить

Я подправил порог молчания в коде и перекодировал. Кодировки были обновлены соответствующим образом. Кроме того, я добавил еще одну песню (технически не с открытым исходным кодом, но я сомневаюсь, что первоначальный правообладатель будет чувствовать, что его IP-адрес находится под угрозой), просто для удовольствия:

  • Имперский марш: оригинал , закодированный - enco 讶 湠 衢 嫵 喋 藭 憣 嗗 颟 蕖唂焰銯艉鶱縩巻痭虊窻熲紆耺哅淙苉嘏庸锺禒旇蘄籷遪刨繱蕖嬯摺祑仰軈牰杊瘷棏郖弘卄浕眮騜阖鏴鶺艂税寛柭菸採偋隆兎豅蚦紛襈洋折踜跅軩树爺奘庄玫亳攩獼匑仡葾昐炡瞱咏斎煟价藭恐鷖璌榍脅樐嬨勀茌愋

Больше обновлений

Я немного подправил кодировщик, и это оказало удивительное влияние на качество (я забыл, что в DHT синфазные сигналы фактически отрицательны, поэтому я игнорировал синфазные сигналы).

Более ранняя версия кода просто принимала больший из этих синфазных сигналов, но теперь мы берем RMS. Кроме того, я добавил довольно консервативную оконную функцию в кодировщик (Tukey, alpha 0.3), чтобы попытаться бороться с артефактами.

Все обновляется соответственно.


1
Я не могу играть в Twinkle Twinkle и на чиптюне. Мех Elise довольно близко, в то время как Бетховен едва узнаваем, хаха.
justhalf

Хотите попробовать Twinkle Twinkle и Chiptune снова? Я думаю, что я исправил URL.
James_pic

1
Это работает сейчас. Twinkle Twinkle довольно спуск. Но что происходит в конце?
justhalf

Да, я не совсем уверен, что происходит в конце. Я подозреваю, что это происходит где-то в арифметическом кодировании. В более ранней версии кода поток был завершен символом EOF, но в некоторых случаях декодер не мог прочитать символ EOF. Я подозреваю, что я не закрыл BitOutputStream правильно, но я рассмотрю его.
James_pic

1
Да, на самом деле это было именно так. Был BitOutputStream::closeметод, который я забыл вызвать. Я исправлю код и обновлю выводы.
James_pic

11

питон

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

Я использовал частоту дискретизации 44100 Гц для входа и выхода. SAMPLES_PER_BYTE контролирует качество конверсии. Чем меньше число, тем лучше качество звука. Используемые мной значения приведены в разделе результатов.

использование

шифровать

Входной файл должен быть WAV. Кодирует только первый канал.

twusic.py -e [input file] > output.b64

раскодировать

twusic.py -d [input file] > output.raw

Воспроизведение декодированной музыки

aplay -f U8 --rate=[rate of input file] output.raw

Код

#!/usr/bin/env python
SAMPLES_PER_BYTE = 25450

from math import sin, pi, log
from decimal import Decimal

PI_2 = Decimal(2) * Decimal(pi)

FIXED_NOTE = Decimal('220') # A
A = Decimal('2') ** (Decimal('1') / Decimal('12'))
A_LN = A.ln()

def freq(note):
    return FIXED_NOTE * (A ** Decimal(note))

def note(freq):
    return (Decimal(freq) / FIXED_NOTE).ln() / A_LN

VOLUME_MAX = Decimal('8')
def volume(level):
    return Decimal('127') * (Decimal(level+1).ln() / VOLUME_MAX.ln())

def antivolume(level):
    x = Decimal(level) / Decimal('127')
    y = VOLUME_MAX ** x
    return y - 1

NOTES = [freq(step) for step in xrange(-16, 16)]
VOLUMES = [volume(level) for level in xrange(0, VOLUME_MAX)]


def play(stream, data):
    t = 0
    for x in data:
        x = ord(x)
        w = PI_2 * NOTES[(x&0xf8) >> 3] / Decimal(16000)
        a = float(VOLUMES[x&0x07])
        for _ in xrange(0, SAMPLES_PER_BYTE):
            stream.write(chr(int(128+(a*sin(w*t)))))
            t += 1

NOTE_MAP = {'A': 0b00000000,
    'g': 0b00001000,
    'G': 0b00010000,
    'f': 0b00011000,
    'F': 0b00100000,
    'E': 0b00101000,
    'd': 0b00110000,
    'D': 0b00111000,
    'c': 0b01000000,
    'C': 0b01001000,
    'B': 0b01010000,
    'a': 0b01011000}

def convert(notes, volume):
    result = []
    for n in notes:
        if n == ' ':
            result += '\00'
        else:
            result += chr(NOTE_MAP[n] | (volume & 0x07)) * 2
    return ''.join(result)

TWINKLE = convert('C C G G A A GG' +
                    'F F E E D D CC' +
                    'G G F F E E DD' +
                    'G G F F E E DD' +
                    'C C G G A A GG' +
                    'F F E E D D CC', 0x7)

if __name__ == '__main__':
    from base64 import b64encode, b64decode
    import numpy as np
    from numpy.fft import fft, fftfreq
    import wave
    import sys

    if len(sys.argv) != 3:
        print 'must specify -e or -d plus a filename'
        sys.exit(1)

    if sys.argv[1] == '-e':
        w = wave.open(sys.argv[2], 'rb')

        try:
            output = []
            (n_channels, sampwidth, framerate, n_frames, comptype, compname) = w.getparams()
            dtype = '<i' + str(sampwidth)

            # Find max amplitude
            frames = np.abs(np.frombuffer(w.readframes(n_frames), dtype=dtype)[::n_channels])
            max_amp = np.percentile(frames, 85)

            w.rewind()

            read = 0
            while read < n_frames:
                to_read = min(n_frames-read, SAMPLES_PER_BYTE)
                raw_frames = w.readframes(to_read)
                read += to_read

                frames = np.frombuffer(raw_frames, dtype=dtype)[::n_channels]
                absolute = np.abs(frames)
                amp = np.mean(absolute)

                amp = int(round(antivolume(min((amp / max_amp) * 127, 127))))

                result = fft(frames)
                freqs = fftfreq(len(frames))

                while True:
                    idx = np.argmax(np.abs(result)**2)
                    freq = freqs[idx]
                    hz = abs(freq * framerate)
                    if hz > 0:
                        break
                    result = np.delete(result, idx)
                    if len(result) <= 0:
                        hz = 220
                        amp = 0
                        break

                n = int(round(note(hz)))
                n &= 0x1F
                n <<= 3
                n |= amp & 0x07
                output.append(chr(n))
        finally:
            w.close()
        print b64encode(''.join(output)).rstrip('=')
    else:
        with open(sys.argv[2], 'rb') as f:
            data = f.read()
        data = data + '=' * (4-len(data)%4)
        play(sys.stdout, b64decode(data))

Входы

Мое официальное представление - « Экспромт» для Pianoforte и Beatbox от Кевина МакЛауда . Для этого файла я использовал SAMPLES_PER_BYTE из 25450.

Я также взял на себя смелость кодировать Twinkle, Twinkle, Little Star с SAMPLES_PER_BYTE 10200. Звучит намного лучше.

Выход

Экспромт для фортепиано и битбокса

aWnxQDg4mWqZWVl6W+LyOThfHOPyQThAe4x5XCqJK1EJ8Rh6jXt5XEMpk1Epe5JqTJJDSisrkkNCSqnSkkJDkiorCZHhCxsq8nlakfEp8vNb8iqLysp6MpJ7s4x7XlxdW4qKMinJKho

Ссылка

Twinkle, Twinkle Маленькая Звезда

HBobGlJSUlJSY2FlYVNRUVFCQkJCQjs5PDksKisqGxoZGVFTUVNRREFDQjs6OjoqKykpKVRRVFJDQkJCOjs6OzksKikpGxobG1JSUlNRZWFlYVNSUVFCQkJDQTw5PDorKisqGhsZGRk

Ссылка

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.