This article was originally published on Medium on Sep 28, 2024.
Hello, Futter developers! Cheers to those searching for a package in Flutter that will be able to add effect to their audio files. Here is a package called flutter_soloud, which gives you the chance to add effects such as echo, reverb, and so on to your audio file. It supports MP3, WAV, OGG, and FLAC formatted files. In this article, we will just focus on adding effects to an audio file. Let us come to the point and move forward step by step.
1 . First step: Initialize soloud
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_soloud/flutter_soloud.dart';
import 'package:logging/logging.dart';
final Logger _log = Logger('RecordProvider');
AudioSource? voiceAudioSource;
late Duration soundLength = Duration.zero;
final ValueNotifier<bool> isPlaying = ValueNotifier(false);
double echo = 0.0;
Future<bool>initRecordedVoiceFile({required String recordedVoicePath})async{
if(!SoLoud.instance.isInitialized){
await SoLoud.instance.init().then((_) {
Logger('main').info('player started');
SoLoud.instance.setVisualizationEnabled(true);
SoLoud.instance.setGlobalVolume(1);
SoLoud.instance.setMaxActiveVoiceCount(32);
},
onError: (Object e) {
Logger('main').severe('player starting error: $e');
},
);
}
try {
voiceAudioSource = await SoLoud.instance.loadFile(recordedVoicePath);
if (voiceAudioSource != null) {
soundLength = SoLoud.instance.getLength(voiceAudioSource!);
prepareAndPlay();
}
} on SoLoudException catch (e) {
_log.severe("Cannot play sound . Ignoring.", e);
}
return true;
}Code language: Dart (dart)Explanation:
- If the SoLoud instance is not initialized, it initializes using
SoLoud.instance.init(). - On successful initialization (
thenblock), several configuration options are set: - Enabling visualization:
SoLoud.instance.setVisualizationEnabled(true)allows the collection of audio visualization data, such as waveform or FFT. - Global volume:
SoLoud.instance.setGlobalVolume(1)sets the global volume to 100% (maximum volume). - Max active voices:
SoLoud.instance.setMaxActiveVoiceCount(32)limits the number of concurrently active audio voices to 32. This helps manage audio performance. - If there’s an error during initialization, it’s caught by the
onErrorcallback, and an error message is logged usingLogger.
voiceAudioSource = await SoLoud.instance.loadFile(recordedVoicePath);Code language: Dart (dart)- The function attempts to load the recorded voice file from the path specified in
recordedVoicePathusingSoLoud.instance.loadFile(). - The loaded file is stored in
voiceAudioSource, which is presumably an instance variable of typeAudioSourcethat holds the sound file in memory for playback.
Check if the Audio File is Loaded
if (voiceAudioSource != null) {
soundLength = SoLoud.instance.getLength(voiceAudioSource!);
prepareAndPlay();
}Code language: Dart (dart)- After loading the audio file, the code checks if the
voiceAudioSourceis not null, meaning the sound file was loaded successfully. - If successful, the following operations occur:
- Sound length:
soundLengthis assigned the length of the loaded sound usingSoLoud.instance.getLength(), which returns the duration of the sound file. - Prepare and Play: The function
prepareAndPlay()is called to handle audio playback preparation and start playing the sound.
2. Second Step: handle audio playback
StreamSubscription<StreamSoundEvent>? subscription;
SoundHandle? voiceHandle;
Future<void> prepareAndPlay() async {
try {
voiceHandle = await SoLoud.instance.play(voiceAudioSource!,paused: true); // Initially paused
SoLoud.instance.setVolume(voiceHandle!, 1);
final voiceSoundFilter = voiceAudioSource!.filters.echoFilter;
if(!voiceSoundFilter.isActive){
voiceSoundFilter.activate();
}
voiceSoundFilter.wet(soundHandle: voiceHandle).value = echo;
if (kDebugMode) {
print("activated echo filter $echo");
}
subscription = voiceAudioSource!.soundEvents.listen((eventResult) {
if (eventResult.event == SoundEventType.handleIsNoMoreValid) {
resetPlayer();
}
});
} on SoLoudException catch (e) {
_log.severe("Cannot play sound . Ignoring.", e);
}
}Code language: Dart (dart)Explanation:
voiceHandle = await SoLoud.instance.play(voiceAudioSource!, paused: true); // Initially pausedCode language: Dart (dart)- This line plays the audio from the
voiceAudioSource. Thepaused: trueparameter means the audio will not start playing immediately but will be loaded into memory, ready to play. - The
voiceHandleis an identifier for this particular playback instance, which you can later use to control the playback (like pausing, stopping, adjusting volume, etc.)
Setting the Volume
SoLoud.instance.setVolume(voiceHandle!, 1);Code language: Dart (dart)This sets the volume for the current audio being played, identified by voiceHandle. Here, the volume is set to 1, which is 100% (full volume).
Applying the Echo Filte
final voiceSoundFilter = voiceAudioSource!.filters.echoFilter;Code language: Dart (dart)This line retrieves the echoFilter from the voiceAudioSource. Each sound in SoLoud can have multiple filters applied, and in this case, the code is specifically selecting the echo filter for the loaded sound.
if (!voiceSoundFilter.isActive) {
voiceSoundFilter.activate();
}Code language: Dart (dart)It checks if the echo filter is not yet active, and if it’s not, it activates the filter. This allows the audio to be played with an echo effect.
voiceSoundFilter.wet(soundHandle: voiceHandle).value = echo;Code language: Dart (dart)- This sets the wet mix value of the echo filter. The wet value controls how much of the echo effect is mixed with the original sound. The
echovariable is a value (likely a user-defined variable) that determines how strong or subtle the echo effect should be. - The
soundHandleparameter specifies that this filter is applied to the sound currently associated withvoiceHandle(the one being played).
Listening for Sound Events
subscription = voiceAudioSource!.soundEvents.listen((eventResult) {
if (eventResult.event == SoundEventType.handleIsNoMoreValid) {
resetPlayer();
}
});Code language: Dart (dart)- This sets up an event listener on the
voiceAudioSource. SoLoud can emit various events during sound playback, and this code listens for those events. - Specifically, it checks if the event is of type
handleIsNoMoreValid, which typically indicates that the sound handle (the currently playing sound) is no longer valid, either because the sound has finished playing or was stopped. - When this event occurs, the function
resetPlayer()is called, which likely resets the state of the player (e.g., stopping playback, resetting UI controls, etc.).
Resetting player after completing 1 cycle
void resetPlayer() {
if(kDebugMode){
print("\n\n===================== resetPlayer() called ========================\n");
}
SoLoud.instance.stop(voiceHandle!);
isPlaying.value = false;
prepareAndPlay(); // Reset the audio to its initial state
}Code language: Dart (dart)resetPlayer()is used to stop the current audio playback, reset the playback state, and reinitialize the player.- It halts the audio using
SoLoud.instance.stop(), sets the playback position to the beginning, and updates the UI to reflect that the sound is no longer playing. - The function then calls
prepareAndPlay()to load the audio again and prepare it for a fresh playback cycle. - This method is useful when you want to reset and start the audio from the beginning, whether because the sound has finished playing or the user manually triggered the reset.
3 . Step Three: PlayPause Audio file or Toggle the Player
void toggleVoicePlayPause() async {
if (voiceHandle != null ) {
try{
SoLoud.instance.pauseSwitch(voiceHandle!);
isPlaying.value = !SoLoud.instance.getPause(voiceHandle!);
if (kDebugMode) {
print("${isPlaying.value}");
}
}on SoLoudException catch (e) {
_log.severe("Cannot play sound . Ignoring.", e);
}
}
}Code language: Dart (dart)- The
toggleVoicePlayPausefunction toggles the play/pause state of an audio file loaded withvoiceHandle. - It uses
pauseSwitch()to switch between playing and pausing the sound and updates theisPlayingvariable to reflect the current state. - The function checks whether the sound is paused using
getPause(), and theisPlayingvariable is updated accordingly, which controls the UI’s play/pause state. - If there’s an issue during playback (such as an invalid sound handle), it handles the error gracefully by logging it.
4. Step Four: Add a function to change echo value dynamically
void updateEchoValue(double updatedValue) {
if (kDebugMode) {
print("Updated ECHO value : $updatedValue");
}
if(voiceAudioSource != null){
final voiceSoundFilter = voiceAudioSource!.filters.echoFilter;
voiceSoundFilter.wet(soundHandle: voiceHandle).value = updatedValue;
echo = updatedValue;
}
}Code language: Dart (dart)updateEchoValue(double updatedValue)dynamically updates the echo effect applied to an audio file.- The function first checks if an audio file (
voiceAudioSource) is loaded. If it is, the echo filter for the current audio playback is retrieved. - It then updates the wet parameter of the echo filter, which controls the intensity of the echo effect in real -time.
- The function also updates the local variable
echoto keep track of the current echo setting.
If you’re building a SaaS product and need help with audio, we’d love to talk. Click here to book a free SaaS plan & quote.
