- uart_audio.py: поток UART на звуковую карту ПК с подстройкой шага пересчёта по заполнению буфера, ФВЧ 20 Гц, --selftest; - Bin_to_wav.py: записанный .bin -> WAV; - requirements.txt: пакеты Python для всех скриптов.
111 lines
4.7 KiB
Python
111 lines
4.7 KiB
Python
import numpy as np
|
|
import wave
|
|
import sys
|
|
import tkinter as tk
|
|
from tkinter import filedialog
|
|
import os
|
|
|
|
# ================= НАСТРОЙКИ =================
|
|
FS = 12500 # Частота дискретизации (должна совпадать с записью)
|
|
ADC_BITS = 24 # Разрядность исходных данных
|
|
|
|
# 📢 КОЭФФИЦИЕНТ УСИЛЕНИЯ (GAIN)
|
|
# 1.0 = без изменений
|
|
# 10.0 = усилить в 10 раз (20 дБ)
|
|
# 100.0 = усилить в 100 раз (40 дБ)
|
|
GAIN = 5
|
|
|
|
# Разрядность WAV. Пишется обычный PCM: float-WAV открывают далеко не все
|
|
# программы, а 64-битный float, который получался раньше, -- почти никакие.
|
|
# 16 -- открывается везде; 24 -- полная разрядность записи
|
|
WAV_BITS = 16
|
|
|
|
|
|
def convert_bin_to_wav(input_filename, output_filename, gain):
|
|
print(f"Чтение файла: {os.path.basename(input_filename)}...")
|
|
|
|
# --- 1. Чтение 24-битного Big Endian файла ---
|
|
# (Используем ту же логику чтения, что и в анализаторе)
|
|
raw = np.fromfile(input_filename, dtype=np.uint8)
|
|
n_samples = len(raw) // 3
|
|
raw = raw[:n_samples * 3]
|
|
data = raw.reshape(-1, 3)
|
|
|
|
# Сборка 3 байт в int32
|
|
samples = (data[:, 0].astype(np.int32) << 16) | \
|
|
(data[:, 1].astype(np.int32) << 8) | \
|
|
data[:, 2].astype(np.int32)
|
|
|
|
# Обработка знака (Two's complement)
|
|
mask = (samples & 0x800000) != 0
|
|
samples[mask] -= 0x1000000
|
|
|
|
# --- 2. Нормализация (перевод в диапазон -1.0 ... 1.0) ---
|
|
# Сразу float64: numpy 1.x всё равно приводит к нему при делении на 8388607
|
|
max_val = 2 ** (ADC_BITS - 1) - 1 # 8,388,607
|
|
audio_float = samples.astype(np.float64) / max_val
|
|
|
|
# Убираем постоянную составляющую (DC Offset), чтобы динамик не "выпирало"
|
|
audio_float -= np.mean(audio_float)
|
|
|
|
# --- 3. Применение усиления ---
|
|
print(f"Применяем усиление: x{gain}")
|
|
audio_float *= gain
|
|
|
|
# --- 4. Проверка на клиппинг (перегрузку) ---
|
|
max_amp = np.max(np.abs(audio_float))
|
|
if max_amp > 1.0:
|
|
clipped_samples = np.sum(np.abs(audio_float) > 1.0)
|
|
percent_clipped = (clipped_samples / len(audio_float)) * 100
|
|
print(f"ВНИМАНИЕ: Перегрузка сигнала! Пик: {max_amp:.2f}")
|
|
print(f" Будет обрезано {percent_clipped:.2f}% сэмплов (Hard Clipping).")
|
|
print(" Рекомендуется уменьшить GAIN.")
|
|
|
|
# Обрезаем все, что выше 1.0 или ниже -1.0
|
|
audio_float = np.clip(audio_float, -1.0, 1.0)
|
|
else:
|
|
print(f"Сигнал в норме. Пиковый уровень: {max_amp:.2f} (из 1.0)")
|
|
|
|
# --- 5. Сохранение WAV (PCM) ---
|
|
print(f"Сохранение в WAV ({WAV_BITS}-bit PCM, {FS} Гц)...")
|
|
full = 2 ** (WAV_BITS - 1) - 1
|
|
pcm = np.round(audio_float * full).astype("<i4")
|
|
if WAV_BITS == 16:
|
|
frames = pcm.astype("<i2").tobytes()
|
|
elif WAV_BITS == 24:
|
|
# Младшие три байта каждого int32 little-endian
|
|
frames = pcm.view(np.uint8).reshape(-1, 4)[:, :3].tobytes()
|
|
else:
|
|
raise ValueError(f"WAV_BITS = {WAV_BITS}: поддерживаются 16 и 24")
|
|
with wave.open(output_filename, "wb") as w:
|
|
w.setnchannels(1)
|
|
w.setsampwidth(WAV_BITS // 8)
|
|
w.setframerate(FS)
|
|
w.writeframes(frames)
|
|
print(f"Готово! Файл сохранен:\n {output_filename}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Файл можно передать в командной строке, иначе -- выбор через окно
|
|
if len(sys.argv) > 1:
|
|
input_path = sys.argv[1]
|
|
else:
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
|
|
input_path = filedialog.askopenfilename(
|
|
title="Выберите .bin файл для конвертации",
|
|
filetypes=[("Binary files", "*.bin")]
|
|
)
|
|
|
|
if input_path:
|
|
# Формируем имя выходного файла
|
|
folder = os.path.dirname(input_path)
|
|
name = os.path.basename(input_path).rsplit('.', 1)[0]
|
|
output_path = os.path.join(folder, f"{name}_gain{GAIN}.wav")
|
|
|
|
try:
|
|
convert_bin_to_wav(input_path, output_path, GAIN)
|
|
except Exception as e:
|
|
print(f"Ошибка при конвертации: {e}")
|