Текст раскодировщика программ формата SAVEX

Раскодировщик работает под управлением Python не ниже 3 версии. Использует дополнительные библиотеки wave, numpy, os, datetime.
Запуск в консоли Линукс командой «python3 ./d3-28_wav_decoder.py»

import wave
import numpy as np
import os
import datetime
atime = datetime.datetime.today().strftime("%d.%m.%Y-%H.%M.%S") # Время начала раскодировки
types = {
    1: np.int8,
    2: np.int16,
    4: np.int32
}


duration = 0.05  # second   # Параметры звукового сигнала
freq = 400  # Hz
(helptext)
print()
print('Список звуковых файлов текущего каталога: ')
wav_files = [f for f in os.listdir() if f.endswith('.wav')]
print('\n'.join(wav_files))

inputfile = input("Введите имя WAV файла для раскодировки: ")

if not os.path.exists(inputfile):
    print ('Файл с таким именем в текущем каталоге не найден. Начните с начала.')
    print()
    os.system('play --no-show-progress --null --channels 1 synth %s sine %f' % (duration, freq))    # Звуковой сигнал
    quit()
print()
print(atime, "Начат процесс раскодирования....")
os.system('play --no-show-progress --null --channels 1 synth %s sine %f' % (0.05, 1600))    # Звуковой сигнал
outfile = inputfile.split('.')
out_file = open(str(outfile[0])+'.decode', 'w') # Выходной текстовый файл с отладочной информацией.
out_file1 = open(str(outfile[0])+'.txt', 'w')   # Выходной текстовый фал. Содержит только байты команд Д3-28. Полностью пригоден для исполнения в имитаторе

wav = wave.open(inputfile, mode="r")       # Чтение входного WAV-файла
(nchannels, sampwidth, framerate, nframes, comptype, compname) = wav.getparams()       # Чтение параметров входного WAV-файла
content = wav.readframes(nframes)
samples = np.fromstring(content, dtype=types[sampwidth])    # Из звукового файла вытаскиваются только значения отдельных сэмплов
samples = abs(samples)  # Все полуволны звукового файла становятся положительными, чтобы проще было искать максимумы
len1 = len(samples)   ### Количество сэмплов всего (в обоих каналах)

print("Отсекаем помеху:")
print("              ",str(len1), end="\r") # Вывод хода обработки
pomeha = max(samples)/3         # Устанвливается уровень "помехи". Меньше этого уровня все приравнивается нулю
for i in range(0,len1) :        # Отсекается сигнал ниже уровня помехи.
    if abs(samples[i]) < pomeha :
        samples[i] = 0.0
    if (i % 1000) == 0:
        print(str(i), end="\r") # Вывод хода обработки

print("Прореживаем локальные максимумы:")
print("              ",str(len1), end="\r") # Вывод хода обработки
for i in range(1,len1) :        # 
    if (abs(samples[i]) > 0) & (abs(samples[i-1]) > 0) :
        samples[i] = 0.0
        samples[i-1] = 0.0    
    if (i % 1000) == 0:
        print(str(i), end="\r") # Вывод хода обработки
        
# Далее обрабатываем WAV-файл и находим значения номеров сэмплов для каждого из максимумов амплитуды
# Обработка сэмплов происходит последовательно сразу обоих каналов без разделения по каналам

bit_code_raw = "" # Это строка, в которую записываются нули и единицы последовательно, если найден локальный максимум
nsample = 0
nsample1 = 0
nbyte = 0
max_temp = 0        # Это текущий номер сэмпла с локальными максимумом амплитуды
byte_sample = [0]   # Номера сэмплов каждого девятого бита в раскодированном байте (для поиска ошибок)   
while nsample < (len1-4) :
    nbyte = nbyte + 1
    if samples[nsample] == 0:        # Пропускаем пустое место между локальными максимумами
        nsample = nsample+1
    else :
        nsample1 = nsample
        max_temp = nsample1
        while samples[nsample1] > 0:    # Находим локальный максимум, запоминаем в max_temp
            if samples[nsample1+2] > samples[nsample1]:
                max_temp = nsample1+2
            nsample1 = nsample1+2
        if (max_temp % 2) == 0:                 # Если номер сэмпла четный (канал нулей в WAVчике)
            bit_code_raw = bit_code_raw + "0"   # то запишем в строку нолик
        else:                                   # Если номер сэмпла нечетный (канал единиц в WAVчике)
            bit_code_raw = bit_code_raw + "1"   # то запишем в строку единичку
        if ((len(bit_code_raw) % 9) == 0) :     # Запоминаем номер сэмпла каждого девятого бита в раскодированном байте
            byte_sample.append(nsample)
        nsample = nsample1

polubyte = {"0000":"00", "0001":"01", "0010":"02", "0011":"03", "0100":"04", "0101":"05", "0110":"06", "0111":"07", "1000":"08", "1001":"09", "1010":"10", "1011":"11", "1100":"12", "1101":"13", "1110":"14", "1111":"15"}     # Справочник перекодировки из битовых тетрад в десятичные
bit_code = [str(i) for i in bit_code_raw]   # Преобразуем строку битов в список строковых значений
code = "".join(bit_code)    # Убираем разделители между строковыми значениями

#len3 = (len(bit_code_raw))/9
#print(len3)
len3a = len(code)/9     # Количество байт в раскодировке
print(len3a)

if (len3a - int(len3a)) > 0 :   # Если количество 9-битовых байт дробное (при ошибках) 
    while (len(code) - int(len(code))) > 0 :    # При дробном количестве дополняем нулями до целого
        code = code + "0" 
        len3a = len(code)/9

chet = [0,2,4,6,8]  # Справочник четных символов в байте
nechet = [1,3,5,7]  # Справочник нечетных символов в байте
bit_chetnosti = ""
ks = 0

print('\n'.join(helptext), file=out_file)   
for i in range(0, int(len3a), 1) :
    i9 = i*9            # Номер первого символа байта (первого полубайта)
    i94 = i*9 + 4       # Номер первого символа второго полубайта
    i98 = i*9+8         # Номер символа четности
    byte = code[i9 : i98]   # Значение раскодированного текущего байта

    chetnost = (byte.count("1"))    #    Вычисленное значение четности байта для сравнения со считанным
    if chetnost in chet :
        bit_chetnosti = "0"
    if chetnost in nechet :
        bit_chetnosti = "1"

    if code[i98] != bit_chetnosti : # Если прочитанный с ленты и вычисленный биты четности не совпадают
        znak = " ?"
    else :              # Если биты четности данного байта совпадают
        znak = " +"
    polubyte1 = polubyte[str(code[i9 : i94])]
    polubyte2 = polubyte[str(code[i94 : i98])]
    ks = ks + int(polubyte1) + int(polubyte2)   # Прибавляем текущий байт в контрольную сумму
    print(i, byte, " ", code[i98], bit_chetnosti, znak, polubyte1 + polubyte2, byte_sample[i], int(byte_sample[i]/2),  file=out_file)
    print(polubyte1 + polubyte2, file=out_file1)

if ((polubyte1 + polubyte2) == "0512"):     # Исключаем байт конца программы из контрольной суммы (при условии, что 0512 последний байт)
        ks = ks -17
print("Количество байт = ", int(len3a),)
print("Контрольная сумма = ", ks,)
print("Контрольная сумма = ", ks, file=out_file)
btime = datetime.datetime.today().strftime("%d.%m.%Y-%H.%M.%S")     # Время окончания раскодировки
print()
print("Раскодирование начато в ", atime, ",", "окончено в", btime)
os.system('sleep 0.9')
os.system('play --no-show-progress --null --channels 1 synth %s sine %f' % (0.05, 1600))    # Звуковой сигнал
print("Раскодирование начато в ", atime, ",", "окончено в", btime, file=out_file)
print("Операция завершена успешно.")
os.system('sleep 0.9')
print("Листинг находится в текущем каталоге в файле ", str(outfile[0])+".txt")
os.system('sleep 0.9')
print("Отладочный листинг находится в текущем каталоге в файле ", str(outfile[0])+".decode")
os.system('sleep 0.1')
os.system('play --no-show-progress --null --channels 1 synth %s sine %f' % (0.05, 1600))    # Звуковой сигнал
os.system('sleep 0.1')
os.system('play --no-show-progress --null --channels 1 synth %s sine %f' % (0.05, 1600))    # Звуковой сигнал
print()
out_file.close()    # Закрываем все файлы
out_file1.close()
wav.close()

Виталий