Инструменты ПК: прослушивание, перевод в WAV, список зависимостей
- uart_audio.py: поток UART на звуковую карту ПК с подстройкой шага пересчёта по заполнению буфера, ФВЧ 20 Гц, --selftest; - Bin_to_wav.py: записанный .bin -> WAV; - requirements.txt: пакеты Python для всех скриптов.
This commit is contained in:
@@ -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}")
|
||||
Reference in New Issue
Block a user