ミュージカルツイートチャレンジ


37

これはTwitter画像エンコーディングチャレンジのオーディオバージョンです。

140バイト以下の印刷可能なUTF-8エンコードテキストで少なくとも1分間の音楽を表現できるオーディオ圧縮形式を設計します。

次の3つの引数(プログラム自体の名前の後)をとるコマンドラインプログラムを記述して実装します。

  1. 文字列encodeまたはdecode
  2. 入力ファイル名。
  3. 出力ファイル名。

(お好みのプログラミング言語にコマンドライン引数を使用する機能がない場合は、別のアプローチを使用できますが、回答で説明する必要があります。)

encode操作は、あなたの圧縮された「つぶやき」のフォーマットにあなたの選択したオーディオ形式から変換します、そしてdecode操作があなたの「つぶやき」形式から元のオーディオ形式に変換します。(もちろん、非可逆圧縮を実装することが期待されているため、出力ファイルは入力と同一である必要はなく、同じ形式である必要があります。)

答えに含めてください:

  • プログラムのソースコード。(このページに対して長すぎる場合は、別の場所でホストし、リンクを投稿できます。)
  • 仕組みの説明。
  • 元のオーディオファイルへのリンク、圧縮先の「ツイート」テキスト、およびツイートをデコードして取得したオーディオファイルを含む少なくとも1つの例。(Answererは著作権の「公正使用」アサーションに対して責任があります。)

ルール

  • 私はいつでもコンテストルールの抜け穴を塞ぐ権利を有します。
  • [4月24日編集]encode関数の入力(および関数の出力decode)には、次のような合理的な一般的なオーディオ形式を使用できます。
    • WAVのような非圧縮波形。
    • MP3のような圧縮された波形。
    • MIDIのような「シートミュージック」スタイル。
  • 圧縮された「ツイート」形式では、実際に入力ファイルのサウンドをエンコードする必要があります。したがって、次のタイプの出力はカウントされませ
    • 実際の出力が保存される場所を示すURIまたはファイルパス。
    • 実際の出力がblobとして保存されるデータベーステーブルへのキー。
    • 同様のもの。
  • プログラムは、一般的な音楽ファイルを圧縮するように設計する必要があります。したがって、特定のサンプルソングに明白に結びついているようなことはしないでください。たとえば、「Twinkle、Twinkle、Little Star」をデモンストレーションしている場合、圧縮ルーチンはシーケンスdo-do-so-so-la-la-soの特定のシンボルをハードコーディングしないでください。
  • あなたのプログラムの出力は、実際にTwitterを通り抜けて無傷で出てくるはずです。サポートされている正確な文字のリストはありませんが、文字、数字、記号、句読点に固執しようとしています。制御文字、文字、BIDIマーカー、またはその他の奇妙なものを組み合わせないようにします。
  • 複数のエントリを提出できます。

判定基準

これは人気コンテストです(つまり、ほとんどの正味の賛成票が勝ちます)が、有権者は次のことを考慮するよう促されます。

正確さ

  • 圧縮された後でも曲を認識できますか?
  • それはいいですね?
  • どの楽器が演奏されているかをまだ認識できますか?
  • それでも歌詞を認識できますか?(これはおそらく不可能ですが、誰かがそれを成し遂げたら印象的です。)

複雑

ここで例の歌の選択が重要です。

  • [4月24日追加]この課題は、MIDIまたは同様のフォーマットで最も簡単になります。ただし、波形タイプのフォーマットで動作させるために余分な努力を払う場合、それは余分な信用に値します。
  • 構造は何ですか?もちろん、同じ4つの測定を任意の回数繰り返すだけで、1分間の要件を満たすことができます。しかし、より複雑な歌の構造はより多くのポイントに値します。
  • このフォーマットは、一度に再生される多くのノートを処理できますか?

コード

  • できるだけ短く簡潔にします。ただし、これはコードゴルフではないため、文字数よりも読みやすさが重要です。
  • 結果の品質の向上によって正当化される限り、巧妙で複雑なアルゴリズムでも問題ありません。

9
MIDIとWAVの使用は、まったく異なる課題です。フォーマットをWAVのみに制限する必要があると思います。
grovesNL

10
解決策を探していますが、正直なところ、60バイトのサウンドを140バイトに詰め込むということは、19ビット/秒未満の空きがあるということです。300 bpsで動作するいくつかの非常に効率的な音声エンコーダーがありますが、これらは包括的な音声を生成する目的で合成音素にデコードすることしかできず、音楽をエンコードすることはできません。
jarnbjo 14

2
あなたは、現在の最新技術よりも桁も大きい圧縮係数を持つソフトウェアを求めています。あなたは賢明な答えをしたい場合(すなわち、様組成関与しない4'33"聴覚障害者の男の土葬のための葬送月)、私は1秒に時間制約をドロップすることをお勧めしたい。
うるさいossifrage

3
@squeamishossifrageは、彼はそれが認識できるように聞こえなければならないとは言わなかった。
cjfaure 14

5
ありますチャットの引数あなたが実際に140バイトまたはつぶやきサイズの140のを意味するかどうかについて(翌日)の文字が
ピーターテイラー

回答:


26

スカラ

もちろん、MIDIファイルをエンコードする方が簡単でしょうが、だれがMIDIファイルを大量に持っているのでしょうか?1997年ではありません!

まず最初に:「Unicodeバイト」を「Unicode文字」として解釈し、CJK文字を使用することにしました。

  • 画像チャレンジにマッチします
  • Twitterはそれでクールです
  • 私は本当にそれらのビット必要です

ソースからのエントロピーの最後の一滴を絞るために私が使用するいくつかのトリックがあります:

まず、音楽は音符で作られます。さらに、通常、異なるオクターブ内の同じ音符を同じ音符と見なします(これが、12弦ギターの音が正しい理由です)。(たとえば、Bを出力すると、実際にはすべてのオクターブのBのみで構成されるコードが出力され、12弦ギターのようになります)。

次に、私は高校の音楽の授業で、ほとんどの音符の遷移が小さいことを覚えています(1つ上の音または下の音)。ジャンプはあまり一般的ではありません。これは、ノート自体よりもジャンプサイズのエントロピーがおそらく少ないことを示しています。

したがって、私たちのアプローチは、ソースをいくつかのブロックに分割することです-1秒あたり14ブロックがうまく機能していることがわかりました(補足として、なぜオーディオが44100 Hzでエンコードされているのか疑問に思っていました。したがって、1、2、3、4、5、6、7、9、10、12、14、15、18、20、21、25、28、または30ブロック/秒を選択できた場合は、きれいに分割されます)。次に、これらのブロックをFFTします(私が使用したライブラリは2のべき乗でないブロックでは高速ではないため、技術的には高速ではありません。技術的には、フーリエではなくHartley変換を使用しました)。

次に、最も音量の大きい音を見つけます(主に実装が最も簡単であるため、カットオフが高いおよび低いA重み付けを使用しまし)。この音をエンコードするか、無音をエンコードします(無音検出はSNR-低SNRに基づいています沈黙です)。

次に、エンコードされたノートをジャンプに変換し、それらを適応算術コーダーに送ります。テキストへの翻訳のプロセスは、画像圧縮の質問に似ています(ただし、BigIntegerの乱用がある程度必要です)。

これまでのところ、これでいいのですが、サンプルのエントロピーが大きすぎる場合はどうでしょうか?粗い音響心理学モデルを使用して一部を削除します。最低のエントロピージャンプは「変化なし」です。したがって、前のブロックのノートが存在するブロックを探して、前のノートを再生し続けてもリスナーが気付かないブロックを見つけるために、FFTデータを調べます。最も大きな音とほぼ同じ大きさです(「ほぼ」は品質パラメーターによって制御されます)。

したがって、140文字のターゲットがあります。品質1.0(最大品質)でエンコードすることから始め、文字数を確認します。多すぎる場合は、0.95に落とし、140文字になるまで繰り返します(または、品質が0.05になるとgiveめます)。これにより、n <= 20の場合、エンコーダーはnパスエンコーダーになります(ただし、他の領域でも非常に効率が悪いため、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の完全なコード。

注意すべき落とし穴:現在Mavenアーティファクトが利用できないnayukiの算術コーディングライブラリが必要です。代わりに、開発者のフォークをローカルでビルドしてインストールする必要があります。

そして、ここにいくつかのサンプルがあります。彼らはひどいが、ほとんど認識できるように聞こえます:

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

更新

コード内の無音しきい値を調整し、再エンコードしました。それに応じてエンコーディングが更新されました。また、楽しみのために、別の曲を追加しました(技術的にはオープンソースではありませんが、元の著作権者は彼らのIPが脅威にさらされていると感じることはないでしょう)

  • インペリアルマーチ:オリジナルエンコード済み -岼讶湠衢嫵焅喋藭霔憣嗗颟橳蕖匵腾嗅鈪芔区顕樭眀冂常僠寝萉乹俚戂闤灈蟑拷萉乹俚戂闤灈蟑音邢唂焰銯艉鶱縩巻痭虊窻熲紆耺哅淙苉嘏庸锺禒旇蘄籷遪刨繱蕖嬯潰祑仰軈牰杊瘷棏郖弘卄浕眮騜阖鏴鶺艂税寛柭菸採偋隆兎豅蚦紛襈洋折踜跅軩树爺奘庄玫亳攩獼匑仡葾昐炡瞱咏斎煟价藭恐鷖璌榍脅樐嬨勀茌愋

その他の更新

エンコーダーを少し調整しましたが、品質に驚くべき影響がありました(DHTでは、位相がずれた信号は事実上負であるため、位相がずれた信号は無視していたことを忘れていました)。

以前のバージョンのコードでは、これらの位相のずれた信号のうち大きい方を使用していましたが、現在はRMSを使用しています。また、アーティファクトに対抗するために、かなり保守的なウィンドウ関数をエンコーダー(Tukey、alpha 0.3)に追加しました。

それに応じてすべてが更新されます。


1
Twinkle Twinkleとchiptuneを再生できません。ファー・エリーゼは非常に近いですが、ベートーベンはほとんど認識できません。
ちょうど半分14

Twinkle TwinkleとChiptuneをもう一度試してみたいですか?URLを修正したと思います。
James_pic

1
今は動作します。Twinkle Twinkleは非常に下降しています。しかし、最後に何が起こっていますか?
ちょうど半分14

ええ、私は最後に何が起こっているのか完全にはわかりません。算術コーディングのどこかで起こっていると思う。コードの以前のバージョンでは、ストリームはEOFシンボルで終了しましたが、場合によってはデコーダーがEOFシンボルの読み取りに失敗していました。BitOutputStreamを正しく閉じていなかったのではないかと疑っていますが、詳しく調べます。
James_pic 14

1
はい、実際はまさにそれでした。BitOutputStream::close呼び出すのを忘れていたメソッドがありました。コードを修正し、出力を更新します。
James_pic

11

Python

UTF-8に関して特別なマングリングを行わないので、私の提出は140バイトの要件を満たします。私は自分のソリューションの有用性、正確性、効率性について一切主張していません。

入力と出力に44100 Hzのサンプルレートを使用しました。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を使用しました。

また、10200のSAMPLES_PER_BYTEでTwinkle、Twinkle、Little Starをエンコードする自由を取りました。

出力

PianoforteおよびBeatboxの即興演奏

aWnxQDg4mWqZWVl6W+LyOThfHOPyQThAe4x5XCqJK1EJ8Rh6jXt5XEMpk1Epe5JqTJJDSisrkkNCSqnSkkJDkiorCZHhCxsq8nlakfEp8vNb8iqLysp6MpJ7s4x7XlxdW4qKMinJKho

リンク

キラキラ光る

HBobGlJSUlJSY2FlYVNRUVFCQkJCQjs5PDksKisqGxoZGVFTUVNRREFDQjs6OjoqKykpKVRRVFJDQkJCOjs6OzksKikpGxobG1JSUlNRZWFlYVNSUVFCQkJDQTw5PDorKisqGhsZGRk

リンク

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.