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() # Get the note and octave using known note forumla 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 # Attempt to get the dominant frequency by windowing our data sample, and taking an FFT # In theory, the highest magnitude frequency of the FFT result should reflect what the human is trying to play in the signal 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) # Ignore low noise to prevent spammy-ness and keep last audible result shown 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.")