Which API doesn't behave as documented, and how does it misbehave?
When an AudioPlayer plays from a StreamAudioSource that returns a response with a stream containing WAV PCM data and contentLength: null, sourceLength: null, offset: null to indicate that the overall length of the stream is not currently known, e.g. background sound that is continually generated, the audio is not played immediately. It may instead be played several minutes after the request completes, even for short amounts of data, e.g. 1 second long.
Minimal reproduction project
import 'dart:async';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chromatic scale',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Chromatic scale'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
const int _kRIFF = 0x52494646; // "RIFF"
const int _kWAVE = 0x57415645; // "WAVE"
const int _kfmt_ = 0x666d7420; // "fmt "
const int _kdata = 0x64617461; // "data"
const int _kMaxSize = 0xffffffff;
const int _kWavHeaderSize = 44;
// Generate a WAV header for a much-too-long file, as length may be unknown ahead of time, or infinite
int _writeWavHeader(ByteData dst, int hz, int nChannels, int nBytes) {
dst.setUint32(0, _kRIFF, Endian.big);
dst.setUint32(4, _kMaxSize, Endian.little);
dst.setUint32(8, _kWAVE, Endian.big);
dst.setUint32(12, _kfmt_, Endian.big);
dst.setUint32(16, 16, Endian.little);
dst.setUint16(20, 1, Endian.little);
dst.setUint16(22, nChannels, Endian.little);
dst.setUint32(24, hz, Endian.little);
dst.setUint32(28, hz * nChannels * nBytes, Endian.little);
dst.setUint16(32, nBytes * nChannels, Endian.little);
dst.setUint16(34, nBytes * 8, Endian.little);
dst.setUint32(36, _kdata, Endian.big);
dst.setUint32(40, _kMaxSize, Endian.little);
return _kWavHeaderSize;
}
// Audio source that generates PCM data and returns it with a WAV header
class StreamingSource extends StreamAudioSource {
static const int _sampleRate = 44100;
double frequency;
StreamingSource([this.frequency = 440]);
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
int t0 = start ?? 0;
int nSecs = 1;
int t1 = end ?? (_sampleRate * nSecs + _kWavHeaderSize);
int len = t1 - t0;
print('Requesting $start to $end, returning data of $len long.');
assert(len >= _kWavHeaderSize);
Uint8List data = Uint8List(len);
int endOfHeader = _writeWavHeader(data.buffer.asByteData(), _sampleRate, 1, 1);
for (int i = 0; i < data.length - endOfHeader; i++) {
double samp = sin(i * 2 * pi * frequency / _sampleRate) * 0.5 + 0.5;
data[endOfHeader + i] = (samp * 255).toInt();
}
return StreamAudioResponse(
sourceLength: null,
contentLength: null,
offset: null,
stream: Stream.fromIterable([data]),
contentType: 'audio/wav',
rangeRequestsSupported: true,
);
}
}
class _MyHomePageState extends State<MyHomePage> {
late AudioPlayer _player;
late StreamingSource _source;
@override
void initState() {
super.initState();
_player = AudioPlayer();
_source = StreamingSource(440);
_initPlayer();
}
Future<void> _initPlayer() async {
await _player.pause();
await _player.setAudioSource(_source, preload: false);
}
Future<void> _playTone() async {
_source.frequency *= pow(2.0, 1.0/12);
await _player.stop();
await _player.seek(Duration.zero); // Stop and seek necesssary to request audio again
await _player.play();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _playTone,
tooltip: 'Play',
child: const Icon(Icons.add),
),
);
}
}
To Reproduce (i.e. user steps, not code)
Steps to reproduce the behavior:
- Run the above sample.
- Press the floating action button to call
play
Actual behavior:
No audio is played for around 5 minutes, then the brief sine tone can be heard.
Error messages
[just_audio_windows] Called setVolume
[just_audio_windows] Called setSpeed
[just_audio_windows] Called setPitch
[just_audio_windows] Called setSkipSilence
[just_audio_windows] Called setLoopMode
[just_audio_windows] Called setShuffleMode
[just_audio_windows] Called play
[just_audio_windows] Called load
flutter: Requesting 0 to null, returning data of 44144 long.
Expected behavior
Once the audio data is requested and received from the Stream returned by StreamAudioSource.request, it should play a 1-second long sine tone of the current frequency.
Screenshots
N/A
Desktop (please complete the following information):
Smartphone (please complete the following information):
N/A
Flutter SDK version
Doctor summary (to see all details, run flutter doctor -v):
[!] Flutter (Channel [user-branch], 3.12.0-1.0.pre.14, on Microsoft Windows [Version 10.0.19045.2965], locale en-US)
! Flutter version 3.12.0-1.0.pre.14 on channel [user-branch] at C:\src\flutter\flutter\flutter
Currently on an unknown channel. Run `flutter channel` to switch to an official channel.
If that doesn't fix the issue, reinstall Flutter by following instructions at https://flutter.dev/docs/get-started/install.
! Unknown upstream repository.
Reinstall Flutter by following instructions at https://flutter.dev/docs/get-started/install.
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Professional 2022 17.5.1)
[√] Android Studio (version 2021.3)
[√] VS Code (version 1.78.2)
[√] Connected device (3 available)
[√] Network resources
Additional context
The owner of the just_audio repo has stated that this is a bug with the native player.
Which API doesn't behave as documented, and how does it misbehave?
When an
AudioPlayerplays from aStreamAudioSourcethat returns a response with a stream containing WAV PCM data andcontentLength: null, sourceLength: null, offset: nullto indicate that the overall length of the stream is not currently known, e.g. background sound that is continually generated, the audio is not played immediately. It may instead be played several minutes after the request completes, even for short amounts of data, e.g. 1 second long.Minimal reproduction project
To Reproduce (i.e. user steps, not code)
Steps to reproduce the behavior:
playActual behavior:
No audio is played for around 5 minutes, then the brief sine tone can be heard.
Error messages
Expected behavior
Once the audio data is requested and received from the
Streamreturned byStreamAudioSource.request, it should play a 1-second long sine tone of the current frequency.Screenshots
N/A
Desktop (please complete the following information):
Smartphone (please complete the following information):
N/A
Flutter SDK version
Additional context
The owner of the
just_audiorepo has stated that this is a bug with the native player.