playSoundOnTargetChannels method

int playSoundOnTargetChannels(
  1. Sound sound, {
  2. List<int>? targetChannels,
  3. bool loop = false,
  4. double? loopStart,
  5. double? loopEnd,
})

Plays the given Sound on one of the specified target channels.

  • sound: The Sound object to play.
  • targetChannels: A list of preferred channel IDs. The sound will be played on the first available channel from this list. If null, all channels are considered.
  • loop: Whether the sound should loop. Defaults to false.
  • loopStart: The start time in samples for looping, if loop is true. Converted to seconds internally using the sound's sample rate. Requires loopEnd to also be set.
  • loopEnd: The end time in samples for looping, if loop is true. Converted to seconds internally using the sound's sample rate. Requires loopStart to also be set.

Returns the channel ID on which the sound is playing, or -1 if the sound could not be played (e.g., sound not ready, retrigger delay not met, or no suitable channel found).

Implementation

int playSoundOnTargetChannels(
  Sound sound, {
  List<int>? targetChannels,
  bool loop = false,
  double? loopStart,
  double? loopEnd,
}) {
  if (audioContext.state == 'suspended') {
    audioContext.resume();
  }

  if (sound.state != LoadingState.ready) {
    return -1;
  }

  if (sound.lastTimePlayed != null) {
    final timeSinceLastPlayed = DateTime.now().difference(sound.lastTimePlayed!);
    if (timeSinceLastPlayed < sound.retriggerDelay) {
      return -1;
    }
  }

  var channelId = obtainFreeChannel(targetChannels);
  if (channelId == -1) {
    warn("All audio channels are in use. Consider to increase channel count.");
    return -1;
  }

  var channel = _channels[channelId];
  if (channel.state != ChannelState.stopped && channel.sourceNode != null) {
    channel.sourceNode!.onended = null;
    try {
      channel
        ..sourceNode!.stop(0)
        ..state = ChannelState.stopped;
    } catch (e) {
      //
    }
  }

  channel
    ..sound = sound
    ..loop = loop
    ..sourceNode = audioContext.createBufferSource()
    ..sourceNode!.buffer = sound.buffer
    ..sourceNode!.playbackRate.value = channel.rate
    ..sourceNode!.loop = loop
    ..sourceNode!.connect(channel.pannerNode);

  if (channel.sourceNode!.loop) {
    if (loopStart != null && loopEnd != null && loopEnd > loopStart) {
      double hz = sound.buffer?.sampleRate ?? 44100;
      loopStart = loopStart / hz;
      loopEnd = loopEnd / hz;
    } else {
      loopStart = 0;
      loopEnd = channel.sourceNode!.buffer!.duration;
    }

    channel.sourceNode!
      ..loopStart = loopStart
      ..loopEnd = loopEnd;
  }

  channel.sourceNode!.onended =
      (Event event) {
        channel
          ..sourceNode = null
          ..state = ChannelState.stopped;
      }.toJS;

  channel
    ..offset = 0
    ..startTime = audioContext.currentTime
    ..sourceNode?.start(0)
    ..state = ChannelState.playing;

  sound.lastTimePlayed = DateTime.now();

  return channelId;
}