Thử thách Tweet nhạc


37

Đây là phiên bản âm thanh của thử thách mã hóa hình ảnh Twitter .

Thiết kế định dạng nén âm thanh có thể biểu thị ít nhất một phút nhạc bằng 140 byte hoặc ít hơn văn bản được mã hóa UTF-8 có thể in được.

Thực hiện nó bằng cách viết chương trình dòng lệnh có 3 đối số sau (sau tên của chính chương trình):

  1. Chuỗi encodehay decode.
  2. Tên tệp đầu vào.
  3. Tên tệp đầu ra.

(Nếu ngôn ngữ lập trình ưa thích của bạn thiếu khả năng sử dụng đối số dòng lệnh, bạn có thể sử dụng một cách tiếp cận khác, nhưng phải giải thích nó trong câu trả lời của bạn.)

Các encodehoạt động sẽ chuyển đổi từ định dạng âm thanh lựa chọn của bạn sang định dạng “tweet” nén của bạn, và các decodehoạt động sẽ chuyển đổi từ định dạng “tweet” của bạn sang định dạng âm thanh gốc. (Tất nhiên, bạn sẽ thực hiện nén mất dữ liệu, vì vậy tệp đầu ra không cần phải giống hệt với đầu vào, chỉ ở cùng định dạng.)

Bao gồm trong câu trả lời của bạn:

  • Mã nguồn của chương trình của bạn, đầy đủ. (Nếu quá dài cho trang này, bạn có thể lưu trữ nó ở nơi khác và đăng một liên kết đến nó.)
  • Một lời giải thích về cách thức hoạt động.
  • Ít nhất một ví dụ, với một liên kết đến (các) tệp âm thanh gốc, văn bản tweet tweet tweet mà nó nén xuống và tệp âm thanh thu được bằng cách giải mã tweet. (Người trả lời chịu trách nhiệm về bản quyền khẳng định sử dụng công bằng.

Quy tắc

  • Tôi bảo lưu quyền đóng bất kỳ sơ hở nào trong thể lệ cuộc thi bất cứ lúc nào.
  • [Đã chỉnh sửa ngày 24 tháng 4] Đối với đầu vào của encodechức năng của bạn (và đầu ra của decodechức năng của bạn ), bạn có thể sử dụng bất kỳ định dạng âm thanh phổ biến, hợp lý nào, cho dù đó là:
    • Dạng sóng không nén, như WAV.
    • Dạng sóng nén, như MP3.
    • Bản nhạc của phong cách trực tuyến, giống như MIDI.
  • Định dạng tweet tweet của bạn đã nén phải thực sự mã hóa âm thanh trong tệp đầu vào. Vì vậy, các loại đầu ra sau đây không được tính:
    • Đường dẫn URI hoặc tệp cho vị trí lưu trữ đầu ra thực tế.
    • Khóa cho bảng cơ sở dữ liệu nơi đầu ra thực tế được lưu trữ dưới dạng blob.
    • Bất cứ điều gì tương tự.
  • Chương trình của bạn phải được thiết kế để nén các tệp nhạc chung , vì vậy đừng làm những thứ quá rõ ràng gắn với bài hát ví dụ cụ thể của bạn. Ví dụ: nếu bạn đang trình diễn Twinkle, Twinkle, Little Star ", thói quen nén của bạn không nên mã hóa một biểu tượng cụ thể cho chuỗi do-do-so-so-la-la-so.
  • Đầu ra của chương trình của bạn thực sự sẽ có thể đi qua Twitter và đi ra ngoài vô tư. Tôi không có danh sách các ký tự chính xác được hỗ trợ, nhưng cố gắng bám vào các chữ cái, chữ số, ký hiệu và dấu chấm câu; và tránh các ký tự điều khiển, kết hợp các ký tự, các dấu BIDI hoặc các thứ lạ khác như thế.
  • Bạn có thể gửi nhiều hơn một mục.

Tiêu chuẩn đánh giá

Đây là một cuộc thi phổ biến (nghĩa là hầu hết các cuộc bầu chọn mạng đều thắng), nhưng các cử tri được khuyến khích xem xét những điều sau đây:

Độ chính xác

  • Bạn vẫn có thể nhận ra bài hát sau khi nó được nén?
  • Nghe có vẻ tốt?
  • Bạn vẫn có thể nhận ra nhạc cụ nào đang được chơi?
  • Bạn vẫn có thể nhận ra lời bài hát? (Điều này có lẽ là không thể, nhưng sẽ rất ấn tượng nếu có ai hoàn thành nó.)

Phức tạp

Sự lựa chọn của bài hát ví dụ quan trọng ở đây.

  • [Đã thêm ngày 24 tháng 4] Thử thách này sẽ dễ dàng nhất với MIDI hoặc các định dạng tương tự. Tuy nhiên, nếu bạn nỗ lực thêm để làm cho nó hoạt động với các định dạng dạng sóng, điều đó xứng đáng được thêm tín dụng.
  • Cấu trúc là gì? Chắc chắn, bạn có thể đáp ứng yêu cầu một phút bằng cách lặp lại cùng 4 biện pháp một số lần tùy ý. Nhưng cấu trúc bài hát phức tạp hơn xứng đáng nhiều điểm hơn.
  • Định dạng có thể xử lý rất nhiều ghi chú đang được phát cùng một lúc không?

Mật mã

  • Giữ nó càng ngắn và đơn giản càng tốt. Tuy nhiên, đây không phải là một mã golf, vì vậy khả năng đọc quan trọng hơn số lượng ký tự.
  • Thông minh, các thuật toán phức tạp cũng ổn, miễn là chúng được chứng minh bằng chất lượng kết quả được cải thiện.

9
Sử dụng MIDI so với WAV là một thách thức rất khác nhau. Tôi nghĩ bạn nên hạn chế các định dạng chỉ WAV.
GrovesNL

10
Tôi rất muốn xem bất kỳ giải pháp nào, nhưng thành thật mà nói: Đóng gói 60 âm thanh trong 140 byte có nghĩa là bạn có sẵn ít hơn 19 bit mỗi giây. Có một vài bộ mã hóa lời nói cực kỳ hiệu quả, hoạt động ở tốc độ 300 bps, nhưng chúng chỉ có thể giải mã thành các âm vị tổng hợp với mục đích tạo ra lời nói dễ hiểu và không có khả năng mã hóa âm nhạc.
jarnbjo

2
Bạn đang yêu cầu phần mềm có hệ số nén nhiều đơn đặt hàng lớn hơn trạng thái hiện tại. Nếu bạn muốn có câu trả lời hợp lý (nghĩa là không liên quan đến các tác phẩm như 4'33 " hoặc Tháng ba tang lễ cho những hậu quả của người điếc ), tôi khuyến khích bạn giảm giới hạn thời gian xuống còn 1 giây.
squossish ossifrage

3
@squeamishossifrage anh ấy đã không nói rằng nó phải nghe có vẻ dễ nhận biết, mặc dù.
cjfaure

5
Có một cuộc tranh luận về trò chuyện (và ngày hôm sau) về việc bạn thực sự có nghĩa là 140 byte hay 140 ký tự có kích thước tweet .
Peter Taylor

Câu trả lời:


26

Scala

Chắc chắn, việc mã hóa các tệp MIDI sẽ dễ dàng hơn, nhưng ai có một loạt các tệp MIDI nằm xung quanh? Không phải năm 1997!

Trước tiên, tôi đã quyết định diễn giải "byte Unicode" là "Unicode char" và sử dụng các ký tự CJK, bởi vì:

  • Nó phù hợp với thử thách hình ảnh
  • Twitter thật tuyệt với nó
  • Tôi thực sự cần những bit đó

Có một vài thủ thuật tôi sử dụng để vắt từng giọt entropy cuối cùng từ các nguồn:

Thứ nhất, âm nhạc được thực hiện với các ghi chú. Hơn nữa, chúng tôi thường coi cùng một nốt trong một quãng tám khác là cùng một nốt (đó là lý do tại sao một cây đàn guitar 12 dây nghe có vẻ đúng), vì vậy chúng tôi chỉ có 12 khả năng để mã hóa. (ví dụ, khi tôi xuất B, tôi thực sự xuất ra một hợp âm, chỉ bao gồm B trong tất cả các quãng tám, hơi giống với guitar 12 dây).

Tiếp theo, tôi nhớ từ lớp âm nhạc ở trường trung học rằng hầu hết các chuyển đổi ghi chú đều nhỏ (tăng hoặc giảm một nốt). Nhảy là ít phổ biến hơn. Điều này cho chúng ta biết rằng có lẽ ít entropy trong các kích thước nhảy hơn so với trong các ghi chú.

Vì vậy, cách tiếp cận của chúng tôi là chia nguồn của chúng tôi thành một số khối - Tôi thấy 14 khối mỗi giây hoạt động tốt (lưu ý phụ, tôi luôn tự hỏi tại sao âm thanh được mã hóa ở 44100 Hz. Hóa ra 44100 có rất nhiều yếu tố, vì vậy tôi có thể chọn 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 14, 15, 18, 20, 21, 25, 28 hoặc 30 khối mỗi giây và nó sẽ được phân chia sạch sẽ ). Sau đó, chúng tôi FFT các khối này (tốt, về mặt kỹ thuật không nhanh, vì thư viện tôi đã sử dụng không nhanh đối với các khối không có công suất 2. Và về mặt kỹ thuật tôi đã sử dụng một biến đổi Hartley , không phải Fourier).

Sau đó, chúng tôi tìm thấy ghi chú nghe có vẻ to nhất (tôi đã sử dụng trọng số A , với mức cắt cao và thấp, chủ yếu là vì nó dễ thực hiện nhất) và mã hóa ghi chú này hoặc mã hóa sự im lặng (phát hiện im lặng dựa trên SNR - SNR thấp là sự im lặng).

Sau đó, chúng tôi dịch các ghi chú được mã hóa thành các bước nhảy và đưa chúng vào một bộ mã hóa số học thích ứng. Quá trình dịch sang văn bản tương tự như câu hỏi nén hình ảnh (nhưng liên quan đến việc sử dụng lạm dụng BigInteger).

Cho đến nay, rất tốt, nhưng nếu mẫu có quá nhiều entropy thì sao? Chúng tôi sử dụng một mô hình tâm lý thô để loại bỏ một số. Bước nhảy entropy thấp nhất là "không thay đổi", vì vậy chúng tôi xem dữ liệu FFT của chúng tôi để cố gắng tìm các khối mà người nghe có thể sẽ không chú ý nếu chúng tôi tiếp tục phát ghi chú trước đó, bằng cách tìm các khối có ghi chú từ khối trước đó gần như to như nốt lớn nhất (trong đó "gần như" được điều khiển bởi tham số chất lượng).

Vì vậy, chúng tôi đã có một mục tiêu gồm 140 ký tự. Chúng tôi bắt đầu bằng cách mã hóa ở chất lượng 1.0 (chất lượng tối đa) và xem có bao nhiêu ký tự. Nếu quá nhiều, chúng tôi giảm xuống 0,95 và lặp lại, cho đến khi chúng tôi đạt được 140 ký tự (hoặc chúng tôi bỏ sau chất lượng 0,05). Điều này làm cho bộ mã hóa trở thành một bộ mã hóa n-pass, cho n <= 20 (mặc dù nó cũng không hiệu quả trong các lĩnh vực khác, vì vậy, đó là).

Bộ mã hóa / giải mã dự kiến ​​âm thanh ở định dạng đơn âm s16be. Điều này có thể đạt được bằng cách sử dụng avconv như:

#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

Để chạy bộ mã hóa:

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"

Mã đầy đủ tại https://github.com/jamespic/twelvestring .

Cạm bẫy cần lưu ý: Bạn sẽ cần thư viện Mã hóa Số học của nayuki, hiện không có sẵn các tạo tác Maven. Thay vào đó, bạn sẽ cần xây dựng và cài đặt cục bộ của nhà phát triển .

Và đây là một số mẫu. Chúng nghe có vẻ khủng khiếp, nhưng chỉ có thể nhận ra:

  • Beethoven's 5: nguyên bản , được mã hóa - 檁 囉 罓 佖姑 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻 椻茏 蛏 姆 臸 胝 婁 遼 憀 麁 黦 掏 毈 喙 綄 鴀 耢 椚 筤 菮 蟞 斗 俼 湛 营 筬 禴 籙 嬧 窻 丄
  • Fur Elise: nguyên bản , được mã hóa - 忢 擫 鏝苯 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺 劺夥 俰 焵 韀 冊 嗥 燠 鱧 駟
  • Twinkle Twinkle Little Star: nguyên bản , được mã hóa - 悺 矜 莳 錥 谴 憝 漿
  • Một chiptune vui vẻ: nguyên bản , được mã hóa - 詐 諥 尘 牿灼 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲 攲蚖 醶

Cập nhật

Tôi đã điều chỉnh ngưỡng im lặng trong mã và mã hóa lại. Các bảng mã đã được cập nhật tương ứng. Ngoài ra, tôi đã thêm một bài hát khác (về mặt kỹ thuật không phải là nguồn mở, nhưng tôi nghi ngờ người giữ bản quyền gốc sẽ cảm thấy IP của họ đang bị đe dọa), chỉ để cho vui:

  • Imperial March: nguyên bản , được mã hóa - 讶 湠 衢唂 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰 焰偋 隆

Cập nhật thêm

Tôi đã điều chỉnh bộ mã hóa một chút và nó có tác động đáng ngạc nhiên đến chất lượng (tôi đã quên rằng trong DHT, tín hiệu lệch pha có hiệu quả âm, vì vậy tôi đã bỏ qua các tín hiệu lệch pha).

Một phiên bản trước đó của mã chỉ lấy các tín hiệu lệch pha lớn hơn, nhưng giờ chúng ta lấy RMS. Ngoài ra, tôi đã thêm một chức năng cửa sổ khá bảo thủ vào bộ mã hóa (Tukey, alpha 0.3), để cố gắng chống lại sự giả tạo.

Mọi thứ đều được cập nhật tương ứng.


1
Tôi không thể chơi Twinkle Twinkle và chiptune. Fur Elise khá gần gũi, trong khi Beethoven hầu như không thể nhận ra, haha.
justhalf

Bạn có muốn thử Twinkle Twinkle và Chiptune nữa không? Tôi nghĩ rằng tôi đã sửa các URL.
James_pic

1
Nó hoạt dộng bây giờ. Twinkle Twinkle khá giảm dần. Nhưng những gì đang xảy ra ở cuối?
justhalf

Vâng, tôi không hoàn toàn chắc chắn những gì xảy ra ở cuối. Tôi nghi ngờ nó xảy ra ở đâu đó trong mã hóa số học. Trong phiên bản trước của mã, luồng bị chấm dứt bởi ký hiệu EOF, nhưng trong một số trường hợp, bộ giải mã không đọc được ký hiệu EOF. Tôi nghi ngờ tôi đã không đóng BitOutputStream một cách chính xác, nhưng tôi sẽ xem xét nó.
James_pic

1
Vâng, trên thực tế đó là chính xác nó. Có một BitOutputStream::closephương pháp tôi đã quên gọi. Tôi sẽ sửa mã và cập nhật kết quả đầu ra.
James_pic

11

Con trăn

Tôi không thực hiện bất kỳ thao tác đặc biệt nào liên quan đến UTF-8, vì vậy việc gửi của tôi vượt qua yêu cầu 140 byte. Tôi không tuyên bố về tính hữu ích, độ chính xác hoặc hiệu quả của giải pháp của tôi.

Tôi đã sử dụng tốc độ mẫu 44100 Hz cho đầu vào và đầu ra. SAMPLES_PER_BYTE kiểm soát chất lượng chuyển đổi. Con số càng thấp, chất lượng âm thanh càng tốt. Các giá trị tôi sử dụng được đưa ra trong phần kết quả.

Sử dụng

Mã hóa

Tập tin đầu vào phải là một wav. Nó chỉ mã hóa kênh đầu tiên.

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

Giải mã

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

Chơi nhạc giải mã

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

Mật mã

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

Đầu vào

Bài dự thi chính thức của tôi là Impromptu cho Pianoforte và Beatbox của Kevin MacLeod . Đối với tệp này, tôi đã sử dụng SAMPLES_PER_BYTE là 25450.

Tôi cũng đã tự do mã hóa Twinkle, Twinkle, Little Star với SAMPLES_PER_BYTE là 10200. Nghe có vẻ hay hơn nhiều.

Đầu ra

Sự ngẫu hứng cho Pianoforte và Beatbox

aWnxQDg4mWqZWVl6W+LyOThfHOPyQThAe4x5XCqJK1EJ8Rh6jXt5XEMpk1Epe5JqTJJDSisrkkNCSqnSkkJDkiorCZHhCxsq8nlakfEp8vNb8iqLysp6MpJ7s4x7XlxdW4qKMinJKho

Liên kết

Ngôi sao nhỏ lấp lánh

HBobGlJSUlJSY2FlYVNRUVFCQkJCQjs5PDksKisqGxoZGVFTUVNRREFDQjs6OjoqKykpKVRRVFJDQkJCOjs6OzksKikpGxobG1JSUlNRZWFlYVNSUVFCQkJDQTw5PDorKisqGhsZGRk

Liên kết

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.