Fix extra button sound playback and add status indicator
**Button Sound Playback Fix:** - Use separate PyAudio instance for button sound during recording - Allows simultaneous audio input (recording) and output (button sound) - Button sound plays through speaker/handset for guest to hear - Button sound is NOT captured in the recording file - Only works during recording phase (not during greeting) **Button Status Indicator:** - Added button status display in Phone Status card - Shows "Button ready" or "Button sound playing..." - Yellow highlight when button sound is playing - Status updates dynamically via refresh - Only visible when extra button is enabled **Technical Details:** - Separate audio_playback instance prevents stream conflicts - Recording uses input stream, button uses output stream - Both operate on same HiFiBerry device simultaneously - Status API now includes extra_button_playing flag 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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] }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if extra_button_enabled %}
|
||||
<div id="button-status" style="margin-top: 10px; padding: 8px; border-radius: 5px; background: {{ '#fef3c7' if status.extra_button_playing else '#f3f4f6' }}; transition: background 0.3s;">
|
||||
<span style="font-size: 0.9em;">
|
||||
{% if status.extra_button_playing %}
|
||||
🔘 Button sound playing...
|
||||
{% else %}
|
||||
🔘 Button ready
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button class="btn btn-secondary" onclick="refreshStatus()">🔄 Refresh</button>
|
||||
</div>
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user