Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions example/lib/chat_bubble.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,18 @@ class _WaveBubbleState extends State<WaveBubble> {
if (widget.index == null && widget.path == null && file?.path == null) {
return;
}
late final WaveformExtractionType waveformExtractionType;
if (widget.index != null) {
waveformExtractionType = widget.index!.isEven
? WaveformExtractionType.extractAsync
: WaveformExtractionType.noExtraction;
} else {
waveformExtractionType = WaveformExtractionType.extractAsync;
}
// Prepare player with extracting waveform if index is even.
controller.preparePlayer(
path: widget.path ?? file!.path,
shouldExtractWaveform: widget.index?.isEven ?? true,
);
path: widget.path ?? file!.path,
waveformExtractionType: waveformExtractionType);
// Extracting waveform separately if index is odd.
if (widget.index?.isOdd ?? false) {
controller.waveformExtraction
Expand Down
2 changes: 1 addition & 1 deletion ios/Classes/WaveformExtractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public class WaveformExtractor {
)
}

let progress = Float(i - startIndex + 1) / Float(endIndex - startIndex)
progress = Float(i - startIndex + 1) / Float(endIndex - startIndex)
await sendWaveformDataToFlutter(
waveformStorage: waveformStorage,
progress: progress,
Expand Down
1 change: 1 addition & 0 deletions lib/audio_waveforms.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export 'src/controllers/recorder_controller.dart';
export 'src/models/android_encoder_settings.dart';
export 'src/models/ios_encoder_setting.dart';
export 'src/models/recorder_settings.dart';
export 'src/enums/waveform_extraction_type.dart';
87 changes: 74 additions & 13 deletions lib/src/audio_file_waveforms.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class AudioFileWaveforms extends StatefulWidget {
/// If decoration is used then use color in it.
final Color? backgroundColor;

/// Duration for animation. Defaults to 500 milliseconds.
/// Duration for animation. If set to Duration.zero, no animation will occur.
/// Defaults to 500 milliseconds.
final Duration animationDuration;

/// Curve for animation. Defaults to Curves.easeIn
Expand Down Expand Up @@ -144,18 +145,26 @@ class _AudioFileWaveformsState extends State<AudioFileWaveforms>
void initState() {
super.initState();
_initialiseVariables();
_growingWaveController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
_growAnimation = CurvedAnimation(
parent: _growingWaveController,
curve: widget.animationCurve,
);

_growingWaveController
..forward()
..addListener(_updateGrowAnimationProgress);
if (widget.animationDuration == Duration.zero) {
_growAnimationProgress = 1.0;
} else {
_growingWaveController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
_growAnimation = CurvedAnimation(
parent: _growingWaveController,
curve: widget.animationCurve,
);

_growingWaveController
..forward()
..addListener(_updateGrowAnimationProgress);
}

_initializeAudioProgress();

onCurrentDurationSubscription =
playerController.onCurrentDurationChanged.listen((event) {
_seekProgress.value = event;
Expand Down Expand Up @@ -188,10 +197,50 @@ class _AudioFileWaveformsState extends State<AudioFileWaveforms>
onCurrentExtractedWaveformData?.cancel();
onCompletionSubscription.cancel();
playerController.removeListener(_addWaveformDataFromController);
_growingWaveController.dispose();

if (widget.animationDuration != Duration.zero) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard (and the late _growingWaveController) keys off the live widget.animationDuration, which can drift from how the controller was actually set up. If it starts non-zero (controller created) and the latest widget value is Duration.zero, dispose() skips _growingWaveController.dispose() and leaks it. And didUpdateWidget updates the cached animationDuration without ever creating the controller, so a Duration.zero → non-zero change leaves _growingWaveController uninitialized and the next access throws LateInitializationError. Could we track whether the controller was actually created (a bool, or make it nullable) and gate create/dispose on that instead of the live duration?

_growingWaveController.dispose();
}
Comment on lines +201 to +203

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please explain why you're only disposing when animation duration isn't zero?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because the controller is not instantiated over the duration is set to zero


super.dispose();
}

@override
void didUpdateWidget(AudioFileWaveforms oldWidget) {
super.didUpdateWidget(oldWidget);

// Vérifier si les waveformData ont changé
if (widget.waveformData != oldWidget.waveformData) {
if (widget.waveformData.isNotEmpty) {
_addWaveformData(widget.waveformData);
}
}

// Vérifier si d'autres propriétés importantes ont changé

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add comment in english

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^

if (widget.playerWaveStyle != oldWidget.playerWaveStyle ||
widget.size != oldWidget.size ||
widget.waveformType != oldWidget.waveformType) {
if (mounted) setState(() {});
}

// Mettre à jour les variables si nécessaire

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well, add comment in english

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^

if (widget.margin != oldWidget.margin ||
widget.padding != oldWidget.padding ||
widget.decoration != oldWidget.decoration ||
widget.backgroundColor != oldWidget.backgroundColor ||
widget.animationDuration != oldWidget.animationDuration ||
widget.animationCurve != oldWidget.animationCurve ||
widget.clipBehavior != oldWidget.clipBehavior) {
margin = widget.margin;
padding = widget.padding;
decoration = widget.decoration;
backgroundColor = widget.backgroundColor;
animationDuration = widget.animationDuration;
animationCurve = widget.animationCurve;
clipBehavior = widget.clipBehavior;
}
}

double _audioProgress = 0.0;
double _cachedAudioProgress = 0.0;

Expand Down Expand Up @@ -406,4 +455,16 @@ class _AudioFileWaveformsState extends State<AudioFileWaveforms>
}
});
}

Future<void> _initializeAudioProgress() async {
if (playerController.playerState == PlayerState.paused &&
playerController.maxDuration > 0) {
final currentPosition =
await playerController.getDuration(DurationType.current);
if (currentPosition > 0) {
_seekProgress.value = currentPosition;
_updatePlayerPercent();
}
}
}
}
21 changes: 10 additions & 11 deletions lib/src/controllers/player_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ class PlayerController extends ChangeNotifier {
Future<void> preparePlayer({
required String path,
double? volume,
bool shouldExtractWaveform = true,
WaveformExtractionType waveformExtractionType =
WaveformExtractionType.noExtraction,
int noOfSamples = 100,
}) async {
path = Uri.parse(path).path;
Expand All @@ -143,20 +144,18 @@ class PlayerController extends ChangeNotifier {
_setPlayerState(PlayerState.initialized);
}

if (shouldExtractWaveform) {
waveformExtraction
.extractWaveformData(
if (waveformExtractionType != WaveformExtractionType.noExtraction) {
var extraction = waveformExtraction.extractWaveformData(
path: path,
noOfSamples: noOfSamples,
)
.then(
(value) {
)..then((values) {
waveformExtraction.waveformData
..clear()
..addAll(value);
notifyListeners();
},
);
..addAll(values);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the async path, the old code called notifyListeners() inside this .then once extraction finished — that's gone now. For extractAsync, the data fills waveformData but listeners are never told, and AudioFileWaveforms (non-continuous) only repaints from the controller's listener, so the waveform stays blank until an unrelated notify (play/pause). Could we restore notifyListeners() here? Also worth adding an onError to the .then — otherwise a platform extraction failure becomes an unhandled async error.

if (waveformExtractionType == WaveformExtractionType.extractSync) {
await extraction;
}
Comment on lines +147 to +158

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add some comment above this to explain the logic here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the verifiable shouldExtractWaveform to enum, to allow for different cases. Don't extract waveforms, extract but don't wait for extraction to finish and finally extract and wait for extraction to finish.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the comments in the code file explaining the logic

}
notifyListeners();
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/controllers/waveform_extraction_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class WaveformExtractionController {

/// This returns waveform data which can be used by [AudioFileWaveforms]
/// to display waveforms.
List<double> get waveformData => _waveformData.toList();
List<double> get waveformData => _waveformData;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't want to allow users to modify the list but if you had any reason to remove it then can please mention it here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it because this part of the code you've written isn't working properly any more.
if (shouldExtractWaveform) { waveformExtraction .extractWaveformData( path: path, noOfSamples: noOfSamples, ) .then( (value) { waveformExtraction.waveformData ..clear() ..addAll(value); notifyListeners(); }, ); }
Since you return a newly created list every time you call waveformData, what you do in the PlayerController cannot work and be made persistent.


/// A stream to get current extracted waveform data. This stream will emit
/// list of doubles which are waveform data point.
Expand Down
10 changes: 10 additions & 0 deletions lib/src/enums/waveform_extraction_type.dart

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the enum under lib/src/base/utils.dart

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
enum WaveformExtractionType {
/// No waveform extraction will be performed.
noExtraction,

/// Extract waveform data asynchronously without waiting for the result.
extractAsync,

/// Extract waveform data and wait until it's completed before continuing.
extractSync,
}