Silence between tracks in just_audio - flutter

I want to have a variable length pause between tracks in a playlist created with a just_audio AudioPlayer instance (there is a background track which I want playing during this interval). Something to the effect of:
_voiceAudioPlayer.currentIndexStream.listen((event) {
_voiceAudioPlayer.pause();
Future.delayed(const Duration(seconds: 4), () => _voiceAudioPlayer.play());
});
This throws an error:
"Unhandled Exception: Bad state: Cannot fire new event. Controller is already firing an event"
Is there a clean way to do this? I'm considering adding silent mp3s at every other track in the playlist, but feel there there ought to be a better way.

This error happens because currentIndexStream is a "sync" broadcast stream, so you can't trigger another state change event while the current event is being processed (i.e. in the same cycle of the event loop). But you can get around that by scheduling a microtask to happen after the current cycle:
_voiceAudioPlayer.currentIndexStream.listen((index) {
scheduleMicrotask(() async {
_voiceAudioPlayer.pause();
await Future.delayed(Duration(seconds: 4));
_voiceAudioPlayer.play();
});
});
Still, I wouldn't depend on this callback being executed soon enough due to the gapless nature of just_audio's playlists. That is, the next audio track will begin playing immediately, so you're bound to hear at least a fraction of the next item's audio before the pause happens.
There is an open feature request for a SilenceAudioSource which could be inserted into a playlist (you can vote for that issue by clicking the thumbs up button if you'd like it to be implemented.) A silent audio file which you proposed is actually the simplest alternative to SilenceAudioSource.
Otherwise, another approach would be to not use the gapless playlists feature at all (since you don't need the gapless feature anyway), and just implement your own logic to advance the queue:
final queue = [source1, source2, source3, ...];
for (var source in queue) {
await _voiceAudioPlayer.setAudioSource(source);
await _voiceAudioPlayer.play();
await Future.delayed(seconds: 4);
}
The above example does not handle pause/resume logic, but it is just to show that it is possible for you to take the playlist logic into your own hands if you don't require the gapless feature.

Related

Сompletion playlist and resetting index in just_audio

I'm using the just_audio package, I have a playlist, and after playing all the tracks, the current index remains equal to the index of the last track. Is there a way to make the index equal to 0 when the playlist ends?
final myPlayList = ConcatenatingAudioSource(
children: listAudios,
);
await _player.setAudioSource(myPlayList, initialIndex: 0, preload: false);
Listen for the completed state and seek to index 0. Assuming you also want to stop playing at that point, call pause() too:
_player.processingStateStream.listen((state) async {
if (state == ProcessingState.completed) {
await _player.pause();
await _player.seek(Duration.zero, index: 0);
}
});
Regarding the call to pause(): just_audio state is a composite of playing and processingState and you will only hear audio when playing=true, processingState=ready which is what you have during normal playback. The playing state only ever changes when you tell it to change, but the processingState may change independently depending on what is happening in the audio.
When reaching the end of the playlist, it will transition to playing=true, processingState=completed, hence no audio. If you seek back to the beginning, it will be playing=true, processingState=ready so you'll hear the audio automatically start back up. This may surprise you, but think of it as comparable to what happens in a YouTube video after it completes, but if you seek to the beginning it continues playing (because it's now ready, not completed).
If you want it to stop playing, you need to actually set playing=false with either pause() or stop(), where the former keeps things in memory, and the latter releases all resources.
For more information about the state model and its rationale, see The State Model section of the README.

Tracking events from audio service in flutter

Is there a way to track if we are triggering rewind and fast forward and skipNext and skip to previous?
You can make an audio handler that wraps around your main audio handler. Your main audio handler is passed into AudioService.init, but your wrapper audio handler is used everywhere else whenever you want to manually trigger a method like fastForward. You can create the wrapper like this:
class FrontendAudioHandler extends CompositeAudioHandler {
FrontendAudioHandler(AudioHandler inner) : super(inner);
#override
Future<void> fastForward() async {
// insert any code here that should only happen when the app
// manually invokes fastForward.
await super.fastForward();
}
// Override any other methods you need to intercept.
}
Now in your app's startup, you set up two audio handlers, one wrapping the other:
final mainAudioHandler = AudioService.init(builder: () => MainAudioHandler());
final frontendAudioHandler = FrontendAudioHandler(mainAudioHandler);
When the user clicks fast forward in the notification, it will go directly to mainAudioHandler. But when you want to manually trigger a fast forward from within your Flutter code, just make sure you go through frontendAudioHandler, like this:
await frontendAudioHandler.fastForward();
This will first call the fastForward implementation above, giving you a chance to handle the manual trigger, but then that will call super.fastForward() which basically forwards on the request to your main audio handler.

Why is the Streamlistener reacting differently?

I'm trying to use the Streambuilder in cooperation with Bloc. The problem is that somehow the UI updates only when the expensive Funktions are finished. It seems that then, and only then updates are made to the stream. But I can not figure out why?
I Implemented a simple Bloc that has 4 events:
1. a Future called over the Bloc Sate Object
2. a Future called inside the Bloc
3. a Funktion called inside the Bloc
4 just using Future.delay
I'm Trying to understand why everything behaves as expectetd, but not when I use first two. My guess is I have a basic misunderstanding of the eventloop but I can not see why there should be a difference in behavior between 2 and 4 or 1 and 4
To make it easy I made an example Project on Github
https://github.com/pekretsc/bloceventloop.git
So I have my refresh methode that ads the new state to the Stream.
if (event is ExpensiveEventInState) {
refresh(BlocUIState.Waiting);
String result = await blocState.doSomeThing();
if (result == '') {
refresh(BlocUIState.Fin);
} else {
refresh(BlocUIState.Fail);
}
}
if (event is ExpensiveEventWhyDoesThisWork) {
refresh(BlocUIState.Waiting);
await Future.delayed(Duration(seconds: 3));
refresh(BlocUIState.Fin);
}
so the question is, should the first and second event not behave the same way?
What happens though is that in the first case the refresh is ignored completely and just the Fin is added to the stream. (I checked that, its not that it is too fast to recognize)
StreamListener's callback is called immediately when an event is pushed on the stream.
On the other hand, StreamBuilder's builder callback isn't. For most common use-cases, it is called at most once per frame.

Is it possible to change MediaRecorder's stream?

getUserMedia(constrains).then(stream => {
var recorder = new MediaRecorder(stream)
})
recorder.start()
recorder.pause()
// get new stream getUserMedia(constrains_new)
// how to update recorder stream here?
recorder.resume()
Is it possible? I've try to create MediaStream and use addTrack and removeTrack methods to change stream tracks but no success (recorder stops when I try to resume it with updated stream)
Any ideas?
The short answer is no, it's not possible. The MediaStream recording spec explicitly describes this behavior: https://w3c.github.io/mediacapture-record/#dom-mediarecorder-start. It's bullet point 15.3 of that algorithm which says "If at any point, a track is added to or removed from stream’s track set, the UA MUST immediately stop gathering data ...".
But in case you only want to record audio you can probably use an AudioContext to proxy your streams. Create a MediaStreamAudioDestinationNode and use the stream that it provides for recording. Then you can feed your streams with MediaStreamAudioSourceNodes and/or MediaStreamTrackAudioSourceNodes into the audio graph and mix them in any way you desire.
Last but not least there are currently plans to add the functionality you are looking for to the spec. Maybe you just have to wait a bit. Or maybe a bit longer depending on the browser you are using. :-)
https://github.com/w3c/mediacapture-record/issues/167
https://github.com/w3c/mediacapture-record/pull/186

Async/Await/then in Dart/Flutter

I have a flutter application where I am using the SQFLITE plugin to fetch data from SQLite DB. Here I am facing a weird problem. As per my understanding, we use either async/await or then() function for async programming.
Here I have a db.query() method which is conducting some SQL queries to fetch data from the DB. After this function fetches the data, we do some further processing in the .then() function. However, in this approach, I was facing some issues. From where I am calling this getExpensesByFundId(int fundId)function, it doesn't seem to fetch the data properly. It's supposed to return Future> object which will be then converted to List when the data is available. But when I call it doesn't work.
However, I just did some experimentation with it and added "await" keyword in front of the db.query() function and somehow it just started to work fine. Can you explain why adding the await keyword is solving this issue? I thought when using .then() function, we don't need to use the await keyword.
Here are my codes:
Future<List<Expense>> getExpensesByFundId(int fundId) async {
Database db = await database;
List<Expense> expenseList = List();
// The await in the below line is what I'm talking about
await db.query(expTable,where: '$expTable.$expFundId = $fundId')
.then((List<Map<String,dynamic>> expList){
expList.forEach((Map<String, dynamic> expMap){
expenseList.add(Expense.fromMap(expMap));
});
});
return expenseList;
}
In simple words:
await is meant to interrupt the process flow until the async method has finished.
then however does not interrupt the process flow (meaning the next instructions will be executed) but enables you to run code when the async method is finished.
In your example, you cannot achieve what you want when you use then because the code is not 'waiting' and the return statement is processed and thus returns an empty list.
When you add the await, you explicitly say: 'don't go further until my Future method is completed (namely the then part).
You could write your code as follows to achieve the same result using only await:
Future<List<Expense>> getExpensesByFundId(int fundId) async {
Database db = await database;
List<Expense> expenseList = List();
List<Map<String,dynamic>> expList = await db.query(expTable,where: '$expTable.$expFundId = $fundId');
expList.forEach((Map<String, dynamic> expMap) {
expenseList.add(Expense.fromMap(expMap));
});
return expenseList;
}
You could also choose to use only the then part, but you need to ensure that you call getExpensesByFundId properly afterwards:
Future<List<Expense>> getExpensesByFundId(int fundId) async {
Database db = await database;
List<Expense> expenseList = List();
return db.query(expTable,where: '$expTable.$expFundId = $fundId')
.then((List<Map<String,dynamic>> expList){
expList.forEach((Map<String, dynamic> expMap){
expenseList.add(Expense.fromMap(expMap));
});
});
}
// call either with an await
List<Expense> list = await getExpensesByFundId(1);
// or with a then (knowing that this will not interrupt the process flow and process the next instruction
getExpensesByFundId(1).then((List<Expense> l) { /*...*/ });
Adding to the above answers.
Flutter Application is said to be a step by step execution of code, but it's not like that.
There are a lot of events going to be triggered in the lifecycle of applications like Click Event, Timers, and all. There must be some code that should be running in the background thread.
How background work execute:
So there are two Queues
Microtask Queue
Event Queue
Microtask Queue runs the code which not supposed to be run by any event(click, timer, etc). It can contain both sync and async work.
Event Queue runs when any external click event occurs in the application like Click event, then that block execution done inside the event loop.
The below diagram will explain in detail how execution will proceed.
Note: At any given point of application development Microtask queue will run then only Event Queue will be able to run.
When making class use async for using await its simple logic to make a wait state in your function until your data is retrieve to show.
Example: 1) Its like when you follow click button 2) Data first store in database than Future function use to retrieve data 3) Move that data into variable and than show in screen 4) Variable show like increment in your following/profile.
And then is use one by one step of code, store data in variable and then move to next.
Example: If I click in follow button until data store in variable it continuously retrieve some data to store and not allow next function to run, and if one task is complete than move to another.
Same as your question i was also doing experiment in social media flutter app and this is my understanding. I hope this would help.
A Flutter question from an answer from your answer.
await is meant to interrupt the process flow until the async method has finished. then however does not interrupt the process flow but enables you to run code when the async method is finished. So, I am asking diff. between top down & bottom down process in programming.