Музичний твіт Challenge


37

Це звукова версія завдання кодування зображень Twitter .

Створіть формат стиснення аудіо, який може представляти принаймні одну хвилину музики в 140 байтах або менше тексту для друку, що кодується UTF-8.

Реалізуйте це, написавши програму командного рядка, яка бере наступні 3 аргументи (після назви самої програми):

  1. Рядок encodeабоdecode .
  2. Ім'я вхідного файлу.
  3. Вихідне ім'я файлу.

(Якщо у вашій бажаній мові програмування відсутня можливість використовувати аргументи командного рядка, ви можете використовувати альтернативний підхід, але слід пояснити це у своїй відповіді.)

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

Включіть у свою відповідь:

  • Вихідний код вашої програми, повністю. (Якщо ця сторінка задовга, ви можете розмістити її в іншому місці та опублікувати посилання на неї.)
  • Пояснення, як це працює.
  • Принаймні один приклад із посиланням на оригінальний аудіофайл (файли), текст "твіт", який він стискає, та аудіофайл, отриманий декодуванням твіту. (Відповідач відповідає за твердження про «добросовісне використання» авторських прав.)

Правила

  • Я залишаю за собою право закрити будь-які лазівки в правилах конкурсу в будь-який час.
  • [Відредаговано 24 квітня] Для введення encodeфункції (та виведення decodeфункції) ви можете використовувати будь-який розумний, поширений аудіо формат, будь то:
    • Нестиснута форма хвилі, як WAV.
    • Стиснута форма хвилі, як MP3.
    • Стиль "нотна музика", як MIDI.
  • Ваш стиснений формат "твіт" повинен фактично кодувати звуки у вхідному файлі. Отже, такі типи продукції не враховуються:
    • URI або шлях до файлу, який дає місце, де зберігається фактичний вихід.
    • Ключ до таблиці баз даних, де фактичний вихід зберігається як крапка.
    • Нічого подібного.
  • Ваша програма повинна бути розроблена для стиснення загальних музичних файлів, тому не робіть матеріалів, які занадто очевидно прив'язані до вашої конкретної приклади пісні. Наприклад, якщо ви демонструєте "Мерехтіння, Мерехтіння, Маленька зірка", ваша програма стиснення не повинна жорстко кодувати конкретний символ для послідовності "зробіть-так-так-ля-ля-так".
  • Результати вашої програми насправді повинні мати можливість пройти через Twitter і вийти непошкодженими. У мене немає списку точних символів, які підтримуються, але спробуйте дотримуватися літер, цифр, символів та пунктуації; і уникайте контрольних символів, комбінуючи символи, маркери BIDI або інші подібні дивні речі.
  • Ви можете подати більше одного запису.

Критерії судження

Це змагання за популярність (тобто виграє більшість чистих результатів), але виборці закликають враховувати наступне:

Точність

  • Чи можете ви ще розпізнати пісню після її стиснення?
  • Це добре звучить?
  • Ви ще можете розпізнати, на яких інструментах граєте?
  • Ви ще можете розпізнати тексти пісень? (Це, мабуть, неможливо, але було б вражаюче, якби хтось це досяг.)

Складність

Тут має значення вибір прикладу пісні.

  • [Додано 24 квітня] Цей виклик буде найпростішим з MIDI або подібними форматами. Однак якщо ви докладете додаткових зусиль, щоб змусити його працювати з форматами типу хвилі, це заслуговує на додатковий кредит.
  • Яка структура? Звичайно, ви можете виконати однохвилинну вимогу, просто повторивши ті ж самі 4 міри довільну кількість разів. Але складніші композиції пісні заслуговують більше балів.
  • Чи може формат обробляти одночасно багато відтворених нот?

Кодекс

  • Зберігайте це якомога коротше і просто. Однак це не кодовий гольф, тому читабельність має значення більше, ніж кількість символів.
  • Розумні, складні алгоритми теж добре, якщо вони виправдані покращеною якістю результатів.

9
Використання MIDI проти WAV - кардинально інша проблема. Я думаю, ви повинні обмежити формати лише WAV.
grovesNL

10
Мені дуже хочеться бачити будь-які рішення, але якщо чесно: пакування 60-ти звуку в 140 байт означає, що у вас менше 19 біт на секунду. Існує кілька ультраефективних кодерів мови, які працюють зі швидкістю 300 bps, але вони здатні розшифрувати лише синтезовані фонеми з метою створення зрозумілої мови і ніяким чином не здатні кодувати музику.
jarnbjo

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

3
@squeamishossifrage він не сказав, що це повинно звучати впізнавано.
cjfaure

5
У чаті (і наступного дня) є аргумент про те, чи маєте ви на увазі 140 байт або 140 символів розміром твіт .
Пітер Тейлор

Відповіді:


26

Скала

Звичайно, було б простіше кодувати файли MIDI, але хто має купу файлів MIDI? Це не 1997 рік!

Спочатку: спочатку я вирішив інтерпретувати байт Unicode як "Unicode char" і використовувати символи CJK, оскільки:

  • Це відповідає зображенню
  • Twitter це круто
  • Мені справді потрібні ці шматочки

Є кілька хитрощів, які я використовую, щоб вичавити кожну останню краплю ентропії з джерел:

По-перше, музика робиться з нотами. Більше того, ми зазвичай вважаємо ту саму ноту в іншій октаві як ту саму ноту (саме тому 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 блоків на секунду, і це було б розділено чисто ). Потім ми FFT ці блоки (ну, технічно це не швидко, оскільки бібліотека, яку я використовував, не є швидкою для блоків без потужності. А технічно я використовував перетворення Хартлі , а не Фур'є).

Потім ми виявляємо ноту, яка звучить найгучніше (я використовував A-зважування , з високим і низьким відсіченням, головним чином тому, що це найпростіше здійснити), або або кодувати цю ноту, або кодувати тишу (виявлення тиші базується на SNR - низький SNR є тиша).

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

Поки що так добре, але що робити, якщо в вибірці занадто багато ентропії? Ми використовуємо грубу психоакустичну модель для видалення деяких. Найнижчий стрибок ентропії - "без змін", тому ми дивимося на наші дані FFT, щоб спробувати знайти блоки, де слухач, ймовірно, не помітить, якщо ми продовжуватимемо грати попередню ноту, шукаючи блоки, де нота з попереднього блоку майже так само голосно, як найгучніша нота (де "майже" контролюється параметром якості).

Отже, у нас є ціль у 140 символів. Почнемо з кодування з якості 1,0 (максимальна якість), і подивимося, скільки символів це. Якщо їх занадто багато, ми опускаємось до 0,95 і повторюємо, поки не досягнемо 140 символів (або не здамося після якості 0,05). Це робить кодер n-прохідним кодером, для n <= 20 (хоча він і в інших областях масово неефективний, так що m'eh).

Кодер / декодер очікує звуку у моноформаті 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-го Бетховена: оригінал , закодований - 刲 檁 囉 罓 佖 镱 賑 皌 蔲 恁 峕 逊 躹 呯 兲 搆 摼 筶 槛 庬 一 掛 獴 趤 笲 銗 娵 纜 覤 粠 僭 嫭 裵 獄 鱨 蠰 浝 儏 儏 儏姑 椻 趕 挍 呪 白 鸞 盙 宠 埘 謭 擆 闯 誜 忘 椐 笸 囃 庣 稨 俖 脔 湙 弻 籚 砌 鍖 裏 橈 镙 訁 鹻 塿 骱 筟 七 趇 杅 峇 敖 窈 裞 瘫 瘫 瘫 峦茏 蛏 姆 臸 胝 婁 遼 憀 麁 黦 掏 毈 喙 眝 鴀 耢 椚 筤 菮 蟞 斗 俼 湛 营 禴 籙 嬧 窻 窻
  • Fur Elise: оригінал , закодований - 訖 忢 擫 鏝 拪 纒 铇 鯪 薯 鉰 暱 埛 痏 絘 僌 莻 暆 鴈 屖 絒 婘 譮 蠣 託 騶 腀 饚 緂 柤 碠 瞢 脅 歙 棆 敗 辦 辦 冏 鄔 酾 酾苯 劺 誺 軩 忇 穤 锳 婁 伉 巠 桭 晘 酉 緵 俅 怚 尵 鎽 蜓 崁 飿 但 鈐 鈐 酝 屮 呤 誥 俊 覊 鶮 龔 癸 埙 臙 牎 繬 肜 摲 炚 雗 頨 款 款 款 款媡 夥 俰 欓 焵 韀 冊 嗥 燠 鱧 駟 髉
  • Twinkle Twinkle Little Star: оригінал , закодований - 欠 悺 矜 莳 錥 鷗 谴 裴 皽 憝 漿 箔 皇 殤 鸧 鸧 蜻 丱
  • Весела чіптуна: оригінальна , закодована - 简 詐 諥 尘 牿 扫 鲑 龮 箫 颫 蠏 騁 煟 靸 阒 萎 囦 鮇 愯 訖 芉 馄 鈿 鬦 嶏 觲 沠 丆 贀 蛑 漥 荤 侲 咔 麑 桬 桬 鲠 僵 擕灼 攲 陇 亄 鹘 琾 業 纟 鵼 牤 棌 匩 碆 葫 鶙 懲 欃 铳 樯 柇 鋡 衻 澯 伅 墇 搢 囻 荸 香 貱 夹 咽 蟽 籐 锂 蛉 袒 貊 屨 鈦 夜 镤 沄 沄 沄 鍡乔 蚖 醶 矕 咻 喸 碋 利 褼 裊 匎 嶮 窢 幘 六 鼝 瞮 坡 葍 帷 锆 邵 符 琨 鱴 郤 栱 烇 仾 椃 荄 荄 嘵 統 篏 珆

Оновлення

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

  • Імператорський марш: оригінал , закодований - 岼 讶 湠 衢 嫵 焅 喋 藭 憣 嗗 颟 橳 蕖 匵 腾 嗅 鈪 芔 顕 樭 眀 冂 常 僠 寝 萉 乹 俚 戂 闤 蟑 拷 邢 音 褈 霈 媬 璥 盒 萳唂 焰 銯 艉 鶱 縩 巻 痭 虊 窻 熲 紆 耺 淙 苉 嘏 庸 锺 禒 旇 蘄 遪 刨 繱 蕖 嬯 摺 祑 仰 軈 牰 杊 瘷 棏 弘 卄 浕 眮 騜 阖 鏴 鶺 艂 艂 艂 税採 偋 隆 兎 豅 蚦 紛 襈 洋 折 踜 跅 軩 树 爺 庄 玫 亳 攩 獼 匑 仡 昐 炡 瞱 咏 斎 煟 藭 恐 鷖 璌 榍 脅 樐 嬨 勀 茌 茌

Більше оновлень

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

Більш рання версія коду просто прийняла більше цих позафазових сигналів, але тепер ми приймаємо RMS. Крім того, я додав досить консервативну функцію вікна в кодер (Tukey, альфа 0,3), щоб спробувати боротися з артефактом.

Все оновлюється відповідно.


1
Я не можу грати в Twinkle Twinkle і чіптуну. Хутро Елізе досить близько, тоді як Бетховен ледве впізнаваний, ха-ха.
justhalf

Ви хочете спробувати Twinkle Twinkle та Chiptune ще раз? Я думаю, що я виправив URL-адреси.
James_pic

1
Це працює зараз. Мерехтіння Мережі досить спуск. Але що відбувається в кінці?
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.