Инструменты ПК: прослушивание, перевод в WAV, список зависимостей

- uart_audio.py: поток UART на звуковую карту ПК с подстройкой шага
  пересчёта по заполнению буфера, ФВЧ 20 Гц, --selftest;
- Bin_to_wav.py: записанный .bin -> WAV;
- requirements.txt: пакеты Python для всех скриптов.
This commit is contained in:
2026-10-02 14:21:07 +03:00
parent 2672f8b8a8
commit d6f3ee5ef8
3 changed files with 691 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
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}")