Audio Devices

Manage microphones, speakers, and audio settings in your PTT application.

Built-in Audio Player

The SDK includes a built-in audio player that automatically handles playback of incoming broadcasts — no manual audio setup required. Broadcasts are queued and played in order. The controls below let you customize which devices are used and adjust volume.

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();
  }
}