Audio Devices
Manage microphones, speakers, and audio settings in your PTT application.
Listing Available Devices
Get lists of available audio input (microphones) and output (speakers) devices:
TypeScript
// Get all microphones
const microphones = await talker.getAudioInputDevices();
console.log('Available microphones:', microphones);
// Get all speakers
const speakers = await talker.getAudioOutputDevices();
console.log('Available speakers:', speakers);
// Each device is a MediaDeviceInfo object:
// {
// deviceId: string,
// label: string,
// kind: 'audioinput' | 'audiooutput',
// groupId: string
// }
Permission Required
Device labels are only available after the user has granted microphone permission. Before permission is granted, label will be empty.
Selecting Devices
Select Microphone
TypeScript
const microphones = await talker.getAudioInputDevices();
// Select a specific microphone
await talker.setAudioInputDevice(microphones[1].deviceId);
// The change takes effect immediately for any active or future recordings
Select Speaker
TypeScript
const speakers = await talker.getAudioOutputDevices();
// Select a specific speaker
await talker.setAudioOutputDevice(speakers[0].deviceId);
// Audio playback will now use the selected speaker
Building a Device Picker UI
TypeScript
async function populateDeviceSelectors() {
const micSelect = document.getElementById('mic-select') as HTMLSelectElement;
const speakerSelect = document.getElementById('speaker-select') as HTMLSelectElement;
// Get devices
const microphones = await talker.getAudioInputDevices();
const speakers = await talker.getAudioOutputDevices();
// Populate microphone dropdown
micSelect.innerHTML = '';
microphones.forEach(device => {
const option = document.createElement('option');
option.value = device.deviceId;
option.textContent = device.label || `Microphone ${micSelect.options.length + 1}`;
micSelect.appendChild(option);
});
// Populate speaker dropdown
speakerSelect.innerHTML = '';
speakers.forEach(device => {
const option = document.createElement('option');
option.value = device.deviceId;
option.textContent = device.label || `Speaker ${speakerSelect.options.length + 1}`;
speakerSelect.appendChild(option);
});
// Handle selection changes
micSelect.addEventListener('change', async () => {
await talker.setAudioInputDevice(micSelect.value);
});
speakerSelect.addEventListener('change', async () => {
await talker.setAudioOutputDevice(speakerSelect.value);
});
}
Volume Control
TypeScript
// Set volume (0 to 1)
talker.setVolume(0.8);
// Volume slider example
const volumeSlider = document.getElementById('volume') as HTMLInputElement;
volumeSlider.addEventListener('input', () => {
const volume = parseFloat(volumeSlider.value);
talker.setVolume(volume);
});
Muting Audio
Mute Speaker
TypeScript
// Mute incoming audio
talker.setSpeakerEnabled(false);
// Unmute
talker.setSpeakerEnabled(true);
// Toggle mute button
let speakerMuted = false;
muteButton.addEventListener('click', () => {
speakerMuted = !speakerMuted;
talker.setSpeakerEnabled(!speakerMuted);
muteButton.textContent = speakerMuted ? 'Unmute' : 'Mute';
});
Mute Microphone
TypeScript
// Disable microphone
talker.setMicrophoneEnabled(false);
// Re-enable microphone
talker.setMicrophoneEnabled(true);
Monitoring Audio Levels
Display real-time microphone input levels:
TypeScript
function startAudioMeter() {
const meter = document.getElementById('audio-meter');
function update() {
// Returns 0-1
const level = talker.getAudioLevel();
// Update visual meter
meter.style.width = `${level * 100}%`;
// Color based on level
if (level > 0.8) {
meter.style.backgroundColor = '#ef4444'; // Red - too loud
} else if (level > 0.1) {
meter.style.backgroundColor = '#10b981'; // Green - good
} else {
meter.style.backgroundColor = '#64748b'; // Gray - quiet
}
requestAnimationFrame(update);
}
update();
}
Handling Device Changes
Detect when devices are connected or disconnected:
TypeScript
// Listen for device changes
navigator.mediaDevices.addEventListener('devicechange', async () => {
console.log('Audio devices changed');
// Refresh device lists
await populateDeviceSelectors();
// Optionally notify user
showNotification('Audio devices updated');
});
Requesting Permissions
Request microphone permission before PTT:
TypeScript
async function requestMicrophonePermission(): Promise {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// Stop the stream immediately - we just needed permission
stream.getTracks().forEach(track => track.stop());
return true;
} catch (error) {
if (error.name === 'NotAllowedError') {
showError('Microphone permission denied. Please enable it in your browser settings.');
} else if (error.name === 'NotFoundError') {
showError('No microphone found. Please connect a microphone.');
} else {
showError('Failed to access microphone: ' + error.message);
}
return false;
}
}
// Request permission on page load or button click
async function init() {
const hasPermission = await requestMicrophonePermission();
if (hasPermission) {
// Now device labels will be available
await populateDeviceSelectors();
}
}
Complete Settings Panel
HTML
<div class="audio-settings">
<h3>Audio Settings</h3>
<div class="setting-group">
<label for="mic-select">Microphone</label>
<select id="mic-select"></select>
</div>
<div class="setting-group">
<label for="speaker-select">Speaker</label>
<select id="speaker-select"></select>
</div>
<div class="setting-group">
<label for="volume">Volume</label>
<input type="range" id="volume" min="0" max="1" step="0.1" value="1">
</div>
<div class="setting-group">
<label>Input Level</label>
<div class="meter-container">
<div id="audio-meter"></div>
</div>
</div>
<button id="test-mic">Test Microphone</button>
</div>
TypeScript
class AudioSettings {
constructor(private talker: TalkerClient) {
this.init();
}
private async init() {
await this.populateDevices();
this.setupEventListeners();
this.startMeter();
}
private async populateDevices() {
const micSelect = document.getElementById('mic-select') as HTMLSelectElement;
const speakerSelect = document.getElementById('speaker-select') as HTMLSelectElement;
const [microphones, speakers] = await Promise.all([
this.talker.getAudioInputDevices(),
this.talker.getAudioOutputDevices(),
]);
micSelect.innerHTML = microphones.map(d =>
`<option value="${d.deviceId}">${d.label || 'Microphone'}</option>`
).join('');
speakerSelect.innerHTML = speakers.map(d =>
`<option value="${d.deviceId}">${d.label || 'Speaker'}</option>`
).join('');
}
private setupEventListeners() {
document.getElementById('mic-select')!.addEventListener('change', (e) => {
this.talker.setAudioInputDevice((e.target as HTMLSelectElement).value);
});
document.getElementById('speaker-select')!.addEventListener('change', (e) => {
this.talker.setAudioOutputDevice((e.target as HTMLSelectElement).value);
});
document.getElementById('volume')!.addEventListener('input', (e) => {
this.talker.setVolume(parseFloat((e.target as HTMLInputElement).value));
});
navigator.mediaDevices.addEventListener('devicechange', () => {
this.populateDevices();
});
}
private startMeter() {
const meter = document.getElementById('audio-meter')!;
const update = () => {
const level = this.talker.getAudioLevel();
meter.style.width = `${level * 100}%`;
requestAnimationFrame(update);
};
update();
}
}