• Re: [d2t] Data to Tones

    From Stefan Claas@21:1/5 to Chris M. Thomasson on Tue Dec 31 22:56:39 2024
    Chris M. Thomasson wrote:
    On 12/27/2024 2:25 PM, Onion Courier wrote:
    Chris M. Thomasson wrote:
    On 12/27/2024 1:45 PM, Onion Courier wrote:
    https://jmp.sh/ELTus4hj
    (download and decode with Python3 code below)

    [snip code]

    Interesting to me. Well, fwiw, here is one of my tries. Cantor Pairing notes via MIDI:

    https://youtu.be/XkwgJt5bxKI

    A drum kit:

    https://youtu.be/712wWf7Q9sE

    Really nice!


    Thank you! :^)

    Happy New Year!

    Btw, I actually made it into the AMS webpage. A screenshot:

    https://i.ibb.co/64dSF1d/image.png

    I am humbled.

    Cool! Happy News Year!

    --
    Regards
    Stefan

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Onion Courier@21:1/5 to All on Fri Dec 27 21:45:42 2024
    https://jmp.sh/ELTus4hj
    (download and decode with Python3 code below)

    import argparse
    import math
    import numpy as np
    import struct
    import sys
    from scipy.fftpack import fft

    SAMPLE_RATE = 44100
    DURATION = 0.2
    FFT_SIZE = 8820
    AMPLITUDE_8BIT = 127
    AMPLITUDE_16BIT = 32760
    BASE_FREQ = 440.0
    FREQ_STEP = 25.0 # Larger step size since we only need 16 values

    def generate_tone_buffer(freq, use_16bit):
    num_samples = int(SAMPLE_RATE * DURATION)
    amplitude = AMPLITUDE_16BIT if use_16bit else AMPLITUDE_8BIT
    bytes_per_sample = 2 if use_16bit else 1
    buf = bytearray(num_samples * bytes_per_sample)

    for i in range(num_samples):
    t = i / SAMPLE_RATE
    window = 0.5 * (1 - math.cos(2 * math.pi * i / (num_samples - 1)))
    sample = int(amplitude * math.sin(2 * math.pi * freq * t) * window)

    if use_16bit:
    struct.pack_into('>h', buf, i * 2, sample)
    else:
    buf[i] = sample + AMPLITUDE_8BIT

    return buf

    def write_au_header(use_16bit):
    encoding = 0x03 if use_16bit else 0x02
    header = bytearray([
    0x2e, 0x73, 0x6e, 0x64,
    0x00, 0x00, 0x00, 0x20,
    0xff, 0xff, 0xff, 0xff,
    0x00, 0x00, 0x00, encoding,
    0x00, 0x00, 0xac, 0x44,
    0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00
    ])
    sys.stdout.buffer.write(header)

    def encode_tones(data, use_16bit, tone_buffers):
    write_au_header(use_16bit)
    hex_str = data.hex()

    for c in hex_str:
    index = int(c, 16)
    sys.stdout.buffer.write(tone_buffers[index])

    def detect_frequency(samples):
    windowed_samples = samples * np.hanning(len(samples))
    spectrum = fft(windowed_samples)
    magnitude = np.abs(spectrum[:len(spectrum) // 2])
    peak_index = np.argmax(magnitude)
    freq = peak_index * SAMPLE_RATE / FFT_SIZE

    if peak_index > 0 and peak_index < FFT_SIZE // 2 - 1:
    alpha = magnitude[peak_index - 1]
    beta = magnitude[peak_index]
    gamma = magnitude[peak_index + 1]
    correction = 0.5 * (alpha - gamma) / (alpha - 2 * beta + gamma)
    freq += correction * SAMPLE_RATE / FFT_SIZE

    return freq

    def freq_to_hex(freq):
    index = round((freq - BASE_FREQ) / FREQ_STEP)
    if 0 <= index < 16:
    return format(index, 'x')
    return None

    def decode_tones(input_data, use_16bit):
    bytes_per_sample = 2 if use_16bit else 1
    input_data = input_data[24:]
    num_samples = len(input_data) // bytes_per_sample

    if use_16bit:
    samples = np.frombuffer(input_data, dtype='>i2').astype(np.float64) / AMPLITUDE_16BIT
    else:
    samples = (np.frombuffer(input_data, dtype=np.uint8).astype(np.float64) - AMPLITUDE_8BIT) / AMPLITUDE_8BIT

    hex_output = []
    for i in range(0, num_samples, FFT_SIZE):
    if i + FFT_SIZE > num_samples:
    break
    window = samples[i:i + FFT_SIZE]
    freq = detect_frequency(window)
    digit = freq_to_hex(freq)
    if digit is not None:
    hex_output.append(digit)

    decoded_data = bytes.fromhex(''.join(hex_output))
    sys.stdout.buffer.write(decoded_data)

    def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-d', '--decode', action='store_true', help='Decode mode')
    parser.add_argument('-16', '--use16bit', action='store_true', help='Use 16-bit audio (default: 8-bit)')
    args = parser.parse_args()

    tone_buffers = [
    generate_tone_buffer(BASE_FREQ + i * FREQ_STEP, args.use16bit)
    for i in range(16)
    ]

    input_data = sys.stdin.buffer.read()

    if args.decode:
    decode_tones(input_data, args.use16bit)
    else:
    encode_tones(input_data, args.use16bit, tone_buffers)

    if __name__ == '__main__':
    main()

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Onion Courier@21:1/5 to Chris M. Thomasson on Fri Dec 27 22:25:26 2024
    Chris M. Thomasson wrote:
    On 12/27/2024 1:45 PM, Onion Courier wrote:
    https://jmp.sh/ELTus4hj
    (download and decode with Python3 code below)

    [snip code]

    Interesting to me. Well, fwiw, here is one of my tries. Cantor Pairing
    notes via MIDI:

    https://youtu.be/XkwgJt5bxKI

    A drum kit:

    https://youtu.be/712wWf7Q9sE

    Really nice!

    --
    Regards
    Stefan

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)