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 {
|
return {
|
||||||
"status": self.phone_status,
|
"status": self.phone_status,
|
||||||
"recording": self.recording,
|
"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):
|
def play_extra_button_sound(self):
|
||||||
@@ -335,9 +336,49 @@ class RotaryPhone:
|
|||||||
|
|
||||||
print(f"\n=== Extra button pressed ===")
|
print(f"\n=== Extra button pressed ===")
|
||||||
self.extra_button_playing = True
|
self.extra_button_playing = True
|
||||||
# Play without checking hook status - play entire sound
|
|
||||||
self.play_sound_file(button_sound, check_hook_status=False)
|
try:
|
||||||
self.extra_button_playing = False
|
# 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):
|
def phone_loop(self):
|
||||||
"""Main phone handling loop"""
|
"""Main phone handling loop"""
|
||||||
@@ -1090,6 +1131,17 @@ if __name__ == "__main__":
|
|||||||
Recording to: {{ status.current_recording.split('/')[-1] }}
|
Recording to: {{ status.current_recording.split('/')[-1] }}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% 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>
|
</div>
|
||||||
<button class="btn btn-secondary" onclick="refreshStatus()">🔄 Refresh</button>
|
<button class="btn btn-secondary" onclick="refreshStatus()">🔄 Refresh</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1532,8 +1584,34 @@ if __name__ == "__main__":
|
|||||||
fetch('/api/status')
|
fetch('/api/status')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.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) {
|
function showAlert(message, type) {
|
||||||
|
|||||||
Reference in New Issue
Block a user