import wave
from array import array
from math import ceil, pow
from PIL import Image

IN_WAV = "C.wav"  # входной WAV (8- или 16-бит, моно или стерео)
OUT_PNG = "encoded.png"
IMG_W = 1280
EXPONENT = 2  # задаваемая степень

def read_wav_any(fname):
    wf = wave.open(fname, "rb")
    nch = wf.getnchannels()
    sw = wf.getsampwidth()
    fr = wf.getframerate()
    nframes = wf.getnframes()
    raw = wf.readframes(nframes)
    wf.close()

    if sw == 1:
        data = array("B", raw)  # unsigned 0..255
    elif sw == 2:
        data16 = array("h", raw)  # signed -32768..32767
        data = array("B", [(s + 32768) >> 8 for s in data16])
    else:
        raise ValueError("Unsupported sample width: %d" % sw)

    if nch == 2:
        data = array("B", data[0::2])
    elif nch != 1:
        raise ValueError("Unsupported channel count: %d" % nch)

    return data, fr

def build_deltas(data8):
    deltas = []
    prev = data8[0]
    for s in data8[1:]:
        d = s - prev
        if d < -127:
            d = -127
        if d > +127:
            d = +127
        deltas.append(d)
        prev = (prev + d) & 0xFF
    return data8[0], deltas

def scale_deltas(deltas, exponent):
    scaled_deltas = []
    max_val = 0

    for d in deltas:
        if d < 0:
            scaled_d = -pow(abs(d), exponent)
        else:
            scaled_d = pow(d, exponent)
        scaled_deltas.append(scaled_d)
        if abs(scaled_d) > max_val:
            max_val = abs(scaled_d)

    if max_val == 0:
        return deltas

    scale_factor = (127 * pow(127, exponent - 1)) / max_val

    for i in range(len(scaled_deltas)):
        scaled_deltas[i] = int(scaled_deltas[i] * scale_factor)

    return scaled_deltas

def encode_png(sample0, deltas, width, out_png):
    n = 1 + len(deltas)
    h = ceil(n / width)
    img = Image.new("L", (width, h), 128)
    px = img.load()

    px[0, 0] = sample0

    for i, d in enumerate(deltas, start=1):
        x = i % width
        y = i // width
        px[x, y] = (d + 128) & 0xFF

    img.save(out_png)
    print(f"Saved {out_png} ({width}×{h})")

def main():
    data8, fr = read_wav_any(IN_WAV)
    s0, deltas = build_deltas(data8)
    scaled_deltas = scale_deltas(deltas, EXPONENT)
    encode_png(s0, scaled_deltas, IMG_W, OUT_PNG)

if __name__ == "__main__":
    main()
import wave
from array import array
from math import pow
from PIL import Image

IN_PNG    = "encoded.png"
OUT_WAV   = "decoded.wav"
FR        = 10500   # частота дискретизации, как в оригинале
EXPONENT  = 2       # та же степень, что и при кодировании

def decode_png(fname):
    """
    Читаем PNG, получаем первый отсчёт и массив байтов со смещениями (+128).
    """
    img = Image.open(fname).convert("L")
    w, h = img.size
    px = img.load()

    flat = [px[x, y] for y in range(h) for x in range(w)]
    first_sample = flat[0]                      # байт 0…255
    deltas = [val - 128 for val in flat[1:]]    # смещения в −128…+127
    return first_sample, deltas

def reverse_scale_deltas(deltas, exponent):
    """
    Обратное масштабирование, зеркально scale_deltas().
    1) Восстанавливаем scale_factor (он же используемый при кодировании).
    2) Домножаем d на 1/scale_factor.
    3) Извлекаем корень степени exponent.
    """
    if not deltas:
        return []

    # наибольшая абсолютная величина закодированных дельт
    max_enc = max(abs(d) for d in deltas)

    # та же формула, что и в encode: scale_factor = (127 * 127**(exp-1)) / max_val
    scale_factor = (127 * pow(127, exponent - 1)) / max_enc

    restored = []
    inv_e = 1.0 / exponent
    for d in deltas:
        # undo scale: получаем pow(original_d, exponent)
        scaled = d / scale_factor
        # корнем степени exponent возвращаем original_d
        # сохраняем знак
        orig = pow(abs(scaled), inv_e)
        if d < 0:
            orig = -orig
        # округляем к ближайшему целому
        restored.append(int(orig + (0.5 if orig > 0 else -0.5)))
    return restored

def rebuild_8bit(first, deltas):
    """
    По начальному значению first и списку дельт
    восстанавливаем 8-битный поток отсчётов.
    """
    samples = [first]
    prev = first
    for d in deltas:
        s = (prev + d) & 0xFF
        samples.append(s)
        prev = s
    return samples

def write_wav_16bit(fname, data8, framerate):
    """
    Записываем 8-битный массив в 16-битный WAV (signed 16bit PCM).
    """
    # превращаем 8-бит unsigned [0..255] в signed 16-bit [-32768..32767]
    data16 = array("h", [((b - 128) << 8) for b in data8])
    wf = wave.open(fname, "wb")
    wf.setnchannels(1)
    wf.setsampwidth(2)
    wf.setframerate(framerate)
    wf.writeframes(data16.tobytes())
    wf.close()
    print(f"Saved {fname}: {len(data8)} samples @ {framerate} Hz")

def main():
    first, enc_deltas = decode_png(IN_PNG)
    orig_deltas = reverse_scale_deltas(enc_deltas, EXPONENT)
    data8       = rebuild_8bit(first, orig_deltas)
    write_wav_16bit(OUT_WAV, data8, FR)

if __name__ == "__main__":
    main()

код декодера работает неправильно