import gradio as gr import numpy as np from scipy import signal import soundfile as sf import matplotlib.pyplot as plt import io import time from datetime import datetime def generate_test_tone(frequency, duration=1.0, sample_rate=44100): t = np.linspace(0, duration, int(sample_rate * duration)) tone = np.sin(2 * np.pi * frequency * t) return tone * np.hanning(len(tone)) def hearing_test(frequency, volume, ear_selection): sample_rate = 44100 tone = generate_test_tone(float(frequency), 1.0, sample_rate) volume_adjusted = tone * (10 ** (volume / 20)) stereo_tone = np.zeros((2, len(tone))) if ear_selection == "Left": stereo_tone[0] = volume_adjusted elif ear_selection == "Right": stereo_tone[1] = volume_adjusted else: stereo_tone[0] = stereo_tone[1] = volume_adjusted output_path = f"test_tone_{frequency}Hz.wav" sf.write(output_path, stereo_tone.T, sample_rate) return output_path def create_audiogram(left_ear_results, right_ear_results): frequencies = [250, 500, 1000, 2000, 4000, 8000] plt.figure(figsize=(10, 8)) plt.fill_between([125, 8000], -10, 25, color='#e6f3ff', alpha=0.3, label='Normal Hearing') plt.fill_between([125, 8000], 25, 40, color='#b3d9ff', alpha=0.3, label='Mild Loss') plt.fill_between([125, 8000], 40, 55, color='#80bfff', alpha=0.3, label='Moderate Loss') plt.fill_between([125, 8000], 55, 70, color='#4da6ff', alpha=0.3, label='Moderate-Severe Loss') plt.plot(frequencies, left_ear_results, 'x-', color='blue', label='Left Ear') plt.plot(frequencies, right_ear_results, 'o-', color='red', label='Right Ear') plt.xscale('log') plt.xlim(125, 8000) plt.ylim(70, -10) plt.grid(True) plt.xlabel('Frequency (Hz)') plt.ylabel('Hearing Level (dB)') plt.title('Audiogram Results') plt.legend() buf = io.BytesIO() plt.savefig(buf, format='png') plt.close() return buf def generate_audio(duration, selected_frequencies): sample_rate = 44100 num_samples = int(float(duration) * sample_rate) noise = np.random.normal(0, 1, num_samples) if selected_frequencies: frequencies = [int(f) for f in selected_frequencies] for freq in frequencies: depth = -40 if freq == 4000 else -30 width = freq / 10 nyquist = sample_rate / 2 freq_normalized = freq / nyquist quality_factor = freq / width b, a = signal.iirnotch(freq_normalized, quality_factor) noise = signal.filtfilt(b, a, noise) noise *= 10 ** (depth / 20) noise = noise / np.max(np.abs(noise)) output_path = "notched_noise.wav" sf.write(output_path, noise, sample_rate) return output_path class HRVMonitor: def __init__(self): self.recording = False self.start_time = None self.data = [] def start_recording(self): self.recording = True self.start_time = time.time() self.data = [] return "Recording started..." def stop_recording(self): self.recording = False return "Recording stopped." def update_display(self): if not self.recording: return None current_time = time.time() - self.start_time base_rr = 1000 # Base RR interval (ms) rr_intervals = base_rr + np.random.normal(0, 50, 10) # Add variability # Calculate HRV metrics rmssd = np.sqrt(np.mean(np.diff(rr_intervals) ** 2)) sdnn = np.std(rr_intervals) lf_power = np.random.uniform(70, 85) # Simulated LF power hf_power = np.random.uniform(15, 30) # Simulated HF power lf_hf_ratio = lf_power / hf_power hrv_score = min(100, max(1, 50 + (rmssd - 30) / 2)) metrics = { 'time': current_time, 'score': round(hrv_score), 'rr': round(np.mean(rr_intervals)), 'rmssd': round(rmssd), 'sdnn': round(sdnn), 'lf': round(lf_power), 'hf': round(hf_power), 'lf_hf': round(lf_hf_ratio, 1) } self.data.append(metrics) return f""" HRV Score: {metrics['score']} RR: {metrics['rr']} ms RMSSD: {metrics['rmssd']} ms SDNN: {metrics['sdnn']} ms LF: {metrics['lf']}% HF: {metrics['hf']}% LF/HF: {metrics['lf_hf']} Recording time: {round(current_time)}s """ def refresh_hrv(hrv_monitor): return hrv_monitor.update_display() if hrv_monitor.recording else "Click Start to begin monitoring..." def create_interface(): hrv_monitor = HRVMonitor() with gr.Blocks(title="Hearing Test & HRV Monitor") as app: with gr.Tabs(): # Hearing Test Tab with gr.Tab("Hearing Test"): gr.Markdown("## Hearing Test") with gr.Row(): with gr.Column(): frequency = gr.Dropdown( choices=["250", "500", "1000", "2000", "4000", "8000"], value="1000", label="Test Frequency (Hz)" ) volume = gr.Slider( minimum=-60, maximum=0, value=-20, step=5, label="Volume (dB)" ) ear_select = gr.Radio( choices=["Both", "Left", "Right"], value="Both", label="Ear Selection" ) test_btn = gr.Button("Play Test Tone") with gr.Column(): audio_output = gr.Audio(label="Test Tone") with gr.Row(): with gr.Column(): left_thresholds = [gr.Number(value=0, label=f"{freq}Hz Left") for freq in [250, 500, 1000, 2000, 4000, 8000]] with gr.Column(): right_thresholds = [gr.Number(value=0, label=f"{freq}Hz Right") for freq in [250, 500, 1000, 2000, 4000, 8000]] generate_audiogram_btn = gr.Button("Generate Audiogram") audiogram_output = gr.Image(label="Audiogram") # White Noise Tab with gr.Tab("White Noise Generator"): gr.Markdown("## Notched White Noise Generator") with gr.Row(): with gr.Column(): duration = gr.Slider( minimum=1, maximum=30, value=5, step=1, label="Duration (seconds)" ) frequencies = gr.CheckboxGroup( choices=["250", "500", "1000", "2000", "4000", "8000"], label="Frequencies to Notch (Hz)", value=["4000", "2000"] ) generate_noise_btn = gr.Button("Generate Noise") with gr.Column(): noise_output = gr.Audio(label="Generated Noise") # HRV Monitor Tab with gr.Tab("HRV Monitor"): with gr.Row(): with gr.Column(): start_btn = gr.Button("Start Recording") stop_btn = gr.Button("Stop Recording") hrv_display = gr.Textbox( label="HRV Metrics", value="Click Start to begin monitoring...", lines=10, interactive=False ) gr.Markdown(""" ### Metrics Explanation: - **HRV Score**: Overall heart rate variability (1-100) - **RR**: Average time between heartbeats (ms) - **RMSSD**: Root Mean Square of Successive Differences - **SDNN**: Standard Deviation of NN intervals - **LF/HF**: Balance between sympathetic and parasympathetic activity """) # Event handlers for hearing test test_btn.click( fn=hearing_test, inputs=[frequency, volume, ear_select], outputs=audio_output ) generate_audiogram_btn.click( fn=lambda *args: create_audiogram(args[:6], args[6:]).getvalue(), inputs=left_thresholds + right_thresholds, outputs=audiogram_output ) # Event handler for noise generator generate_noise_btn.click( fn=generate_audio, inputs=[duration, frequencies], outputs=noise_output ) # Event handlers for HRV monitor start_btn.click( fn=hrv_monitor.start_recording, outputs=hrv_display ) stop_btn.click( fn=hrv_monitor.stop_recording, outputs=hrv_display ) # Auto-refresh HRV display hrv_display.change( fn=lambda: refresh_hrv(hrv_monitor), inputs=None, outputs=hrv_display, every=1 ) return app if __name__ == "__main__": app = create_interface() app.launch(share=False)