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:
2025-10-24 15:45:30 +07:00
parent 4d608555cc
commit 9ea6a5e3a6

View File

@@ -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) {