뮤지컬 트윗 도전


37

이것은 트위터 이미지 인코딩 챌린지 의 오디오 버전입니다 .

140 바이트 이하의 인쇄 가능한 UTF-8 인코딩 텍스트로 1 분 이상의 음악 을 표현할 수있는 오디오 압축 형식을 디자인 하십시오.

프로그램 이름 뒤에 다음 3 개의 인수를 사용하는 명령 행 프로그램을 작성하여이를 구현하십시오.

  1. 문자열 encode또는 decode.
  2. 입력 파일 이름입니다.
  3. 출력 파일 이름입니다.

선호하는 프로그래밍 언어에 명령 줄 인수를 사용할 수있는 기능이 없으면 대체 방법을 사용할 수 있지만 답을 설명해야합니다.

encode작업은 압축 "짹짹"형식으로 선택한 오디오 포맷 변환 것이며, decode동작은 원래의 오디오 형식으로 "짹짹"형식으로 변환됩니다. (물론, 손실 압축을 구현해야하므로 출력 파일이 입력과 동일한 형식 일 필요는 없습니다.)

답변에 포함하십시오 :

  • 프로그램의 소스 코드 전체입니다. (이 페이지가 너무 길면 다른 곳에서 호스팅하고 링크를 게시 할 수 있습니다.)
  • 작동 방식에 대한 설명.
  • 적어도 하나의 예는, 원본 오디오 파일 (들)에 대한 링크와 함께, 압축되는 "트위트"텍스트, 및 트위트를 디코딩함으로써 얻어진 오디오 파일이다. (응답자는 저작권 "공정 사용"주장에 대한 책임이 있습니다.)

규칙

  • 나는 컨테스트 규칙의 허점을 언제든지 닫을 권리를 보유합니다.
  • [편집 된 4 월 24 일]encode 함수 입력 (및 함수 출력 decode)을 위해 다음에 관계없이 합리적이고 일반적인 오디오 형식을 사용할 수 있습니다.
    • WAV와 같은 비 압축 파형
    • MP3와 같은 압축 파형.
    • MIDI와 같은 "Sheet music"스타일.
  • 압축 된 "트위트"형식은 실제로 입력 파일의 사운드를 인코딩해야합니다. 따라서 다음 유형의 출력은 계산 되지 않습니다 .
    • 실제 출력이 저장되는 위치를 제공하는 URI 또는 ​​파일 경로입니다.
    • 실제 출력이 BLOB으로 저장되는 데이터베이스 테이블의 키입니다.
    • 비슷한 것.
  • 프로그램은 일반 음악 파일 을 압축하도록 설계되었으므로 특정 예제 곡과 너무 관련이있는 작업은 수행하지 마십시오. 예를 들어, "Twinkle, Twinkle, Little Star"를 시연하는 경우 압축 루틴이 do-do-so-so-la-la-so 시퀀스의 특정 기호를 하드 코딩해서는 안됩니다.
  • 당신의 프로그램의 출력은 실제로 트위터를 통과하고 상처없이 나올 수 있어야합니다. 지원되는 정확한 문자 목록이 없지만 문자, 숫자, 기호 및 문장 부호를 사용하십시오. 제어 문자, 문자 결합, BIDI 마커 또는 기타 이상한 것을 피하십시오.
  • 하나 이상의 출품작을 제출할 수 있습니다.

심사 기준

이것은 인기 콘테스트 (즉, 대부분의 순 공감대 승리)이지만 유권자들은 다음 사항을 고려해야합니다.

정확성

  • 압축 된 후에도 노래를 계속 인식 할 수 있습니까?
  • 잘 들립니까?
  • 연주중인 악기를 여전히 인식 할 수 있습니까?
  • 여전히 가사를 인식 할 수 있습니까? (이것은 아마도 불가능하지만, 누군가가 그것을 성취하면 인상적입니다.)

복잡성

예제 곡의 선택은 여기서 중요합니다.

  • [4 월 24 일 추가] 이 과제는 MIDI 또는 유사한 형식으로 가장 쉬울 것입니다. 그러나 파형 유형 형식으로 작동하도록 추가 노력을 기울이면 추가 크레딧이 필요합니다.
  • 구조는 무엇입니까? 물론, 동일한 4 개의 측정 값을 임의의 횟수만큼 반복하여 1 분 요구 사항을 충족 할 수 있습니다. 그러나 더 복잡한 곡 구조는 더 많은 점수가 필요합니다.
  • 한 번에 많은 음표를 연주 할 수 있습니까?

코드

  • 가능한 짧고 간단하게 유지하십시오. 그러나 이것은 코드 골프가 아니므로 가독성은 문자 수보다 중요합니다.
  • 개선 된 결과 품질로 정당화되는 한 영리하고 복잡한 알고리즘도 괜찮습니다.

9
MIDI와 WAV를 사용하는 것은 크게 다른 도전입니다. 형식을 WAV로만 제한해야한다고 생각합니다.
grovesNL

10
솔직히 말해서 해결책이 필요하지만 정직합니다. 60 바이트의 사운드를 140 바이트로 패킹하면 초당 19 비트 미만이 가능하다는 의미입니다. 300bps에서 작동하는 매우 효율적인 음성 인코더가 몇 가지 있지만, 이해하기 쉬운 음성을 생성하기 위해 합성 음소로만 디코딩 할 수 있으며 음악을 인코딩 할 수는 없습니다.
jarnbjo

2
압축 요인으로 당신은 소프트웨어에 대한 요구하고 많은 예술의 현재 상태보다 더 크기 이상의 주문을. 현명한 답변을 원한다면 (즉, 청각 장애인의 혐의로 4'33 " 또는 장례식 3 월 과 같은 작곡을 포함하지 않음 ) 시간 제한을 1 초로 낮추는 것이 좋습니다.
squeamish ossifrage

3
@squeamishossifrage 그러나 그는 그것이 알아볼 수 있다고 들리라고 말하지 않았다.
cjfaure

5
실제로 140 바이트 또는 트윗 크기 140 인지 여부에 대한 대화 (및 다음 날)에 대한 논쟁 이 있습니다 .
피터 테일러

답변:


26

스칼라

물론, MIDI 파일을 인코딩하는 것이 더 쉬울 수 있지만 누가 많은 MIDI 파일을 가지고 있습니까? 1997 년이 아닙니다!

우선 : "유니 코드 바이트"를 "유니 코드 문자"로 해석하고 CJK 문자를 사용하기로 결정했습니다.

  • 이미지 챌린지와 일치
  • 트위터는 멋지다
  • 나는 정말 그 비트를 필요

소스에서 엔트로피의 모든 마지막 드롭을 짜기 위해 사용하는 몇 가지 트릭이 있습니다.

첫째, 음악은 노트로 만들어집니다. 또한, 일반적으로 다른 옥타브에서 동일한 음을 동일한 음으로 간주하므로 (12 스트링 기타가 올바르게 들리는 이유) 인코딩 할 수있는 12 가지만 있습니다. (예를 들어, B를 출력 할 때는 실제로 모든 옥타브에서 B로만 구성된 12 줄 기타와 같은 코드를 출력합니다).

다음으로, 고등학교 음악 수업에서 대부분의 음표 전환은 작습니다 (한 음표 위 또는 아래). 점프는 덜 일반적입니다. 이것은 노트 자체보다 점프 크기가 ​​엔트로피가 적다는 것을 알려줍니다.

그래서 우리의 접근 방식은 소스를 여러 블록으로 나누는 것입니다-초당 14 블록이 잘 작동한다는 것을 알았습니다 (참고로, 항상 오디오가 44100Hz로 인코딩 된 이유가 궁금했습니다. 44100에는 많은 요소가 있음, 그래서 초당 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 14, 15, 18, 20, 21, 25, 28 또는 30 블록을 선택할 수 있었고 깨끗하게 나뉘었을 것입니다 ). 그런 다음 이러한 블록을 FFT합니다 (기술적으로 빠르지 않습니다. 사용 한 라이브러리가 비 제곱 블록에 빠르지 않기 때문에 기술적으로 푸리에가 아닌 Hartley 변환을 사용했습니다 ).

그런 다음 가장 큰 소리를냅니다 ( 주로 구현하기가 가장 쉬우므로 높고 낮은 컷오프를 가진 A- 가중치를 사용 했습니다). 침묵입니다).

그런 다음 인코딩 된 노트를 점프로 변환하여 적응 형 산술 코더에 보냅니다. 텍스트로 변환하는 과정은 이미지 압축 질문과 유사하지만 BigInteger를 일부 사용하는 경우도 있습니다.

지금까지는 훌륭했지만 샘플에 엔트로피가 너무 많으면 어떻게됩니까? 우리는 조잡한 정신 음향 모델을 사용하여 일부를 제거합니다. 가장 낮은 엔트로피 점프는 "변경 없음"이므로 FFT 데이터를보고 이전 블록의 음표가있는 블록을 찾아서 이전 음표를 계속 연주해도 청취자가 알아 채지 못할 블록을 찾으려고 시도합니다. 가장 큰 음표만큼 큰 소리를냅니다 ( '거의'는 품질 매개 변수에 의해 제어됩니다)

우리는 140자를 목표로합니다. 우리는 품질 1.0 (최대 품질)로 인코딩하여 시작하여 몇 개의 문자인지 확인합니다. 너무 많으면 0.95로 떨어지고 140자가 될 때까지 반복합니다 (또는 품질이 0.05 이하인 경우 포기). 이것은 n <= 20 동안 엔코더를 n- 패스 엔코더로 만듭니다 (다른 지역에서도 매우 비효율적이지만 m'eh).

인코더 / 디코더는 s16의 오디오가 모노 형식이어야합니다. 이것은 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의 전체 코드 .

주의 사항 : 현재 Maven 아티팩트가없는 nayuki의 산술 코딩 라이브러리가 필요합니다. 대신, 개발자 의 포크 를 로컬로 빌드하고 설치해야 합니다 .

여기 몇 가지 샘플이 있습니다. 그들은 끔찍하게 들리지만 거의 알아볼 수 있습니다.

  • 베토벤의 5 : 원래 , 인코딩 -刲檁囉罓佖镱賑皌蝩蔲恁峕逊躹呯兲搆摼蝺筶槛庬一掛獴趤笲銗娵纜喫覤粠僭嫭裵獄鱨蠰儏咍箪浝姑 椻 趕 挍 呪 白 鸞 盙 宠 埘 謭 擆 闯 脲 誜 忘 椐 笸 囃 庣 稨 俖 咛 脔 湙 弻 籚 砌 鍖 裏 橈 镙 訁 鹻 塿 骱 踠 筟 七 趇 杅 峇 敖 窈 裞 瘫 峦 咰 呹 櫬茏 蛏 姆 臸 胝 婁 遼 憀 麁 黦 掏 毈 喙 眝 綄 鴀 耢 椚 筤 菮 蟞 斗 俼 湛 营 筬 禴 籙 嬧 窻 丄
  • 퍼 엘리스 : 원본 , 인코딩 -訖 忢 擫 鏝 拪 纒 铇 鯪 薯 鉰 孝 暱 埛 痏 絘 僌 莻 暆 鴈 屖 鳮 絒 婘 譮 蠣 託 騶 腀 饚 緂 柤 碠 瞢 碩 脅 歙 棆 敗 辦 冏 鄔 酾 萖苯 劺 誺 軩 忇 穤 锳 婁 伉 巠 桭 晘 酉 筟 緵 俅 怚 尵 鎽 蜓 崁 飿 嘔 但 鈐 謽 酝 屮 呤 誥 俊 覊 鶮 龔 癸 埙 煂 臙 牎 繬 肜 摲 炚 雗 頨 款 驦 燈 菩 凧 咁 楾媡 夥 俰 欓 焵 韀 冊 嗥 燠 鱧 駟 髉
  • 작은 별 : 원래 , 인코딩 -欠悺矜莳錥鷗谴裴皽鳺憝漿箔皇殤鸧蜻猻丱
  • 재미있는 칩튠 : 원본 , 인코딩 된 -简 詐 諥 尘 牿 扫 鲑 龮 箫 颫 齰 蠏 騁 煟 靸 阒 萎 囦 鮇 雝 愯 訖 芉 馄 鈿 鬦 嶏 觲 沠 丆 贀 蛑 蛀 漥 荤 侲 咔 麑 桬 鲠 僵 擕灼 攲 陇 亄 鹘 琾 業 纟 鵼 牤 棌 匩 碆 踬 葫 鶙 懲 欃 铳 樯 柇 鋡 疡 衻 澯 伅 墇 搢 囻 荸 香 貱 夹 咽 蟽 籐 屈 锂 蛉 袒 貊 屨 鈦 夜 镤 沄 鍡 唦 魮 骬乔 蚖 醶 矕 咻 喸 碋 利 褼 裊 匎 嶮 窢 幘 六 沧 鼝 瞮 坡 葍 帷 锆 邵 旻 符 琨 鱴 郤 栱 烇 仾 椃 騋 荄 嘵 統 篏 珆 罽

최신 정보

코드에서 무음 임계 값을 조정하고 다시 인코딩했습니다. 인코딩이 그에 따라 업데이트되었습니다. 또한 다른 노래 (기술적으로 오픈 소스는 아니지만 원본 저작권 보유자가 자신의 IP가 위협 받고 있다고 생각하지 않을 것입니다)를 추가했습니다.

  • 임페리얼 행진 : 원본 , 인코딩 -岼 讶 湠 衢 嫵 焅 喋 藭 霔 憣 嗗 颟 橳 蕖 匵 腾 嗅 鈪 芔 区 顕 樭 眀 冂 常 僠 寝 萉 乹 俚 戂 闤 灈 蟑 拷 邢 音 褈 霈 媬 盒 萳 璥唂 焰 銯 艉 鶱 縩 巻 痭 虊 窻 熲 紆 耺 哅 淙 苉 嘏 庸 锺 禒 旇 蘄 籷 遪 刨 繱 蕖 嬯 摺 祑 仰 軈 牰 杊 瘷 棏 郖 弘 卄 浕 眮 騜 阖 鏴 鶺 艂 税 寛 柭 菸採 偋 隆 兎 豅 蚦 紛 襈 洋 折 踜 跅 軩 树 爺 奘 庄 玫 亳 攩 獼 匑 仡 葾 昐 炡 瞱 咏 斎 煟 价 藭 恐 鷖 璌 榍 脅 樐 嬨 勀 茌 愋

더 많은 업데이트

엔코더를 약간 조정하여 품질에 놀라운 영향을 미쳤습니다 (DHT에서는 위상차 신호가 효과적으로 음의 값을 가지므로 위상차 신호를 무시하고 있음).

이전 버전의 코드는 이러한 위상차 신호 중 더 큰 것을 취했지만 이제는 RMS를 사용합니다. 또한 아티팩트를 방지하기 위해 상당히 보수적 인 창 기능을 인코더 (Tukey, alpha 0.3)에 추가했습니다.

그에 따라 모든 것이 업데이트됩니다.


1
Twinkle Twinkle과 Chiptune을 재생할 수 없습니다. Fur Elise는 꽤 가까이 있지만 베토벤은 거의 알아볼 수 없습니다.
justhalf

Twinkle Twinkle and the Chiptune을 다시 시도 하시겠습니까? URL을 수정했다고 생각합니다.
James_pic

1
지금 작동합니다. 반짝 반짝 반짝입니다. 그러나 마지막에 무슨 일이 일어나고 있습니까?
justhalf

네, 마지막에 무슨 일이 일어나고 있는지 잘 모르겠습니다. 산술 코딩 어딘가에서 발생하고 있다고 생각합니다. 이전 버전의 코드에서 스트림은 EOF 기호로 종료되었지만 경우에 따라 디코더가 EOF 기호를 읽지 못했습니다. BitOutputStream을 올바르게 닫지 않은 것 같지만 살펴 보겠습니다.
James_pic

1
그렇습니다. 실제로 그렇습니다. BitOutputStream::close내가 잊어 버린 방법 이 있었다 . 코드를 수정하고 출력을 업데이트하겠습니다.
James_pic

11

파이썬

UTF-8과 관련하여 특별한 맹 글링을하지 않으므로 제출이 140 바이트 요구 사항을 통과합니다. 나는 솔루션의 유용성, 정확성 또는 효율성에 대해 어떠한 주장도하지 않습니다.

입력 및 출력에 44100Hz의 샘플 레이트를 사용했습니다. 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))

입력

나의 공식 제출물은 Kevin MacLeod의 Pianoforte와 Beatbox의 Impromptu입니다 . 이 파일에는 25450의 SAMPLES_PER_BYTE를 사용했습니다.

또한 SAMPLES_PER_BYTE가 10200 인 Twinkle, Twinkle, Little Star 인코딩의 자유 를 얻었습니다. 훨씬 나아졌습니다.

출력

피아노 포르테와 비트 박스에 대한 즉흥

aWnxQDg4mWqZWVl6W+LyOThfHOPyQThAe4x5XCqJK1EJ8Rh6jXt5XEMpk1Epe5JqTJJDSisrkkNCSqnSkkJDkiorCZHhCxsq8nlakfEp8vNb8iqLysp6MpJ7s4x7XlxdW4qKMinJKho

링크

반짝 반짝 작은 별

HBobGlJSUlJSY2FlYVNRUVFCQkJCQjs5PDksKisqGxoZGVFTUVNRREFDQjs6OjoqKykpKVRRVFJDQkJCOjs6OzksKikpGxobG1JSUlNRZWFlYVNSUVFCQkJDQTw5PDorKisqGhsZGRk

링크

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.