EN
УчилищеPython

Резачка за училищен звънец

Аудио инструмент, който анализира песни, предлага най-подходящите откъси за училищен звънец и позволява преглед, настройка и експорт.

README

🔔 School Bell Cutter

Автоматично намиране и рязане на най-добрия момент от песен за училищен звънец

School Bell Cutter е десктоп приложение с графичен интерфейс, което анализира аудио файлове и автоматично открива най-подходящия момент за рязане — така че звънецът да звучи отчетливо, на удар и без изрязани думи или слаби пасажи. Поддържа множество алгоритми за анализ, аудио обработка в реално време и директен предварителен преглед преди запис.


📋 Съдържание


🎯 Защо съществува това?

Ръчното рязане на музика за звънци е досаден и итеративен процес. Трябва да:

  1. Отвориш аудио редактор
  2. Слушаш откъс по откъс
  3. Намериш момент, където песента „влиза" убедително
  4. Режеш, добавяш fade-in/fade-out, нормализираш
  5. Повтаряш, докато резултатът звучи добре

Това отнема 10-30 минути за един звънец. School Bell Cutter го прави за под 30 секунди — автоматично, с научно базирани алгоритми за анализ на аудио.


⚙️ Как работи

Приложението следва следния pipeline:

Входен файл
     │
     ▼
[Librosa зарежда аудиото като float32 array]
     │
     ▼
[Избран алгоритъм изчислява "score" за всяка позиция]
     │
     ▼
[top_candidates() намира топ 5 позиции без припокриване]
     │
     ▼
[Потребителят преглежда, коригира и избира]
     │
     ▼
[FFmpeg реже, обработва и записва изходния файл]

Стъпка 1 — Зареждане

При избор на файл librosa.load() го конвертира до mono float32 с оригиналната честота на дискретизация. Данните се пазят в RAM за бърз достъп по-късно. Зареждането е в отделен thread — интерфейсът не замръзва.

Стъпка 2 — Анализ

Избраният алгоритъм обхожда аудиото в рамките на зададения времеви прозорец (От → До) и изчислява скор за всяка потенциална начална позиция на клипа. Работи с hop size от 512 семпъла (~11.6 ms резолюция при 44100 Hz).

Стъпка 3 — Топ кандидати

top_candidates() прилага non-maximum suppression: след намиране на най-добрия пик, блокира зона с ширина clip_dur / 2 около него, преди да търси следващия. Така 5-те кандидата са реално различни позиции, а не клъстер около един пик.

Стъпка 4 — Рязане с FFmpeg

Рязането се извършва директно с FFmpeg чрез subprocess, без междинен файл от pydub. Командата изглежда така:

ffmpeg -y -ss <start> -i <input> -t <duration> -af <filters> -ar 44100 -ac 2 <output>

Флагът -ss е поставен преди -i — това е „fast seek" режимът на FFmpeg, значително по-бърз при дълги файлове.


🧠 Алгоритми за анализ

🚀 Fusion (препоръчително)

Комбинира резултатите от всички останали методи чрез тегловно гласуване:

Fusion = Smart Bell × 0.4 + Onset × 0.3 + Percussive × 0.2 + Chorus × 0.1

Всеки метод е нормализиран до [0, 1] преди комбинирането. Fusion е най-устойчив при разнообразни жанрове.

🔔 Smart Bell

Специално проектиран за звуци с ясна атака (като звънец, удар на барабан или пиано). Комбинира:

  • Onset strength (0.7 тегло) — открива моменти с рязко нарастване на енергията
  • RMS energy (0.3 тегло) — предпочита части с висока средна сила

При конволюцията използва намаляващо ядро (linspace(1.0, 0.2)), за да даде по-голяма тежест на началото на потенциалния клип спрямо края му.

🥁 Beat

Открива темпото чрез librosa.beat.beat_track() и ограничава кандидатите само до позиции, съвпадащи с удари. Ако в зададения прозорец няма удари, автоматично се превключва на Energy метода. Работи добре за поп, рок и танцова музика с ясен ритъм.

⚡ Energy

Изчислява RMS (Root Mean Square) енергия и търси позицията с най-висока средна енергия за целия период на клипа (конволюция с правоъгълно ядро). Прост и бърз, подходящ за тежка музика.

🎵 Chorus

Използва self-similarity matrix базирана на хрома характеристики (chroma_cqt). Части с висок структурен повтор (припеви, теми) получават по-висок скор. Подходящ, когато искаш познатата, повтаряща се част от песента.

🎯 Onset

Чист onset strength без допълнителна обработка — открива моменти с максимална концентрация на нови звукови събития. По-прецизен от Energy за жанрове с много промени.

🥁 Percussive

Прилага Harmonic-Percussive Source Separation (HPSS) чрез librosa.effects.hpss() и изчислява RMS само върху ударния компонент. Идеален за хип-хоп, електронна музика или всяко парче с доминиращи барабани.


📦 Изисквания

Python

Python 3.8 или по-нов

Python пакети

Пакет Версия Защо
customtkinter ≥ 5.2 Модерен GUI framework
librosa ≥ 0.9 Аудио анализ и DSP
pydub ≥ 0.25 AudioSegment конфигурация
numpy ≥ 1.21 Числени операции
scipy ≥ 1.7 Сигнална обработка
pygame ≥ 2.0 Предварителен преглед на аудио

Забележка за pygame: Версии под 2.0 нямат pygame.mixer.music.unload(). Приложението го открива автоматично с hasattr() и работи коректно и без него, но може да има проблеми с освобождаването на временния preview файл.

Забележка за librosa: От версия 0.10 librosa.beat.beat_track() връща (ndarray, ndarray) вместо (float, ndarray). Кодът е съвместим и с двете версии.

FFmpeg

FFmpeg е задължителен за рязане и аудио обработка. Не се разпространява с приложението.

Windows: Свали от ffmpeg.org и постави ffmpeg.exe в същата папка като main.py.

Linux/macOS:

# Ubuntu/Debian
sudo apt install ffmpeg

# macOS (Homebrew)
brew install ffmpeg

Приложението търси FFmpeg в следния ред:

  1. Папката на main.py
  2. Папката на стартирания процес
  3. Системния PATH

🛠️ Инсталация

1. Клонирай репозиторито

git clone https://github.com/<твоето-потребителско-име>/school-bell-cutter.git
cd school-bell-cutter

2. Създай виртуална среда (препоръчително)

python -m venv venv

# Windows
venv\Scripts\activate

# Linux/macOS
source venv/bin/activate

3. Инсталирай зависимостите

pip install customtkinter librosa pydub numpy scipy pygame

4. Добави FFmpeg

Windows: Постави ffmpeg.exe (и опционално ffprobe.exe) в папката на проекта.

Linux/macOS: FFmpeg от apt/brew се открива автоматично в PATH.

5. Стартирай

python main.py

При успешно стартиране заглавната лента показва ✅ ffmpeg: ffmpeg.exe.
При неуспех — ❌ ffmpeg НЕ е намерен! и ще се появи диалог с инструкции.


🖥️ Употреба

Основен workflow

1. Избери входен файл  →  2. Задай прозорец  →  3. Анализирай
       ↓                                               ↓
4. Прегледай кандидатите  →  5. (По желание) Коригирай времето
       ↓
6. Изрежи и запази

Подробно описание

Входен файл

Поддържани формати: .mp3, .wav, .flac, .ogg, .m4a, .aac — всичко, което FFmpeg може да декодира.

При избор на файл се стартира фоново зареждане, което показва:

  • Пълна дължина в MM:SS.ss формат
  • Честота на дискретизация в Hz
  • Името на файла

Времеви прозорец

  • От — начало на търсенето в секунди (0 = от самото начало)
  • До — край на търсенето в секунди (остави (края) за цялата песен)

Полезно когато знаеш приблизително къде е хубавата част на песента — стеснявайки прозореца ускоряваш анализа и намаляваш шума от нерелевантни участъци.

Дължина на клипа

Слайдерът контролира дължината на изходния клип от 5 до 120 секунди. Стандартните училищни звънци са 20-45 секунди.

Метод на анализ

Виж раздел [Алгоритми за анализ](#-алгоритми-за анализ). Бутонът ℹ️ отваря описание директно в приложението.

Топ кандидати

След анализа се появяват до 5 кандидата, наредени по скор. За всеки можеш да:

  • ▶ Пусни — чуеш откъса с всички текущи аудио настройки (fade, EQ, нормализация)
  • ⏹ Спри — спреш възпроизвеждането
  • +/- бутони — коригираш начало и край с ±0.5 секунди
  • Ръчно въведеш точно време в полетата (формат MM:SS.ss, напр. 01:23.50)
  • 🔄 — нулираш до оригиналната позиция от алгоритъма
  • ✔ Избери — маркираш като активен кандидат за рязане

Динамично поле показва реалната дължина след корекциите.

Изходен файл

Ако не е зададен изричен път, файлът се записва до оригинала с наставка _bell:

/music/my_song.mp3  →  /music/my_song_bell.mp3

Поддържани изходни формати: .mp3, .wav, .ogg, .flac.


🎚️ Аудио настройки

Нормализация

Контролира финалната сила на звука. Два режима:

LUFS (препоръчително) — Loudness Units Full Scale. Стандарт използван от стрийминг платформи. Прилага ffmpeg loudnorm филтъра.

  • Target по подразбиране: -14 LUFS
  • Диапазон: -23 до -6 LUFS
  • По-ниска стойност = по-тих; по-висока = по-силен

Peak — нормализира до максимална амплитуда. По-просто, но без контрол над perceived loudness.

⚠️ Техническа бележка: FFmpeg loudnorm в единичен проход е приблизителен. За максимална точност е нужен двупроходен анализ (анализ → нормализация). За училищни звънци единичният проход е достатъчен.

Еквалайзер

Параметър Честота Диапазон Ефект
Bass 100 Hz, октава -10 до +10 dB Повече/по-малко дъно
Treble 8000 Hz, октава -10 до +10 dB По-ясни/по-меки висини
Volume — -12 до +12 dB Обща сила след EQ

Еквалайзерът използва FFmpeg equalizer филтър (biquad peaking EQ).

Fade In / Fade Out

Стандартни линейни fade ефекти чрез ffmpeg afade. Диапазон 0-5 секунди.

  • Fade-in стартира от началото на клипа
  • Fade-out завършва точно в края на клипа

Битрейт (само MP3/AAC/OGG)

Стойност Препоръка
128k Минимално приемливо качество
192k Добро качество, малък файл (по подразбиране)
256k Високо качество
320k Максимално MP3 качество

📁 Структура на проекта

school-bell-cutter/
│
├── main.py              # Целият код — GUI + аудио логика
├── ffmpeg.exe           # (Windows) Постави тук ★
├── ffprobe.exe          # (Windows, опционално) По-точен анализ
│
├── song_cache/          # Автоматично създадена при preview
│   └── preview.wav      # Временен файл за предварителен преглед
│
└── README.md

Папката song_cache/ се създава автоматично при първото preview. Може да я изтриеш по всяко време — ще се пресъздаде. Препоръчително е да я добавиш в .gitignore.

.gitignore препоръка

# Cache
song_cache/

# Виртуална среда
venv/
.venv/

# FFmpeg бинарни файлове (не ги commit-вай)
ffmpeg.exe
ffprobe.exe

# Python
__pycache__/
*.pyc
*.pyo

⚠️ Известни ограничения

Loudnorm единичен проход

FFmpeg loudnorm в единичен проход дава приблизителен резултат. При критично важна нормализация (напр. за излъчване) използвай двупроходна команда ръчно или се откажи от нормализацията в приложението и я направи отделно.

Preview файл и Windows file locking

На Windows OS заключва файлове докато се ползват. Ако спреш preview и веднага пуснеш нов, FFmpeg може да даде грешка Access denied при презаписването на preview.wav. Вградено решение: _stop_preview() + 0.2s пауза преди всяко ново preview. При персистиращи проблеми изчакай секунда преди пускане.

Librosa зарежда целия файл в RAM

За много дълги файлове (>1 час) зареждането може да отнеме 30+ секунди и да изисква 1-3 GB RAM. Нямаш нужда от streaming анализ за звънци — те обикновено идват от песни с нормална дължина.

Loudnorm и WAV изход

loudnorm работи правилно и за WAV изход, но резултатът може да се различава леко от MP3 поради различното вътрешно квантизиране.


🔧 Технически детайли

Thread архитектура

Приложението използва три типа threads:

Thread Задача Защита
Main (Tkinter) GUI events —
_load_meta Librosa зареждане threading.Lock за _y, _sr, _total_dur
_run_analysis Алгоритмичен анализ Локално копие на аудио данните под lock
_preview / worker FFmpeg + pygame Daemon thread
_do_cut / worker FFmpeg рязане Daemon thread

Всички UI обновявания от фонови threads минават през self.after(0, callback) — стандартният Tkinter-safe начин.

FFmpeg интеграция

Рязането не използва pydub за самото рязане — само за конфигурация на пътищата. Причините:

  • pydub зарежда целия сегмент в RAM преди запис
  • FFmpeg директно поддържа loudnorm и afade като native филтри
  • По-малко зависимости в критичния path

Командата за рязане е конструирана в build_af_filter() и изпълнена с subprocess.run() с capture_output=True за прихващане на грешки.

Hop size и времева резолюция

Всички librosa операции използват hop_length=512. При 44100 Hz:

Времева резолюция = 512 / 44100 ≈ 11.6 ms

Това е достатъчно за практически цели — разликата от 11ms не се чува.


📄 Лиценз

MIT License — виж LICENSE файла.


🙏 Благодарности

  • librosa — аудио анализ
  • CustomTkinter — модерен GUI
  • FFmpeg — аудио обработка и кодиране
  • pygame — аудио playback

Направен с 🔔 за всички, които режат звънци ръчно и страдат.