Repository: scaperot/the-BPM-detector-python
Branch: master
Commit: 788e5c03d0bc
Files: 4
Total size: 6.2 KB
Directory structure:
gitextract_4nt3c9pe/
├── .gitignore
├── README.md
├── bpm_detection/
│ └── bpm_detection.py
└── requirements.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.vscode/
build/
dist/
env/
htmlcov/
.coverage
*.DS_Store
*.pyc
*.bak
*.mp3
*.m4a
*.tmp
*.wav
================================================
FILE: README.md
================================================
BPM Detector in Python
=======================
Implementation of a Beats Per Minute (BPM) detection algorithm, as presented in the paper of G. Tzanetakis, G. Essl and P. Cook titled: "Audio Analysis using the Discrete Wavelet Transform".
You can find it here: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.63.5712
Based on the work done in the MATLAB code located at github.com/panagiop/the-BPM-detector-python.
Process .wav file to determine the Beats Per Minute.
## Requirements
Tested with Python 3.10. Key Dependencies: scipy, numpy, pywavelets, matplotlib. See requirements.txt
================================================
FILE: bpm_detection/bpm_detection.py
================================================
# Copyright 2012 Free Software Foundation, Inc.
#
# This file is part of The BPM Detector Python
#
# The BPM Detector Python is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# The BPM Detector Python is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with The BPM Detector Python; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
import argparse
import array
import math
import wave
import matplotlib.pyplot as plt
import numpy
import pywt
from scipy import signal
def read_wav(filename):
# open file, get metadata for audio
try:
wf = wave.open(filename, "rb")
except IOError as e:
print(e)
return
# typ = choose_type( wf.getsampwidth() ) # TODO: implement choose_type
nsamps = wf.getnframes()
assert nsamps > 0
fs = wf.getframerate()
assert fs > 0
# Read entire file and make into an array
samps = list(array.array("i", wf.readframes(nsamps)))
try:
assert nsamps == len(samps)
except AssertionError:
print(nsamps, "not equal to", len(samps))
return samps, fs
# print an error when no data can be found
def no_audio_data():
print("No audio data for sample, skipping...")
return None, None
# simple peak detection
def peak_detect(data):
max_val = numpy.amax(abs(data))
peak_ndx = numpy.where(data == max_val)
if len(peak_ndx[0]) == 0: # if nothing found then the max must be negative
peak_ndx = numpy.where(data == -max_val)
return peak_ndx
def bpm_detector(data, fs):
cA = []
cD = []
correl = []
cD_sum = []
levels = 4
max_decimation = 2 ** (levels - 1)
min_ndx = math.floor(60.0 / 220 * (fs / max_decimation))
max_ndx = math.floor(60.0 / 40 * (fs / max_decimation))
for loop in range(0, levels):
cD = []
# 1) DWT
if loop == 0:
[cA, cD] = pywt.dwt(data, "db4")
cD_minlen = len(cD) / max_decimation + 1
cD_sum = numpy.zeros(math.floor(cD_minlen))
else:
[cA, cD] = pywt.dwt(cA, "db4")
# 2) Filter
cD = signal.lfilter([0.01], [1 - 0.99], cD)
# 4) Subtract out the mean.
# 5) Decimate for reconstruction later.
cD = abs(cD[:: (2 ** (levels - loop - 1))])
cD = cD - numpy.mean(cD)
# 6) Recombine the signal before ACF
# Essentially, each level the detail coefs (i.e. the HPF values) are concatenated to the beginning of the array
cD_sum = cD[0 : math.floor(cD_minlen)] + cD_sum
if [b for b in cA if b != 0.0] == []:
return no_audio_data()
# Adding in the approximate data as well...
cA = signal.lfilter([0.01], [1 - 0.99], cA)
cA = abs(cA)
cA = cA - numpy.mean(cA)
cD_sum = cA[0 : math.floor(cD_minlen)] + cD_sum
# ACF
correl = numpy.correlate(cD_sum, cD_sum, "full")
midpoint = math.floor(len(correl) / 2)
correl_midpoint_tmp = correl[midpoint:]
peak_ndx = peak_detect(correl_midpoint_tmp[min_ndx:max_ndx])
if len(peak_ndx) > 1:
return no_audio_data()
peak_ndx_adjusted = peak_ndx[0] + min_ndx
bpm = 60.0 / peak_ndx_adjusted * (fs / max_decimation)
print(bpm)
return bpm, correl
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Process .wav file to determine the Beats Per Minute.")
parser.add_argument("--filename", required=True, help=".wav file for processing")
parser.add_argument(
"--window",
type=float,
default=3,
help="Size of the the window (seconds) that will be scanned to determine the bpm. Typically less than 10 seconds. [3]",
)
args = parser.parse_args()
samps, fs = read_wav(args.filename)
data = []
correl = []
bpm = 0
n = 0
nsamps = len(samps)
window_samps = int(args.window * fs)
samps_ndx = 0 # First sample in window_ndx
max_window_ndx = math.floor(nsamps / window_samps)
bpms = numpy.zeros(max_window_ndx)
# Iterate through all windows
for window_ndx in range(0, max_window_ndx):
# Get a new set of samples
# print(n,":",len(bpms),":",max_window_ndx_int,":",fs,":",nsamps,":",samps_ndx)
data = samps[samps_ndx : samps_ndx + window_samps]
if not ((len(data) % window_samps) == 0):
raise AssertionError(str(len(data)))
bpm, correl_temp = bpm_detector(data, fs)
if bpm is None:
continue
bpms[window_ndx] = bpm
correl = correl_temp
# Iterate at the end of the loop
samps_ndx = samps_ndx + window_samps
# Counter for debug...
n = n + 1
bpm = numpy.median(bpms)
print("Completed! Estimated Beats Per Minute:", bpm)
n = range(0, len(correl))
plt.plot(n, abs(correl))
plt.show(block=True)
================================================
FILE: requirements.txt
================================================
certifi @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_477u68wvzm/croot/certifi_1671487773341/work/certifi
contourpy==1.0.7
cycler==0.11.0
fonttools==4.38.0
kiwisolver==1.4.4
matplotlib==3.6.3
numpy==1.24.2
packaging==23.0
Pillow==9.4.0
pyparsing==3.0.9
python-dateutil==2.8.2
PyWavelets==1.4.1
scipy==1.10.0
six==1.16.0
gitextract_4nt3c9pe/ ├── .gitignore ├── README.md ├── bpm_detection/ │ └── bpm_detection.py └── requirements.txt
SYMBOL INDEX (4 symbols across 1 files) FILE: bpm_detection/bpm_detection.py function read_wav (line 31) | def read_wav(filename): function no_audio_data (line 58) | def no_audio_data(): function peak_detect (line 64) | def peak_detect(data): function bpm_detector (line 72) | def bpm_detector(data, fs):
Condensed preview — 4 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (7K chars).
[
{
"path": ".gitignore",
"chars": 93,
"preview": ".vscode/\nbuild/\ndist/\nenv/\nhtmlcov/\n.coverage\n*.DS_Store\n*.pyc\n*.bak\n*.mp3\n*.m4a\n*.tmp\n*.wav\n"
},
{
"path": "README.md",
"chars": 599,
"preview": "BPM Detector in Python\n=======================\nImplementation of a Beats Per Minute (BPM) detection algorithm, as presen"
},
{
"path": "bpm_detection/bpm_detection.py",
"chars": 5300,
"preview": "# Copyright 2012 Free Software Foundation, Inc.\n#\n# This file is part of The BPM Detector Python\n#\n# The BPM Detector Py"
},
{
"path": "requirements.txt",
"chars": 344,
"preview": "certifi @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_477u68wvzm/croot/certifi_1671487773341/wor"
}
]
About this extraction
This page contains the full source code of the scaperot/the-BPM-detector-python GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 4 files (6.2 KB), approximately 2.0k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.