tuner code
This commit is contained in:
parent
824c752c51
commit
839411b1fd
85
code/tuner/main.py
Normal file
85
code/tuner/main.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import string
|
||||||
|
from typing import Any
|
||||||
|
from scipy.io import wavfile as wav
|
||||||
|
import numpy as np
|
||||||
|
import sounddevice as sd
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print("Tuner exercise")
|
||||||
|
SAMPLE_RATE = 44100
|
||||||
|
CHUNK = 2048
|
||||||
|
QUIET_THRESHOLD = 0.01
|
||||||
|
A4 = 440
|
||||||
|
DEBUG = True
|
||||||
|
NOTES = "A B♭/A♯ B C D♭/C♯ D E♭/D♯ E F F♯/G♭ G A♭/G♯".split()
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_note(frequency):
|
||||||
|
if frequency > 0:
|
||||||
|
note_num = round(math.log2(frequency / 440) * 12)
|
||||||
|
octave = math.floor(4 + note_num / 12)
|
||||||
|
rel_note = note_num % 12
|
||||||
|
note = NOTES[rel_note]
|
||||||
|
return f"{note}{octave}"
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def dominant_frequency(data, samplerate):
|
||||||
|
windowed_data = data * np.hanning(len(data))
|
||||||
|
fft_result = np.fft.rfft(windowed_data)
|
||||||
|
fft_magnitude = np.abs(fft_result)
|
||||||
|
freqs = np.fft.rfftfreq(len(windowed_data), 1 / samplerate)
|
||||||
|
peak_idx = np.argmax(fft_magnitude)
|
||||||
|
dominant_freq = freqs[peak_idx]
|
||||||
|
return dominant_freq
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_rms(data):
|
||||||
|
return np.sqrt(np.mean(data**2))
|
||||||
|
|
||||||
|
|
||||||
|
def audio_callback(indata: np.ndarray, frames: int, time: Any, status: Any):
|
||||||
|
if status:
|
||||||
|
print(f"Status: {status}")
|
||||||
|
rms = calculate_rms(indata)
|
||||||
|
|
||||||
|
if rms > QUIET_THRESHOLD:
|
||||||
|
mono_data = np.mean(indata, axis=1)
|
||||||
|
frequency = dominant_frequency(mono_data, SAMPLE_RATE)
|
||||||
|
out = f"\r{calculate_note(frequency)}"
|
||||||
|
|
||||||
|
if DEBUG: out += f" Dominant Frequency: {frequency:.2f} Hz (RMS: {rms:.3f})"
|
||||||
|
sys.stdout.write(out)
|
||||||
|
sys.stdout.flush()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
print(sd.query_devices())
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
stream = sd.InputStream(
|
||||||
|
device=15,
|
||||||
|
callback=audio_callback,
|
||||||
|
samplerate=SAMPLE_RATE,
|
||||||
|
channels=1,
|
||||||
|
blocksize=CHUNK,
|
||||||
|
)
|
||||||
|
stream.start()
|
||||||
|
if DEBUG:
|
||||||
|
print("channels? ", stream.channels)
|
||||||
|
print("device? ", stream.device)
|
||||||
|
print("active? ", stream.active)
|
||||||
|
while True:
|
||||||
|
pass
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Audio recording stopped.")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
print("Stream closed.")
|
||||||
|
|
||||||
|
# https://python-sounddevice.readthedocs.io/en/0.5.1/examples.html#plot-microphone-signal-s-in-real-time
|
||||||
|
# https://github.com/mzucker/python-tuner/blob/master/tuner.py
|
Reference in New Issue
Block a user