import streamlit as st import librosa import numpy as np import tempfile from collections import defaultdict from audiorecorder import audiorecorder st.set_page_config(page_title="Bass Guitar Tablature Generator", layout="centered") # Title st.title("🎸 Real-Time Bass Guitar Tab Generator") st.markdown("Upload or record your bass guitar audio and generate tab notation in standard tuning (E1, A1, D2, G2).") # Note mapping (EADG - standard 4-string bass) note_names = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] open_strings_bass = { 4: "E1", 3: "A1", 2: "D2", 1: "G2" } def generate_note_to_fretboard_map_bass(): note_to_positions = defaultdict(list) for string, open_note in open_strings_bass.items(): base_note = open_note[:-1] base_octave = int(open_note[-1]) start_index = note_names.index(base_note) for fret in range(0, 21): note_index = (start_index + fret) % 12 octave_shift = (start_index + fret) // 12 note = note_names[note_index] + str(base_octave + octave_shift) note_to_positions[note].append((string, fret)) return note_to_positions note_to_frets_bass = generate_note_to_fretboard_map_bass() # Audio input uploaded_file = st.file_uploader("Upload a bass audio clip (WAV or MP3)", type=["wav", "mp3"]) audio = audiorecorder("🎧 Click to Record", "⏸ Stop Recording") if audio.tobytes(): with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp: tmp.write(audio.tobytes()) uploaded_file = tmp.name if uploaded_file: st.audio(uploaded_file) # Load and analyze y, sr = librosa.load(uploaded_file) pitches, magnitudes = librosa.piptrack(y=y, sr=sr) notes_detected = [] for i in range(pitches.shape[1]): index = magnitudes[:, i].argmax() pitch = pitches[index, i] if pitch > 0: note_index = int(np.round(12 * np.log2(pitch / 440.0) + 69)) note_name = note_names[note_index % 12] + str((note_index // 12) - 1) if note_name not in notes_detected: notes_detected.append(note_name) # Tablature rendering tab_lines = {1: "G|", 2: "D|", 3: "A|", 4: "E|"} for note in notes_detected: positions = note_to_frets_bass.get(note, []) if not positions: continue string, fret = sorted(positions, key=lambda x: x[1])[0] # pick lowest fret for s in tab_lines: if s == string: tab_lines[s] += f"--{fret:2}" # spacing for 2-digit else: tab_lines[s] += "----" st.subheader("🎼 Generated Tablature:") st.code("\n".join(tab_lines[s] for s in sorted(tab_lines)), language="text")