pyo for beat recognition

283 views
Skip to first unread message

siggy

unread,
Dec 28, 2011, 5:59:09 AM12/28/11
to pyo-discuss
Hi there,

I am working on a project which takes a .wav file as input and counts
the number of beats in it(for bassy songs like hip-hop, trance)

Will I be able to achieve this in pyo?

The algorithm I am using is as follows:

1) Apply a bandpass filter to the input signal to filter out the beat
producing instrument.

2) Calculate the amplitude of the filtered signal every n*1024(n<=4)
number of samples. Then compare the amplitudes and choose the highest
one as a beat.

I am able to filter the input signal. I require your assistance in
calculation of amplitude. Will I have to create a pyobject?

Further, will it be possible to have a text output saying "beat"
every time a beat is recognized?

Forgive my noobishness. I would appreciate your guidance.

siggy

unread,
Dec 28, 2011, 7:06:35 AM12/28/11
to pyo-discuss
Hi to calculate amplitude this is what i have attempted:

t=SndTable("m1.wav",chnl=1)

what is t.get(300) ? Is it the value of the amplitude at sample no.
300?

Olivier Bélanger

unread,
Jan 6, 2012, 12:29:46 AM1/6/12
to pyo-d...@googlegroups.com
Hi,

Sorry for the delay, I was out of town for hollydays!

First, beat detection is a little bit tricky to implement. It can work very nicely for clean transients in an audio stream, but not necessarily if applied to a full song... Even with the bandpass filtering.

It's in my TODO list to create an attackDetector object but here is a pyo script that implement the process. It is based on transient detection by comparing the current rms value with a delayed one. A trig is sent on detection and recorded in an audio table. You can save the table as audio file or text file with the two functions at the end of the script.

It works very well if you set the input to "mic" and clap your hands in front of the microphone. If you use a soundfile, you can adjust the settings to enhance the results, but don't expect perfection! With some additional features, the C object will be more accurate...

Hope that helps!

Olivier

___________________________________________________

#!/usr/bin/env python
# encoding: utf-8
"""
Attack detector.

"""
from pyo import *

s = Server(sr=44100, nchnls=2, buffersize=512, duplex=1).boot()

### Settings ###
SND_PATH = '/Users/olivier/Dropbox/private/snds/drumBeat.wav' # Enter your sound path here...
INPUT = "snd" # or "mic"    # Input type
DELAY_TIME = 0.005          # Time between current and previous rms values
RMS_FREQ = 20               # Cutoff of the analysis lowpass filter
ATT_THRESH = -3             # Attack threshold in dB
MIN_THRESH = -60            # Minimum threshold in dB
REL_TIME = .1               # Time to wait before reporting a new attack

# Input sound table
snd = SndTable(SND_PATH, chnl=1)
# triggers table (length is the same as sound table)
trig_table = NewTable(length=snd.getDur())

# Input sound
if INPUT == "snd":
    inp = TableRead(snd, snd.getRate()).out()
else:
    inp = Input()
# Input rms value
rms = Follower(inp, freq=RMS_FREQ)
rms_db = AToDB(rms)

# Previous rms value
inp_prev = Delay(inp, delay=DELAY_TIME)
rms_prev = Follower(inp_prev, freq=RMS_FREQ)
rms_prev_db = AToDB(rms_prev)

# if current rms plus threshold is larger than previous rms
rms_over_prev = Compare(rms_db + ATT_THRESH, rms_prev_db, ">")
# if current rms is over minimum attack threshold
rms_over_min = Compare(rms_db, MIN_THRESH, ">")
# if both are true
trig = Compare(rms_over_prev + rms_over_min, 2.0, "==")

# On attack signal, closes the gate for REL_TIME seconds
rel_time = Timer(trig, Trig()+trig)
gate = Compare(rel_time, REL_TIME, ">")

# If attack signal and gte is open, there is a real trig
real_trig = trig * gate

# Print it to the console
printing = Print(real_trig, method=1)

# Record triggers in a table (records for the duration of the table "trig_table")
rec_trig = TableRec(real_trig, trig_table).play()

# Save the triggers table as audio file (wav, 16 bits)
def save_as_audio(filepath):
    trig_table.save(filepath)

# Save the triggers table as text file
def save_as_text(filepath):
    trig_table.write(filepath, False)


s.gui(locals())
___________________________________________________


2011/12/28 siggy <great...@gmail.com>
attack_detector.py

Olivier Bélanger

unread,
Jan 6, 2012, 12:30:34 AM1/6/12
to pyo-d...@googlegroups.com
The get() method returns the real value (positive or negative) of the sample at the position (in sample) in argument.

Olivier

2011/12/28 siggy <great...@gmail.com>
Reply all
Reply to author
Forward
0 new messages