[fix] Better bpm

This commit is contained in:
alban 2020-09-29 15:50:00 +02:00
parent 6ae754e893
commit 73f0765519

View File

@ -38,8 +38,12 @@ _BPM_MIN=10
_BPM_MAX=400 _BPM_MAX=400
# Argument parsing # Argument parsing
# Audio Args
parser = argparse.ArgumentParser(prog='realtime_redis') parser = argparse.ArgumentParser(prog='realtime_redis')
# Standard Args
parser.add_argument("-v","--verbose",action="store_true",help="Verbose")
# Redis Args
parser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
parser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
# Audio Capture Args # Audio Capture Args
parser.add_argument('--list-devices','-L', action='store_true', help='Which devices are detected by pyaudio') parser.add_argument('--list-devices','-L', action='store_true', help='Which devices are detected by pyaudio')
parser.add_argument('--mode','-m', required=False, default='spectrum', choices=['spectrum', 'bpm'], type=str, help='Which mode to use. Default=spectrum') parser.add_argument('--mode','-m', required=False, default='spectrum', choices=['spectrum', 'bpm'], type=str, help='Which mode to use. Default=spectrum')
@ -47,16 +51,11 @@ parser.add_argument('--device','-d', required=False, type=int, help='Which pyaud
parser.add_argument('--sampling-frequency','-s', required=False, default=0.1, type=float, help='Which frequency, in seconds. Default={}f '.format(_SAMPLING_FREQUENCY)) parser.add_argument('--sampling-frequency','-s', required=False, default=0.1, type=float, help='Which frequency, in seconds. Default={}f '.format(_SAMPLING_FREQUENCY))
parser.add_argument('--channels','-c', required=False, default=_CHANNELS, type=int, help='How many channels. Default={} '.format(_CHANNELS)) parser.add_argument('--channels','-c', required=False, default=_CHANNELS, type=int, help='How many channels. Default={} '.format(_CHANNELS))
parser.add_argument('--rate','-r', required=False, default=44100, type=int, help='The audio capture rate in Hz. Default={} '.format(_RATE)) parser.add_argument('--rate','-r', required=False, default=44100, type=int, help='The audio capture rate in Hz. Default={} '.format(_RATE))
#parser.add_argument('--frames','-f', required=False, default=4410, type=int, help='How many frames per buffer. Default={}'.format(_FRAMES_PER_BUFFER)) parser.add_argument('--frames','-f', required=False, default=4410, type=int, help='How many frames per buffer. Default={}'.format(_FRAMES_PER_BUFFER))
# BPM Mode Args # BPM Mode Args
parser.add_argument('--bpm-min', required=False, default=_BPM_MIN, type=int, help='BPM mode only. The low BPM threshold. Default={} '.format(_BPM_MIN)) parser.add_argument('--bpm-min', required=False, default=_BPM_MIN, type=int, help='BPM mode only. The low BPM threshold. Default={} '.format(_BPM_MIN))
parser.add_argument('--bpm-max', required=False, default=_BPM_MAX, type=int, help='BPM mode only. The high BPM threshold. Default={} '.format(_BPM_MAX)) parser.add_argument('--bpm-max', required=False, default=_BPM_MAX, type=int, help='BPM mode only. The high BPM threshold. Default={} '.format(_BPM_MAX))
# Redis Args
parser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
parser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
# Standard Args
parser.add_argument("-v","--verbose",action="store_true",help="Verbose")
args = parser.parse_args() args = parser.parse_args()
# global # global
@ -110,7 +109,6 @@ if( LIST_DEVICES ):
list_devices() list_devices()
os._exit(1) os._exit(1)
p = pyaudio.PyAudio()
def m_bpm(audio_data): def m_bpm(audio_data):
@ -122,7 +120,6 @@ def m_bpm(audio_data):
global bpm global bpm
global start global start
bpm_delay = SAMPLING_FREQUENCY + start - time.time()
# Detect tempo / bpm # Detect tempo / bpm
new_bpm, beats = librosa.beat.beat_track( new_bpm, beats = librosa.beat.beat_track(
@ -138,19 +135,25 @@ def m_bpm(audio_data):
''' '''
# Correct the eventual octave error # Correct the eventual octave error
if new_bpm < bpm_min or new_bpm > bpm_max: if new_bpm < bpm_min or new_bpm > bpm_max:
found = False
octaveErrorList = [ 0.5, 2, 0.3333, 3 ] octaveErrorList = [ 0.5, 2, 0.3333, 3 ]
for key,factor in enumerate(octaveErrorList): for key,factor in enumerate(octaveErrorList):
correction = new_bpm * factor correction = new_bpm * factor
if correction > bpm_min and correction < bpm_max: if correction > bpm_min and correction < bpm_max:
debug( "Corrected bpm to:{}".format(correction)) debug( "Corrected high/low bpm:{} to:{}".format(new_bpm, correction))
new_bpm = correction new_bpm = correction
found = True
break break
if new_bpm < bpm_min : if found == False:
new_bpm = bpm_min if new_bpm < bpm_min :
else : new_bpm = bpm_min
new_bpm = bpm_max else :
new_bpm = bpm_max
debug("new_bpm:{}".format(new_bpm))
''' '''
How to guess the next beats based on the data sent to redis
~~ A Dirty Graph ~~
|start end| |start end|
Capture |........................| Capture |........................|
@ -167,22 +170,29 @@ def m_bpm(audio_data):
. passed (...b....b....b.) . passed (...b....b....b.)
. guessed (..b....b....b....b... . guessed (..b....b....b....b...
Next Beat Calculation b....b....b....b.|..b Next Beat Calculation b....b....b....b.|..b
Beats |last beat
0 1 2 3 4
=> (Delay - last beat) + x*BPM/60 (with x >= read_delay/BPM/60)
Redis: Redis:
bpm_sample_interval
|........................| key bpm_sample_interval
bpm_delay visual |........................|
|.........................|
key bpm_delay
visual |.........................|
''' '''
bpm = new_bpm bpm = new_bpm
bpm_sample_interval = SAMPLING_FREQUENCY * 1000
bpm_delay = (SAMPLING_FREQUENCY + time.time() - start ) * 1000
pexpireat = int( 2 * bpm_sample_interval);
# Save to Redis # Save to Redis
r.set( 'bpm', new_bpm, px=( 2* int(SAMPLING_FREQUENCY * 1000))) r.set( 'bpm', round(bpm,2), px = pexpireat )
r.set( 'bpm_sample_interval', SAMPLING_FREQUENCY ) r.set( 'bpm_sample_interval', bpm_sample_interval )
r.set( 'bpm_delay', bpm_delay ) r.set( 'bpm_delay', bpm_delay )
r.set( 'beats', json.dumps( beats.tolist() ) ) r.set( 'beats', json.dumps( beats.tolist() ) )
debug( "bpm:{} bpm_delay:{} beats:{}".format(bpm,bpm_delay,beats) ) #debug( "pexpireat:{}".format(pexpireat))
debug( "bpm:{} bpm_delay:{} bpm_sample_interval:{} beats:{}".format(bpm,bpm_delay,bpm_sample_interval,beats) )
return True return True
def m_spectrum(audio_data): def m_spectrum(audio_data):
@ -250,6 +260,7 @@ if MODE == 'spectrum':
elif MODE == 'bpm': elif MODE == 'bpm':
debug("In this mode, we will set keys: onset, bpm, beats") debug("In this mode, we will set keys: onset, bpm, beats")
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paFloat32, stream = p.open(format=pyaudio.paFloat32,
channels=CHANNELS, channels=CHANNELS,
rate=RATE, rate=RATE,