This commit is contained in:
Martin Bauer 2021-12-24 00:12:43 +01:00
parent 9a68619293
commit 823552ce7f
4 changed files with 169 additions and 8 deletions

View File

@ -0,0 +1,129 @@
from typing import overload
import librosa
from numba import jit
import numpy as np
@jit(nopython=True)
def normalize_feature_sequence(X, norm='2', threshold=0.0001, v=None):
"""Normalizes the columns of a feature sequence
Notebook: C3/C3S1_FeatureNormalization.ipynb
Args:
X (np.ndarray): Feature sequence
norm (str): The norm to be applied. '1', '2', 'max' or 'z' (Default value = '2')
threshold (float): An threshold below which the vector ``v`` used instead of normalization
(Default value = 0.0001)
v (float): Used instead of normalization below ``threshold``. If None, uses unit vector for given norm
(Default value = None)
Returns:
X_norm (np.ndarray): Normalized feature sequence
"""
assert norm in ['1', '2', 'max', 'z']
K, N = X.shape
X_norm = np.zeros((K, N))
if norm == '1':
if v is None:
v = np.ones(K, dtype=np.float64) / K
for n in range(N):
s = np.sum(np.abs(X[:, n]))
if s > threshold:
X_norm[:, n] = X[:, n] / s
else:
X_norm[:, n] = v
if norm == '2':
if v is None:
v = np.ones(K, dtype=np.float64) / np.sqrt(K)
for n in range(N):
s = np.sqrt(np.sum(X[:, n]**2))
if s > threshold:
X_norm[:, n] = X[:, n] / s
else:
X_norm[:, n] = v
if norm == 'max':
if v is None:
v = np.ones(K, dtype=np.float64)
for n in range(N):
s = np.max(np.abs(X[:, n]))
if s > threshold:
X_norm[:, n] = X[:, n] / s
else:
X_norm[:, n] = v
if norm == 'z':
if v is None:
v = np.zeros(K, dtype=np.float64)
for n in range(N):
mu = np.sum(X[:, n]) / K
sigma = np.sqrt(np.sum((X[:, n] - mu)**2) / (K - 1))
if sigma > threshold:
X_norm[:, n] = (X[:, n] - mu) / sigma
else:
X_norm[:, n] = v
return X_norm
def compute_chromagram_from_filename(fn_wav,
Fs=22050,
N=4096,
H=2048,
gamma=None,
version='STFT',
norm='2'):
"""Compute chromagram for WAV file specified by filename
Notebook: C5/C5S2_ChordRec_Templates.ipynb
Args:
fn_wav (str): Filenname of WAV
Fs (scalar): Sampling rate (Default value = 22050)
N (int): Window size (Default value = 4096)
H (int): Hop size (Default value = 2048)
gamma (float): Constant for logarithmic compression (Default value = None)
version (str): Technique used for front-end decomposition ('STFT', 'IIS', 'CQT') (Default value = 'STFT')
norm (str): If not 'None', chroma vectors are normalized by norm as specified ('1', '2', 'max')
(Default value = '2')
Returns:
X (np.ndarray): Chromagram
Fs_X (scalar): Feature reate of chromagram
x (np.ndarray): Audio signal
Fs (scalar): Sampling rate of audio signal
x_dur (float): Duration (seconds) of audio signal
"""
x, Fs = librosa.load(fn_wav, sr=Fs)
x_dur = x.shape[0] / Fs
if version == 'STFT':
# Compute chroma features with STFT
X = librosa.stft(x, n_fft=N, hop_length=H, pad_mode='constant', center=True)
if gamma is not None:
X = np.log(1 + gamma * np.abs(X)**2)
else:
X = np.abs(X)**2
X = librosa.feature.chroma_stft(S=X, sr=Fs, tuning=0, norm=None, hop_length=H, n_fft=N)
if version == 'CQT':
# Compute chroma features with CQT decomposition
X = librosa.feature.chroma_cqt(y=x, sr=Fs, hop_length=H, norm=None)
if version == 'IIR':
# Compute chroma features with filter bank (using IIR elliptic filter)
X = librosa.iirt(y=x, sr=Fs, win_length=N, hop_length=H, center=True, tuning=0.0)
if gamma is not None:
X = np.log(1.0 + gamma * X)
X = librosa.feature.chroma_cqt(C=X,
bins_per_octave=12,
n_octaves=7,
fmin=librosa.midi_to_hz(24),
norm=None)
if norm is not None:
X = normalize_feature_sequence(X, norm='2')
Fs_X = Fs / H
return X, Fs_X, x, Fs, x_dur
if __name__ == "__main__":
test_file = "/home/martin/Music/deemix Music/Simone Sommerland - Ki-Ka-Kinderturnen.mp3"
chroma = compute_chromagram_from_filename(test_file)

View File

@ -8,7 +8,10 @@ from player import AudioPlayer
from glob import glob
from copy import deepcopy
import os
from hass_client import HomeAssistantClient
HASS_URL = "https://ha.bauer.tech"
HASS_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI3YmY1OTc3NWJhZGI0MzFkOTBiNDBkZDA3OGQ1MTMxNSIsImlhdCI6MTY0MDAzNzI3MSwiZXhwIjoxOTU1Mzk3MjcxfQ.c7RPRP_hxzIwd3xcFTNLz94rOjLQDR0elH8RE-jCDgc"
MUSIC_FOLDER = "/home/martin/code/musicmouse/espmusicmouse/host_driver/music"
audio_player = AudioPlayer()
@ -96,6 +99,7 @@ def on_rfid(protocol, tagid):
ring_eff = EffectSwipeAndChange()
ring_eff.swipe.primary_color = ccfg.primary
ring_eff.swipe.secondary_color = ccfg.secondary
ring_eff.swipe.swipe_speed = 180
ring_eff.change.color1 = ccfg.primary
ring_eff.change.color2 = ccfg.secondary
protocol.led_ring_effect(ring_eff)
@ -104,6 +108,7 @@ def on_rfid(protocol, tagid):
mouse_eff = deepcopy(ring_eff)
mouse_eff.swipe.start_position = 6 / 45 * 360
mouse_eff.swipe.bell_curve_width_in_leds = 16
mouse_eff.swipe.swipe_speed = 180
protocol.mouse_led_effect(mouse_eff)
protocol.button_background_led_prev(0.3)
@ -132,12 +137,23 @@ def on_firmware_msg(protocol: MusicMouseProtocol, message):
audio_player.previous()
elif message.button == "right" and message.event == "pressed":
audio_player.next()
elif isinstance(message, TouchButtonPress):
ccfg = color_cfg[current_figure]
protocol.mouse_led_effect(EffectStaticConfig(ccfg.accent,
*mouse_leds[message.touch_button]))
elif isinstance(message, TouchButtonRelease):
ccfg = color_cfg[current_figure]
elif False and isinstance(message, TouchButtonPress):
if current_figure:
ccfg = color_cfg[current_figure]
protocol.mouse_led_effect(
EffectStaticConfig(ccfg.accent, *mouse_leds[message.touch_button]))
#if message.touch_button == TouchButton.RIGHT_FOOT:
# asyncio.create_task(
# hass.call_service("switch", "toggle", {"entity_id": "switch.tasmota06"}))
# asyncio.create_task(
# hass.call_service("light", "turn_on", {"entity_id": "light.arbeitszimmer_fluter"}))
#elif message.touch_button == TouchButton.LEFT_FOOT:
# asyncio.create_task(
# hass.call_service("light", "turn_off", {"entity_id": "light.arbeitszimmer_fluter"}))
elif False and isinstance(message, TouchButtonRelease):
if current_figure:
ccfg = color_cfg[current_figure]
eff_change = EffectRandomTwoColorInterpolationConfig()
eff_static = EffectStaticConfig(ColorRGBW(0, 0, 0, 0), *mouse_leds[message.touch_button])
if audio_player.is_playing():
@ -152,6 +168,7 @@ def on_firmware_msg(protocol: MusicMouseProtocol, message):
loop = asyncio.get_event_loop()
hass = HomeAssistantClient(HASS_URL, HASS_TOKEN, loop)
coro = serial_asyncio.create_serial_connection(loop,
MusicMouseProtocol,
'/dev/ttyUSB0',
@ -161,5 +178,6 @@ protocol.register_message_callback(on_firmware_msg)
audio_player.on_playlist_end_callback = lambda: on_music_end_callback(protocol)
loop.create_task(hass.connect())
loop.run_forever()
loop.close()

View File

@ -12,7 +12,7 @@
#include <cstring>
static constexpr int MAX_EFFECT_CONFIG_SIZE = 128;
static constexpr int MAX_EFFECT_CLASS_SIZE = 4 * 1024;
static constexpr int MAX_EFFECT_CLASS_SIZE = 8 * 1024;
template <typename TLedStrip>
class LedTask

View File

@ -129,7 +129,7 @@ LedTask<decltype(ledStripCircle)> ledTaskCircle;
void setupLedCircle()
{
ledDriverCircle.begin(23, 0);
ledDriverCircle.begin(22, 0);
ledTaskCircle.begin(ledStripCircle, ledDriverCircle);
ledTaskCircle.startEffect(EffectStaticConfig(ColorRGBW{0, 0, 0, 0}));
}
@ -147,6 +147,19 @@ void setupMouseLeds()
ledTaskMouse.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}, 0, 0});
}
// -------------------------------------------------- Shelf Leds -------------------------------------------
LedStripRGBW<250> ledStripShelf;
Esp32DriverRGBW ledDriverShelf;
LedTask<decltype(ledStripShelf)> ledTaskShelf;
void setupShelfLeds()
{
ledDriverShelf.begin(17, 2);
ledTaskShelf.begin(ledStripShelf, ledDriverShelf);
ledTaskShelf.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 30}, 0, 0});
}
// -------------------------------------------------- Touch Buttons ----------------------------------------
constexpr auto TOUCH_PAD_RIGHT_EAR = TOUCH_PAD_NUM0;
@ -205,6 +218,7 @@ void setup()
setupButtons();
setupTouchButtons();
setupMouseLeds();
setupShelfLeds();
}
void loop()