diff --git a/rotary_phone_web.py b/rotary_phone_web.py index 7a5f301..1eb4377 100644 --- a/rotary_phone_web.py +++ b/rotary_phone_web.py @@ -315,7 +315,8 @@ class RotaryPhone: return { "status": self.phone_status, "recording": self.recording, - "current_recording": self.current_recording + "current_recording": self.current_recording, + "extra_button_playing": self.extra_button_playing if EXTRA_BUTTON_ENABLED else False } def play_extra_button_sound(self): @@ -335,9 +336,49 @@ class RotaryPhone: print(f"\n=== Extra button pressed ===") self.extra_button_playing = True - # Play without checking hook status - play entire sound - self.play_sound_file(button_sound, check_hook_status=False) - self.extra_button_playing = False + + try: + # Use a separate PyAudio instance for playback during recording + # This allows simultaneous input (recording) and output (button sound) + audio_playback = pyaudio.PyAudio() + + wf = wave.open(button_sound, 'rb') + + stream = audio_playback.open( + format=audio_playback.get_format_from_width(wf.getsampwidth()), + channels=wf.getnchannels(), + rate=wf.getframerate(), + output=True, + output_device_index=AUDIO_DEVICE_INDEX, + frames_per_buffer=CHUNK + ) + + # Get volume multiplier + volume = self.get_volume() / 100.0 + + # Play the sound + data = wf.readframes(CHUNK) + while data: + # Apply volume + if volume < 1.0: + audio_data = np.frombuffer(data, dtype=np.int16) + audio_data = (audio_data * volume).astype(np.int16) + data = audio_data.tobytes() + + stream.write(data) + data = wf.readframes(CHUNK) + + stream.stop_stream() + stream.close() + wf.close() + audio_playback.terminate() + + print("Extra button sound playback finished") + + except Exception as e: + print(f"Error playing button sound: {e}") + finally: + self.extra_button_playing = False def phone_loop(self): """Main phone handling loop""" @@ -1090,6 +1131,17 @@ if __name__ == "__main__": Recording to: {{ status.current_recording.split('/')[-1] }}
{% endif %} + {% if extra_button_enabled %} + + {% endif %} @@ -1532,8 +1584,34 @@ if __name__ == "__main__": fetch('/api/status') .then(response => response.json()) .then(data => { - location.reload(); - }); + // Update status text + const statusText = document.getElementById('status-text'); + const statusDot = document.getElementById('status-dot'); + + if (data.recording) { + statusText.textContent = '🔴 Recording in progress...'; + statusDot.className = 'status-dot recording'; + } else if (data.status === 'off_hook') { + statusText.textContent = '📞 Handset off hook'; + statusDot.className = 'status-dot off-hook'; + } else { + statusText.textContent = '✅ Ready (handset on hook)'; + statusDot.className = 'status-dot on-hook'; + } + + // Update extra button status + const buttonStatus = document.getElementById('button-status'); + if (buttonStatus) { + if (data.extra_button_playing) { + buttonStatus.style.background = '#fef3c7'; + buttonStatus.querySelector('span').textContent = '🔘 Button sound playing...'; + } else { + buttonStatus.style.background = '#f3f4f6'; + buttonStatus.querySelector('span').textContent = '🔘 Button ready'; + } + } + }) + .catch(error => console.error('Error refreshing status:', error)); } function showAlert(message, type) {