Flutter just_audio package how play audio from bytes

6.2k views Asked by At

I'm using just_audio plugin and it has on description a feature: Read from byte stream.

Basically when I put a file (from url) to play, I'm saving the bytes from file so after this step I want to play it locally.

I have a question about how play from byte stream. Can anyone provide an example how to do this? I need to put this on my playlist so it has to be a child of ConcatanatingAudioSource.

The only Audio Source that I found was using it from Uri.

final _playlist = ConcatenatingAudioSource(
children: [
    AudioSource.uri(
      Uri.parse(
          "https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"),
      tag: AudioMetadata(
        album: "Science Friday",
        title: "ddddd",
        artwork:
            "https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg",
      ),
    )
]
)

This is how I save the bytes:

void getBytes() async {
  Uri uri = Uri.parse(url);
  var rng = new Random();
// get temporary directory of device.
  Directory tempDir = await getTemporaryDirectory();
// get temporary path from temporary directory.
  String tempPath = tempDir.path;
// create a new file in temporary path with random file name.
  File file = new File('$tempPath' + (rng.nextInt(100)).toString() + '.mp3');
// call http.get method and pass imageUrl into it to get response.
  http.Response response = await http.get(uri);
// write bodyBytes received in response to file.
  await file.writeAsBytes(response.bodyBytes);
}

Thanks in advance

1

There are 1 answers

2
Regular Jo On

So it seems that you need to create your own class as an extension of StreamAudioSource.

import 'dart:typed_data';
import 'package:just_audio/just_audio.dart';

class MyJABytesSource extends StreamAudioSource {
  final Uint8List _buffer;

  MyJABytesSource(this._buffer) : super(tag: 'MyAudioSource');

  @override
  Future<StreamAudioResponse> request([int? start, int? end]) async {
    // Returning the stream audio response with the parameters
    return StreamAudioResponse(
      sourceLength: _buffer.length,
      contentLength: (end ?? _buffer.length) - (start ?? 0),
      offset: start ?? 0,
      stream: Stream.fromIterable([_buffer.sublist(start ?? 0, end)]),
      contentType: 'audio/wav',
    );
  }
}

And then invoke it like so

await thePlayer.setAudioSource(MyJABytesSource(bytes));

You can call thePlayer.play(). after, but I prefer to use this as a listener.

thePlayer.processingStateStream.listen((ja.ProcessingState state) {
  if (state == ja.ProcessingState.ready) {
    // I'm using flutter_cache_manager, and it serves all the file
    // under the same name, which is fine, but I think this is why
    // I need to pause before I can play again.
    // (For tracks after the first, the first plays fine.)

    // You probably won't need to pause, but I'm not sure.
    thePlayer.pause();
    thePlayer.play();
  } else if (state == ja.ProcessingState.completed) {
    // What to do when it completes.
  }
});

The nice part about doing this way, is that you don't actually need await keyword, which can be situationally useful. I have it there just to make clear that this is an async function.