Tracking events from audio service in flutter - 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.

Related

Flutter custom timer stream - where to insert in Fllutter widget hierarchy

Inside my Flutter app I am looking to inject a timer function that will execute a periodic call to an underlying service every half hour.
Currently my app is using a Provider class to hold all the business logic, including a method that calls the underlying service. This method makes a call to the service, updates the provider data properties with the results and then calls 'notifyListeners'.
To institute a periodic refresh of this process, I am thinking about using a custom stream that generates a periodic output of a value. A listener can then pick up the stream change, and then call the provider method.
My question is about how best to implement this. Should I set up the stream and listener inside the Provider class. Or should I be using a Stream builder as part of the widget hierarchy?
In the example code below, I have added a periodic streamer to simulate the timer as part of the screen setup. Is this a sound way of doing things?
#override
void didChangeDependencies() {
super.didChangeDependencies();
JourneyProvider aProvider = Provider.of<JourneyProvider>(context, listen: false);
print('initial - ' + DateTime.now().timeZoneName);
aProvider.refreshCollectors();
_timerStream = Stream<DateTime>.periodic(Duration(minutes:30,),(_) {return DateTime.now();});
_sub = _timerStream.listen((dt) {
print(dt);
aProvider.refreshCollectors();
});
}

Is there an function for "wait frame to end" like "WaitForEndOfFrame" in unity?

I need to do some calculations between every update(), so I need to ensure that a function is processed before the next frame.
Is there any way to achieve this? Just like "WaitForEndOfFrame" in Unity?
You can override the updateTree in your FlameGame (or in your Component, if you only want it to happen before the updates of a subtree) and do your calculations right before all the other components start updating. This would be right after the last frame, except for the first frame, but that one you can skip by setting a boolean.
So something like this:
class MyGame extends FlameGame {
bool isFirstTick = true;
#override
void updateTree(double dt) {
if(!isFirstTick) {
// Do your calculations
} else {
isFirstTick = false;
}
super.updateTree(dt);
}
}
But I have to ask, why do you need to do this? render won't be called until all the update calls are done, do can't you just put your calculations in the normal update method?
In Flutter we don't get an update() function unlike Unity. That is in the default API that we use, there are ways to tap into something of that effect. Normally we use a Ticker and create an animation to get periodic updates synced with screen refresh rate.
However, if what you are trying to do is to run something in between build() calls, WidgetsBinding.instance!.addPostFrameCallback() may be what you are looking for.
Here is a detailed answer that may help in this regard: https://stackoverflow.com/a/51273797/679553

Silence between tracks in just_audio

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.

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.

Background Process as NSOperation or Thread to monitor and update File

I want to check if a pdf file is changed or not, and if is changed i want to update the corresponding view. I don't know if it's more suitable to use a background process as a Thread or as an NSOperation to do this task. The Apple Documentation says: "Examples of tasks that lend themselves well to NSOperation include network requests, image resizing, text processing, or any other repeatable, structured, long-running task that produces associated state or data.But simply wrapping computation into an object doesn’t do much without a little oversight".
Also, if I understood correctly from the documentation, a Thread once started can't be stopped during his execution while an NSOperation could be paused or stopped and also they could rely on dependency to wait the completion of another task.
The workflow of this task should be more or less this diagram:
Task workflow
I managed to get the handler working after the notification of type .write has been sent. If i monitor for example a *.txt file everything works as expected and i receive only one notification. But i am monitoring a pdf file which is generated from terminal by pdflatex and thus i receive with '.write' nearly 15 notification. If i change to '.attrib' i get 3 notification. I need the handler to be called only once, not 15 or 3 times. Do you have any idea how can i do it or is not possible with a Dispatch Source? Maybe there is a way to execute a dispatchWorkItem only once?
I have tried to implement it like this(This is inside a FileMonitor class):
func startMonitoring()
{
....
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: fileStringURL)
let fileDescriptor = open(fileSystemRepresentation, O_EVTONLY)
let newfileMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileDescriptor,
eventMask: .attrib,
queue: queue)
newfileMonitorSource.setEventHandler(handler:
{
self.queue.async
{
print(" \n received first write event, removing handler..." )
self.newfileMonitorSource.setEventHandler(handler: nil)
self.test()
}
})
self.fileMonitorSource = newfileMonitorSource
fileMonitorSource!.resume()
}
func test()
{
fileMonitorSource?.cancel()
print(" restart monitoring ")
startMonitoring()
}
I have tried to reassign the handler in test(), but it's not working(if a regenerate the pdf file, what is inside the new handler it's not executed) and to me, doing in this way, it seems a bit boilerplate code. I have also tried the following things:
suspend the DispatchSource in the setEventHandler of startMonitoring() (passing nil), but then when i am resuming it, i get the remaining .write events.
cancel the DispatchSource object and recall the startMonitoring() as you can see in the code above, but in this way i create and destroy the DispatchSource object everytime i receive an event, which i don't like because the cancel() function shoul be called in my case only when the user decide to disable this feauture i am implementing.
I will try to write better how the workflow of the app should be so you can have an more clear idea of what i am doing:
When the app starts, a functions sets the default value of some checkboxes of the window preference. The user can modify this checkboxes. So when the user open a pdf file, the idea is to launch in a background thread the following task:
I create a new queue call it A and launch asynch an infinite while where i check the value of the UserDefault checkboxe (that i use to reload and update the pdf file) and two things could happen
if the user set the value to off and the pdf document has been loaded there could be two situations:
if there is no current monitoring of the file (when the app starts): continue to check the checkboxe value
if there is currently a monitoring of the file: stop it
if the user set value to on and the pdf document has been loaded in this background thread (the same queue A) i will create a class Monitor (that could be a subclass of NSThread or a class that uses DispatchSourceFileSystemObject like above), then i will call startMonitoring() that will check the date or .write events and when there is a change it will call the handler. Basically this handler should recall the main thread (the main queue) and check if the file can be loaded or is corrupted and if so update the view.
Note: The infinite while loop(that should be running in the background), that check the UserDefault related to the feature i am implementing it's launched when the user open the pdf file.
Because of the problem above (multiple handlers calls), i should use the cancel() function when the user set checkboxe to off, and not create/destroy the DispatchSource object everytime i receive a .write event.