Dynamically loading sound file for use with expo-av

Please provide the following:

  1. SDK Version: 40
  2. Platforms(Android/iOS/web/all): all
  3. Add the appropriate “Tag” based on what Expo library you have a question on.

I’m using the sample expo-av code from this page to play sound files: Audio - Expo Documentation

It works fine when I hard-code a filename like this:

const { sound } = await Audio.Sound.createAsync(
       require('./assets/Hello.mp3')
    );

But the call to require will not accept variables as arguments – it wants a fixed filename only, so this fails:

const { sound } = await Audio.Sound.createAsync(
       const filename = `./assets/${variable}.mp3`
       require(filename)
    );

The error is some variation of: Invalid call at line 50: require(filename)

My app is testing players with flashcards and has hundreds of sounds to choose from, so the filepaths need to be calculated. It appears the expo-av library has different ways of creating a sound object, but all involve the Asset class, and all the docs I’ve found so far only show using Asset with ‘require’ – I haven’t found any explanation of how to feed it a generated filepath.

the reason for this is that imports are static in metro bundler - you can’t dynamically construct imports based on runtime code. we should probably call this out clearly in our tutorial.

this section of reactnative.dev explains it also: Images · React Native

Thanks, I sorta gathered that, but expo-av offers another option for loading the sound file from URI, and I saw an example of passing in a file:// URI, but can such a parameter access files in my source code?

Thanks – what is your recommendation for how to handle an app that deals with several hundred sounds that need to be selected and played programmatically? Do I load them all with Audio.Sound.createAsync(require(‘filename’)) and save them in a way I can reference them when I need them? Is there a performance issue to having that many instantiated Sound objects always in memory? Or since sound files need to be released after each play (with unloadAsync() ) anyway, do I just build a dictionary to enable grabbing the files dynamically, like {x: require(‘X’), y: require(‘Y’)} and just createAsync them on demand? Since I’m unclear on the implementation of the Sound library and its memory usage, it would be helpful to know some best practices around this.

  1. you could upload them to some hosting service in advance and then refer to them by url in the same way you tried to do with string interpolation above
  2. you could create a file that lists all of the assets like this:
// Sounds.js
export default {
  Hello: require('./assets/Hello.mp3'),
  GoodBye: require('./assets/GoodBye.mp3'),
}

// Some other file
import Sounds from './Sounds';

Audio.Sound.createAsync(Sounds.Hello);
Audio.sound.createAsync(Sounds['Hello']); // if you prefer
  1. you could write some node script to generate the Sounds.js file above for you - it would crawl your sounds directory and then generate a js file like the above