Müzikal Tweet Mücadelesi


37

Bu Twitter resim kodlaması mücadelesinin ses sürümüdür .

En az bir dakikalık müziği 140 bayt veya daha az yazdırılabilir UTF-8 kodlu metinde temsil edebilecek bir ses sıkıştırma formatı tasarlayın .

Aşağıdaki 3 argümanı alan bir komut satırı programı yazarak uygulayın (programın adından sonra):

  1. Dize encodeveya decode.
  2. Giriş dosya adı.
  3. Çıktı dosya adı.

(Tercih ettiğiniz programlama dili, komut satırı argümanlarını kullanma yeteneğine sahip değilse, alternatif bir yaklaşım kullanabilirsiniz, ancak cevabınızda açıklamanız gerekir.)

encodeİşlem, sıkıştırılmış “tweet” biçimine seçtiğiniz ses biçiminden dönüştürür ve decodeoperasyon orijinal ses biçimine “tweet” biçiminden dönüştürür. (Elbette, kayıplı bir sıkıştırma uygulamanız beklenir, bu nedenle çıktı dosyasının aynı formatta girdiyle aynı olması gerekmez.)

Cevabınıza ekleyin:

  • Programınızın kaynak kodu tam olarak. (Bu sayfa için çok uzunsa, başka bir yerde barındırabilir ve bir link gönderebilirsiniz.)
  • Nasıl çalıştığının bir açıklaması.
  • En az bir örnek, orijinal ses dosyalarına bir bağlantıyla, sıkıştırdığı “tweet” metni ve tweet'in kodunu çözerek elde edilen ses dosyası. (Cevaplayıcı, “adil kullanım” iddialarının telif haklarından sorumludur.)

kurallar

  • Yarışma kurallarındaki boşlukları herhangi bir zamanda kapatma hakkını saklı tutarım.
  • [24 Nisan] düzenlendiencode İşlevinizin (ve işlevinizin çıktısı decode) girişi için , aşağıdaki şekilde olsun, makul herhangi bir ortak ses biçimini kullanabilirsiniz:
    • Sıkıştırılmamış dalga biçimi, WAV gibi.
    • MP3 gibi sıkıştırılmış dalga biçimi.
    • MIDI gibi “Notalar” tarzı.
  • Sıkıştırılmış "tweet" biçiminiz aslında giriş dosyasındaki sesleri kodlamalıdır. Yani, çıkış aşağıdaki türlerde do not saymak:
    • Gerçek çıktının depolandığı konumu veren bir URI veya dosya yolu.
    • Gerçek çıkışın blob olarak depolandığı bir veritabanı tablosunun anahtarı.
    • Benzer bir şey.
  • Programınız genel müzik dosyalarını sıkıştıracak şekilde tasarlanmalıdır , bu nedenle belirli örnek şarkınıza çok açık bir şekilde bağlı olan şeyleri yapmayın. Örneğin, “Pırıltı, Pırıltı, Küçük Yıldız” gösteriyorsanız, sıkıştırma yordamınız, do-do-so-la-so-so dizisi için belirli bir sembolü kodlamamalıdır.
  • Programınızın çıktısı aslında Twitter üzerinden geçebilmeli ve bozulmadan çıkabilmelidir. Desteklenen tam karakterlerin bir listesine sahip değilim, ancak harflere, rakamlara, sembollere ve noktalamalara bağlı kalmaya çalışın; ve karakterleri, BIDI işaretleyicilerini veya bunun gibi diğer garip şeyleri birleştirmek için kontrol karakterlerinden kaçının.
  • Birden fazla giriş gönderebilirsiniz.

Hakim kriterler

Bu bir popülerlik yarışmasıdır (yani çoğu net galibiyet kazanır), ancak seçmenlerin aşağıdakileri dikkate alması isteniyor:

doğruluk

  • Sıkıştırıldıktan sonra şarkıyı hala tanıyabilir misin?
  • Kulağa hoş geliyor mu?
  • Hangi enstrümanların çalındığını hala anlayabiliyor musunuz?
  • Sözleri hala tanıyor musun? (Bu muhtemelen imkansızdır, ancak biri başarırsa etkileyici olurdu.)

karmaşa

Örnek şarkının seçimi burada önemlidir.

  • [24 Nisan Eklendi] Bu meydan okuma MIDI veya benzeri formatlarla en kolay olacak. Bununla birlikte, dalga biçimi tipi biçimlerle çalışması için fazladan çaba sarfederseniz, ekstra kredi almaya hak kazanır.
  • Yapı nedir Elbette, bir dakikalık gereksinimi, yalnızca aynı 4 ölçümü rastgele sayıda tekrarlayarak yapabilirsiniz. Ancak daha karmaşık şarkı yapıları daha fazla puan hak ediyor.
  • Format, bir kerede çalınan birçok nota işleyebilir mi?

Kod

  • Mümkün olduğunca kısa ve basit tutun. Bununla birlikte, bu bir kod golf değildir, bu yüzden okunabilirlik karakter sayısından daha önemlidir.
  • Zekice, karmaşık algoritmalar da, gelişmiş sonuç kalitesiyle haklı oldukları sürece tamamdır.

9
MIDI'ye karşı WAV kullanmak son derece farklı bir mücadeledir. Bence sadece formatları WAV ile sınırlandırmalısın.
grovesNL

10
Herhangi bir çözüm görmek istiyorum, ama dürüst olmak gerekirse: 60 byte sesin 140 bayta paketlenmesi, saniyede 19 bitin daha azına sahip olduğunuz anlamına gelir. 300 bps hızında çalışan birkaç ultra verimli konuşma kodlayıcı var, ancak bunlar anlaşılır bir konuşma üretmek amacıyla sentezlenmiş fonemlerin kodunu çözebiliyor ve hiçbir şekilde müzik kodlayamıyor.
jarnbjo

2
Sıkıştırma faktörler ile Sen yazılımı için soruyorsun birçok tekniğin şimdiki halinden daha sayıca büyük emirlerini. Eğer mantıklı cevaplar istiyorsanız (yani gibi kompozisyonlar kapsamayan 4'33" ya bir Sağır Man cenaze töreni için Cenaze Mart ), ben 1 saniye zamanı kısıtlaması bırak öneririm.
alıngan ossifrage

3
@squeamishossifrage, yine de tanınabilir bir ses olduğunu söylemedi.
cjfaure

5
Sohbette (ve ertesi gün) gerçekte 140 bayt mı, yoksa tweet büyüklüğünde bir 140 karakter mi demek istediğinize dair bir tartışma var .
Peter Taylor

Yanıtlar:


26

Scala

Tabii ki, MIDI dosyalarını kodlamak daha kolay olurdu, fakat etrafta bir sürü MIDI dosyası var. 1997 değil!

Öncelikle ilk şeyler: "Unicode byte" ı "Unicode char" olarak yorumlamaya ve CJK karakterlerini kullanmaya karar verdim, çünkü:

  • Bu görüntü meydan okuma ile eşleşiyor
  • Twitter onunla havalı
  • Ben gerçekten bu bitleri ihtiyaç

Kaynaklardan her son entropi damlasını sıkmak için kullandığım birkaç püf noktası var:

İlk olarak, müzik notaları ile yapılır. Dahası, genellikle aynı notayı farklı bir oktavda aynı nota olarak görüyoruz (bu yüzden 12 telli bir gitar doğru geliyor), bu yüzden kodlamak için sadece 12 seçeneğimiz var. (örneğin B çıkışı yaptığımda, aslında tüm oktavlarda B'den oluşan, 12 telli bir gitar gibi bir akor çıkardım).

Sonra, lise müzik sınıfından nota geçişlerinin çoğunun küçük olduğunu hatırlıyorum (bir not aşağı ya da yukarı). Atlamalar daha az yaygındır. Bu bize, atlama büyüklüklerinde muhtemelen notların kendisinden daha az entropi olduğunu söylüyor.

Bu yüzden, yaklaşımımız kaynağımızı bir takım bloklara bölmektir - saniyede 14 blokun iyi çalıştığını gördüm (yan not, her zaman sesin neden 44100 Hz'de kodlandığını merak etmişimdir. Böylece saniyede 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 14, 15, 18, 20, 21, 25, 28 veya 30 blok seçebilirdim ve temiz bir şekilde bölünmüş olurdu. ). Daha sonra bu blokları FFT olarak kullandık (teknik olarak hızlı değil, kullandığım kütüphane 2 güç olmayan bloklar için hızlı değil. Teknik olarak , Fourier değil, Hartley dönüşümü kullandım ).

Daha sonra en yüksek ses seviyesine sahip olan notu bulduk ( A-ağırlıklandırma kullandım , yüksek ve düşük kesikli, çoğunlukla uygulaması en kolay olanı) ve ya bu notu kodluyor ya da sessizliği kodluyor (sessizlik tespiti SNR - düşük SNR'ye dayanıyor) sessizliktir).

Daha sonra kodlanmış notlarımızı atlamalara çevirir ve bunları uyarlamalı bir aritmetik kodlayıcıya besleriz. Metne çeviri işlemi, görüntü sıkıştırma sorusuna benzer (ancak BigInteger'ın küfürlü kullanılmasını içerir).

Şimdiye kadar, çok iyi, ama ya numunede çok fazla entropi varsa? Bazılarını çıkarmak için ham bir psikoakustik model kullanıyoruz. En düşük entropi sıçraması "değişiklik yok", bu nedenle önceki bloğun notunun bulunduğu blokları arayarak, önceki notayı çalmaya devam edersek dinleyicinin farketmeyeceği blokları bulmaya çalışmak için FFT verilerimize bakarız. neredeyse en yüksek nota kadar gürültülü (burada "neredeyse" kalite parametresi tarafından kontrol edilir).

Öyleyse 140 karakterlik bir hedefimiz var. En yüksek kalitede 1.0 (en yüksek kalitede) kodlayarak başlıyoruz ve kaç karakter olduğunu görüyoruz. Çok fazla ise, 0,95'e düşer ve tekrar 140 karaktere ulaşana kadar tekrar ederiz (ya da kalite 0,05'ten sonra pes ederiz). Bu, enkoderi n-= 20 için bir n-pass enkoderi yapar (her ne kadar diğer alanlarda da büyük ölçüde etkisiz olsa da, m'eh).

Video kodlayıcı / kod çözücü s16be mono biçiminde ses bekler. Bu, avconv kullanılarak şu şekilde elde edilebilir:

#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

Kodlayıcıyı çalıştırmak için:

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"

Tam kod https://github.com/jamespic/twelvestring adresindedir .

Dikkat edilmesi gerekenler: Şu anda Maven eserleri bulunmayan nayuki'nin Aritmetik Kodlama kütüphanesine ihtiyacınız olacak. Bunun yerine, geliştiricinin çatalını yerel olarak kurmanız ve kurmanız gerekir .

Ve işte bazı örnekler. Kulağa kulağa korkunç geliyor, ama sadece tanınabilirler:

  • Beethoven'ın 5.'si: orijinal , kodlanmış - 刲 檁 囉 罓 佖 镱 賑 筶 庬 一 掛 掛 獴 笲 銗 娵 纜 纜 喫 僭 掛 嫭 裵 笲 鱨 娵 纜 纜 纜 咍 咍姑椻趕挍呪白鸞盙宠埘謭擆闯脲誜忘椐笸囃庣稨俖咛脔湙弻籚砌鍖裏橈镙訁鹻塿骱踠筟七趇杅峇敖窈裞瘫峦咰呹櫬茏蛏姆臸胝婁遼憀麁黦掏毈喙眝綄鴀耢椚筤菮蟞斗俼湛营筬禴籙嬧窻丄
  • Fur Elise: orijinal , kodlanmış - 訖 忢 擫 鏝 拪 纒 铇 鯪 薯 鉰 孝 埛 痏 絘 僌 莻 暆 緂 柤 碠 瞢 蠣 託 騶 騶 腀 饚 冏 柤 碠 瞢 碩 歙 棆 敗 腀 辦 冏 冏 碠 碠 瞢 瞢苯劺誺軩忇穤锳婁伉巠桭晘酉筟緵俅怚尵鎽蜓崁飿嘔但鈐謽酝屮呤誥俊覊鶮龔癸埙煂臙牎繬肜摲炚雗頨款驦燈菩凧咁楾媡夥俰欓焵韀冊嗥燠鱧駟髉
  • Twinkle Twinkle Little Star: orijinal , kodlanmış - 欠 悺 矜 莳 錥 谴 裴 皽 鳺 憝 漿 箔 皇 殤 鸧 蜻 猻 丱
  • Eğlenceli bir chiptune: orijinal , kodlanmış - 简 詐 諥 尘 牿 扫 鲑 龮 箫 颫 蠏 騁 煟 靸 阒 觲 沠 丆 贀 蛑 馄 鈿 鬦 嶏 嶏 觲 桬 丆 贀 贀灼攲陇亄鹘琾業纟鵼牤棌匩碆踬葫鶙懲欃铳樯柇鋡疡衻澯伅墇搢囻荸香貱夹咽蟽籐屈锂蛉袒貊屨鈦夜镤沄鍡唦魮骬乔蚖醶矕咻喸碋利褼裊匎嶮窢幘六沧鼝瞮坡葍帷锆邵旻符琨鱴郤栱烇仾椃騋荄嘵統篏珆罽

Güncelleştirme

Koddaki sessizlik eşiğini değiştirdim ve yeniden kodladım. Kodlamalar buna göre güncellendi. Ayrıca, başka bir şarkı ekledim (teknik olarak açık kaynak değil, ancak orijinal telif hakkı sahibinin IP’lerinin tehdit altında olduğunu hissedeceğini sanmıyorum), sadece eğlence için:

  • İmparatorluk Mart: orijinal , kodlanmış - 岼 讶 湠 衢 嫵 焅 喋 藭 霔 憣 嗗 橳 蕖 匵 腾 嗅 乹 俚 戂 灈 灈 常 僠 寝 萉 萉 媬 媬 闤 闤 灈唂焰銯艉鶱縩巻痭虊窻熲紆耺哅淙苉嘏庸锺禒旇蘄籷遪刨繱蕖嬯摺祑仰軈牰杊瘷棏郖弘卄浕眮騜阖鏴鶺艂税寛柭菸採偋隆兎豅蚦紛襈洋折踜跅軩树爺奘庄玫亳攩獼匑仡葾昐炡瞱咏斎煟价藭恐鷖璌榍脅樐嬨勀茌愋

Diğer Güncellemeler

Enkoderi biraz değiştirdim ve kaliteye şaşırtıcı bir etkisi oldu (DHT'de, faz dışı sinyallerin etkili bir şekilde negatif olduğunu, bu yüzden faz dışı sinyalleri görmezden geldiğimi unutmuşum).

Kodun daha önceki bir sürümü, bu faz dışı sinyallerin büyüsünü daha da aldı, ancak şimdi RMS'yi alıyoruz. Ayrıca kodlama yapısına (Tukey, alfa 0.3) oldukça muhafazakar bir pencere fonksiyonu ekledim.

Her şey buna göre güncellenir.


1
Twinkle Twinkle ve chiptune oynayamıyorum. Fur Elise oldukça yakın, Beethoven ise zar zor tanınabiliyor, haha.
justhalf

Twinkle Twinkle ve Chiptune'u tekrar denemek ister misiniz? URL’leri düzelttiğimi düşünüyorum.
James_pic

1
Şuan çalışıyor. Twinkle Twinkle oldukça inişli çıkışlı. Ama sonunda neler oluyor?
justhalf

Evet, sonunda ne olduğundan tam olarak emin değilim. Aritmetik kodlamada bir yerde olduğunu sanıyorum. Kodun daha önceki bir sürümünde, akış bir EOF sembolü ile sonlandırıldı, ancak bazı durumlarda kod çözücü EOF sembolünü okuyamadı. BitOutputStream'i doğru kapatmadığımdan şüpheleniyorum, ancak araştıracağım.
James_pic

1
Evet, aslında tam olarak buydu. BitOutputStream::closeAramayı unuttuğum bir yöntem vardı. Kodu düzeltip çıktıları güncelleyeceğim.
James_pic

11

piton

UTF-8 ile ilgili herhangi bir özel yönetim yapmıyorum, bu yüzden gönderim 140 baytlık şartı geçiyor. Çözümümün yararlılığı, doğruluğu veya etkinliği hakkında hiçbir iddiada bulunmam.

Giriş ve çıkış için 44100 Hz örnekleme hızı kullandım. SAMPLES_PER_BYTE, dönüşümün kalitesini kontrol eder. Sayı ne kadar düşük olursa, ses o kadar iyi kalitede olur. Kullandığım değerler sonuçlar bölümünde verilmiştir.

kullanım

kodlamak

Giriş dosyası bir wav olmalıdır. Sadece ilk kanalı kodlar.

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

Kod Çözme

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

Kod Çözülmüş Müziği Çalmak

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

Kod

#!/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))

Girişler

Resmi gönderim, Kevin MacLeod tarafından Pianoforte ve Beatbox için doğaçlama . Bu dosya için 25450 SAMPLES_PER_BYTE kullandım.

Ayrıca Twinkle, Twinkle, Little Star'ı 10200'lük bir SAMPLES_PER_BYTE kodlama özgürlüğünü de aldım. Çok daha iyi geliyor.

Çıktı

Pianoforte ve Beatbox için doğaçlama

aWnxQDg4mWqZWVl6W+LyOThfHOPyQThAe4x5XCqJK1EJ8Rh6jXt5XEMpk1Epe5JqTJJDSisrkkNCSqnSkkJDkiorCZHhCxsq8nlakfEp8vNb8iqLysp6MpJ7s4x7XlxdW4qKMinJKho

bağlantı

Pırıltı, Pırıltı Küçük Yıldız

HBobGlJSUlJSY2FlYVNRUVFCQkJCQjs5PDksKisqGxoZGVFTUVNRREFDQjs6OjoqKykpKVRRVFJDQkJCOjs6OzksKikpGxobG1JSUlNRZWFlYVNSUVFCQkJDQTw5PDorKisqGhsZGRk

bağlantı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.