From 839411b1fddceb5f730e7fcf5114dd29f2b54b9d Mon Sep 17 00:00:00 2001 From: David Westgate Date: Fri, 15 Nov 2024 15:50:44 -0800 Subject: [PATCH] tuner code --- code/tuner/main.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 code/tuner/main.py diff --git a/code/tuner/main.py b/code/tuner/main.py new file mode 100644 index 0000000..567a72e --- /dev/null +++ b/code/tuner/main.py @@ -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