Files
lamp/.agents/skills/upgrading-expo/references/expo-av-to-audio.md
Seven 8963f777ee Add PostCSS configuration and skills lock file
- Created a new PostCSS configuration file to integrate Tailwind CSS.
- Added a skills lock file containing various Expo skills with their respective source and computed hashes.
2026-03-09 06:41:01 +07:00

3.6 KiB

Migrating from expo-av to expo-audio

Imports

// Before
import { Audio } from 'expo-av';

// After
import { useAudioPlayer, useAudioRecorder, RecordingPresets, AudioModule, setAudioModeAsync } from 'expo-audio';

Audio Playback

Before (expo-av)

const [sound, setSound] = useState<Audio.Sound>();

async function playSound() {
  const { sound } = await Audio.Sound.createAsync(require('./audio.mp3'));
  setSound(sound);
  await sound.playAsync();
}

useEffect(() => {
  return sound ? () => { sound.unloadAsync(); } : undefined;
}, [sound]);

After (expo-audio)

const player = useAudioPlayer(require('./audio.mp3'));

// Play
player.play();

Audio Recording

Before (expo-av)

const [recording, setRecording] = useState<Audio.Recording>();

async function startRecording() {
  await Audio.requestPermissionsAsync();
  await Audio.setAudioModeAsync({ allowsRecordingIOS: true, playsInSilentModeIOS: true });
  const { recording } = await Audio.Recording.createAsync(Audio.RecordingOptionsPresets.HIGH_QUALITY);
  setRecording(recording);
}

async function stopRecording() {
  await recording?.stopAndUnloadAsync();
  const uri = recording?.getURI();
}

After (expo-audio)

const recorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY);

async function startRecording() {
  await AudioModule.requestRecordingPermissionsAsync();
  await recorder.prepareToRecordAsync();
  recorder.record();
}

async function stopRecording() {
  await recorder.stop();
  const uri = recorder.uri;
}

Audio Mode

Before (expo-av)

await Audio.setAudioModeAsync({
  allowsRecordingIOS: true,
  playsInSilentModeIOS: true,
  staysActiveInBackground: true,
  interruptionModeIOS: InterruptionModeIOS.DoNotMix,
});

After (expo-audio)

await setAudioModeAsync({
  playsInSilentMode: true,
  shouldPlayInBackground: true,
  interruptionMode: 'doNotMix',
});

API Mapping

expo-av expo-audio
Audio.Sound.createAsync() useAudioPlayer(source)
sound.playAsync() player.play()
sound.pauseAsync() player.pause()
sound.setPositionAsync(ms) player.seekTo(seconds)
sound.setVolumeAsync(vol) player.volume = vol
sound.setRateAsync(rate) player.playbackRate = rate
sound.setIsLoopingAsync(loop) player.loop = loop
sound.unloadAsync() Automatic via hook
playbackStatus.positionMillis player.currentTime (seconds)
playbackStatus.durationMillis player.duration (seconds)
playbackStatus.isPlaying player.playing
Audio.Recording.createAsync() useAudioRecorder(preset)
Audio.RecordingOptionsPresets.* RecordingPresets.*
recording.stopAndUnloadAsync() recorder.stop()
recording.getURI() recorder.uri
Audio.requestPermissionsAsync() AudioModule.requestRecordingPermissionsAsync()

Key Differences

  • No auto-reset on finish: After play() completes, the player stays paused at the end. To replay, call player.seekTo(0) then play()
  • Time in seconds: expo-audio uses seconds, not milliseconds (matching web standards)
  • Immediate loading: Audio loads immediately when the hook mounts—no explicit preloading needed
  • Automatic cleanup: No need to call unloadAsync(), hooks handle resource cleanup on unmount
  • Multiple players: Create multiple useAudioPlayer instances and store them—all load immediately
  • Direct property access: Set volume, rate, loop directly on the player object (player.volume = 0.5)

API Reference

https://docs.expo.dev/versions/latest/sdk/audio/