Skip to content

Commit c1c242c

Browse files
authored
Merge pull request #190 from alnitak/newAudioData
added `alwaysReturnData` to `AudioData.getAudioData`
2 parents 8ed6e45 + b8f135e commit c1c242c

22 files changed

+383
-216
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
"cwd": "${workspaceFolder}"
9090
},
9191
{
92-
"name": "Chrome",
92+
"name": "Chrome release",
9393
"type": "chrome",
9494
"preLaunchTask": "compile web wasm release",
9595
"request": "launch"

.vscode/tasks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
},
1010
{
1111
"label": "compile linux debug",
12-
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/main.dart --debug",
12+
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/audio_data/audio_data.dart --debug",
1313
"type": "shell"
1414
},
1515
{

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### 3.1.0 (18 Mar 2025)
2+
- wWhen calling `AudioData.getAudioData` is now possible to check if the audio data is the same as before. Useful to visualize waveforms. This is because `AudioData.getAudioData` returns the current data in the buffer and if it is called before the buffer has been updated, it will return the previous data.
3+
- added `resetBufferStream` method to `SoLoud`. It happens that when playing a stream, maybe from the web, it is needed to change it to another source. The player continues to play the already added audio data to the buffer. This method can be used to reset the buffer and start with the new audio data.
4+
15
### 3.0.3 (7 Mar 2025)
26
- it's now possible to choose to not link opus and ogg libraries (see `NO_OPUS_OGG_LIBS.md`). Fix for #191 and #192.
37

example/lib/audio_data/audio_data.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ void main() async {
3939
WidgetsFlutterBinding.ensureInitialized();
4040

4141
/// Initialize the player.
42-
await SoLoud.instance.init();
42+
await SoLoud.instance.init(bufferSize: 1024, channels: Channels.mono);
4343

4444
/// Activate the visualization. Mandatory to acquire audio data.
4545
SoLoud.instance.setVisualizationEnabled(true);

example/lib/audio_data/data_widget.dart

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ class AudioDataWidgetState extends State<AudioDataWidget>
1414
with SingleTickerProviderStateMixin {
1515
Ticker? ticker;
1616

17-
/// Set [AudioData] to use a `linear` data kind. This is the way to get both
18-
/// wave and FFT data.
17+
/// Set [AudioData] to use a [GetSamplesKind.linear] data kind.
18+
/// This is the way to get both wave and FFT data in a single list.
1919
final AudioData audioData = AudioData(GetSamplesKind.linear);
2020

2121
@override
@@ -35,7 +35,7 @@ class AudioDataWidgetState extends State<AudioDataWidget>
3535
void _tick(Duration elapsed) {
3636
if (context.mounted) {
3737
try {
38-
/// Internally update the audio data to be used later
38+
/// Internally update the audio data to be get later
3939
/// with `audioData.getLinear*()` in [WavePainter]
4040
audioData.updateSamples();
4141
setState(() {});
@@ -71,8 +71,7 @@ class AudioDataWidgetState extends State<AudioDataWidget>
7171
}
7272
}
7373

74-
/// Custom painter to draw the wave in a circle
75-
///
74+
/// Custom painter to draw the wave and the FFT
7675
class WavePainter extends CustomPainter {
7776
const WavePainter({
7877
required this.audioData,
@@ -81,13 +80,18 @@ class WavePainter extends CustomPainter {
8180

8281
@override
8382
void paint(Canvas canvas, Size size) {
83+
// ignore: avoid_redundant_argument_values
84+
final samples = audioData.getAudioData(alwaysReturnData: true);
85+
// Using `alwaysReturnData: true` this will always return a non-empty list
86+
// even if the audio data is the same as the previous one.
87+
if (samples.isEmpty) {
88+
return;
89+
}
8490
final barWidth = size.width / 256;
8591
final paint = Paint()
8692
..strokeWidth = barWidth * 0.8
8793
..color = Colors.yellowAccent;
8894

89-
final samples = audioData.getAudioData();
90-
9195
double waveHeight;
9296
double fftHeight;
9397

example/pubspec.lock

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,18 @@ packages:
7777
dependency: transitive
7878
description:
7979
name: ffi
80-
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
80+
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
8181
url: "https://pub.dev"
8282
source: hosted
83-
version: "2.1.3"
83+
version: "2.1.4"
8484
file_picker:
8585
dependency: "direct main"
8686
description:
8787
name: file_picker
88-
sha256: "3d57312a53746ed4eb8c843dc50372454bbda37dd0c01a4d40fedc83e2ce4921"
88+
sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810
8989
url: "https://pub.dev"
9090
source: hosted
91-
version: "8.3.5"
91+
version: "8.3.7"
9292
flutter:
9393
dependency: "direct main"
9494
description: flutter
@@ -98,17 +98,17 @@ packages:
9898
dependency: transitive
9999
description:
100100
name: flutter_plugin_android_lifecycle
101-
sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e"
101+
sha256: "5a1e6fb2c0561958d7e4c33574674bda7b77caaca7a33b758876956f2902eea3"
102102
url: "https://pub.dev"
103103
source: hosted
104-
version: "2.0.24"
104+
version: "2.0.27"
105105
flutter_soloud:
106106
dependency: "direct main"
107107
description:
108108
path: ".."
109109
relative: true
110110
source: path
111-
version: "3.0.3"
111+
version: "3.1.0"
112112
flutter_test:
113113
dependency: "direct dev"
114114
description: flutter
@@ -211,10 +211,10 @@ packages:
211211
dependency: transitive
212212
description:
213213
name: path_provider_android
214-
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
214+
sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12"
215215
url: "https://pub.dev"
216216
source: hosted
217-
version: "2.2.15"
217+
version: "2.2.16"
218218
path_provider_foundation:
219219
dependency: transitive
220220
description:
@@ -352,10 +352,10 @@ packages:
352352
dependency: transitive
353353
description:
354354
name: web
355-
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
355+
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
356356
url: "https://pub.dev"
357357
source: hosted
358-
version: "1.1.0"
358+
version: "1.1.1"
359359
web_socket:
360360
dependency: transitive
361361
description:
@@ -376,10 +376,10 @@ packages:
376376
dependency: transitive
377377
description:
378378
name: win32
379-
sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
379+
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
380380
url: "https://pub.dev"
381381
source: hosted
382-
version: "5.10.1"
382+
version: "5.12.0"
383383
xdg_directories:
384384
dependency: transitive
385385
description:
@@ -389,5 +389,5 @@ packages:
389389
source: hosted
390390
version: "1.1.0"
391391
sdks:
392-
dart: ">=3.7.0-0 <4.0.0"
393-
flutter: ">=3.24.0"
392+
dart: ">=3.7.0 <4.0.0"
393+
flutter: ">=3.27.0"

lib/src/bindings/audio_data.dart

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class AudioData {
122122
/// The callback used to get new audio samples.
123123
/// This callback is used in [updateSamples] to avoid to
124124
/// do [GetSamplesKind] checks on every calls.
125-
late void Function(AudioData) _updateCallback;
125+
late bool Function(AudioData) _updateCallback;
126126

127127
/// Update the content of samples memory to be read later
128128
/// using [getAudioData].
@@ -136,7 +136,7 @@ class AudioData {
136136
/// [GitHub](https://github.com/alnitak/flutter_soloud/issues) providing
137137
/// a simple working example.
138138
void updateSamples() {
139-
_updateCallback(this);
139+
ctrl.dataIsTheSameAsBefore = _updateCallback(this);
140140
}
141141

142142
/// Changes the input device from which to retrieve audio data and its kind.
@@ -152,23 +152,39 @@ class AudioData {
152152
ctrl.dispose(_getSamplesKind);
153153
}
154154

155-
/// Get audio data data.
155+
/// Get audio data only for visualization purposes. Don't use this method
156+
/// for audio processing or for saving audio data.
156157
///
157158
/// Depending on the [GetSamplesKind] used to initialize [AudioData],
158159
/// the returned data will be a [Float32List]. See [GetSamplesKind] for
159160
/// more information.
160-
Float32List getAudioData() {
161+
///
162+
/// [alwaysReturnData] if true, the audio data list will be returned even if
163+
/// it is the same as the previous one.
164+
/// When this method is callled, the returned audio data is the current
165+
/// audio buffer shrinked to 256 samples currently playing. For example,
166+
/// when initializing the engine with a buffer of 2048, this will return
167+
/// the average of that data grouped by 8 samples. For a good visualization
168+
/// should be better to use a buffer size of 1024 or maybe 512.
169+
/// When calling this method 2 times and before all the buffer is played,
170+
/// the returned data will be the same. The purpose of [alwaysReturnData] is
171+
/// to avoid this and when this happens, the returned data will be an
172+
/// empty list when [alwaysReturnData] is false.
173+
Float32List getAudioData({bool alwaysReturnData = true}) {
174+
/// Non blocking condition.
175+
if (!SoLoudController().soLoudFFI.isInited()) {
176+
return Float32List(0);
177+
}
161178
if (!SoLoudController().soLoudFFI.getVisualizationEnabled()) {
162179
throw const SoLoudVisualizationNotEnabledException();
163180
}
164-
165181
switch (_getSamplesKind) {
166182
case GetSamplesKind.wave:
167-
return ctrl.getWave();
183+
return ctrl.getWave(alwaysReturnData: alwaysReturnData);
168184
case GetSamplesKind.linear:
169-
return ctrl.getFftAndWave();
185+
return ctrl.getFftAndWave(alwaysReturnData: alwaysReturnData);
170186
case GetSamplesKind.texture:
171-
return ctrl.get2DTexture();
187+
return ctrl.get2DTexture(alwaysReturnData: alwaysReturnData);
172188
}
173189
}
174190
}

lib/src/bindings/audio_data_ffi.dart

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'package:ffi/ffi.dart' show calloc;
66
import 'package:flutter/foundation.dart';
77
import 'package:flutter_soloud/src/bindings/audio_data.dart';
88
import 'package:flutter_soloud/src/bindings/soloud_controller.dart';
9-
import 'package:flutter_soloud/src/enums.dart';
109

1110
class AudioDataCtrl {
1211
/// To reflect [AudioDataCtrl] for web. Not used with `dart:ffi`
@@ -20,20 +19,23 @@ class AudioDataCtrl {
2019
late Pointer<Pointer<Float>> samples2D;
2120

2221
/// Where the audio 1D data is stored.
23-
late Pointer<Float> samples1D;
22+
late Pointer<Pointer<Float>> samples1D;
2423

25-
final void Function(AudioData) waveCallback =
24+
final bool Function(AudioData) waveCallback =
2625
SoLoudController().soLoudFFI.getWave;
2726

28-
final void Function(AudioData) textureCallback =
27+
final bool Function(AudioData) textureCallback =
2928
SoLoudController().soLoudFFI.getAudioTexture;
3029

31-
final PlayerErrors Function(AudioData) texture2DCallback =
30+
final bool Function(AudioData) texture2DCallback =
3231
SoLoudController().soLoudFFI.getAudioTexture2D;
3332

33+
late bool dataIsTheSameAsBefore;
34+
3435
void allocSamples(AudioData audioData) {
36+
dataIsTheSameAsBefore = false;
3537
samples2D = calloc();
36-
samples1D = calloc(512 * 4);
38+
samples1D = calloc();
3739
samplesWave = calloc();
3840
}
3941

@@ -43,35 +45,50 @@ class AudioDataCtrl {
4345
if (samplesWave != nullptr) calloc.free(samplesWave);
4446
if (samples1D != nullptr) calloc.free(samples1D);
4547
if (samples2D != nullptr) calloc.free(samples2D);
48+
samplesWave = nullptr;
49+
samples1D = nullptr;
50+
samples2D = nullptr;
4651
}
4752

48-
Float32List getWave() {
49-
final val = Pointer<Float>.fromAddress(samplesWave.value.address);
50-
if (val == nullptr) return Float32List(0);
53+
Float32List getWave({bool alwaysReturnData = true}) {
54+
final wavePtr = samplesWave.value;
55+
if (!alwaysReturnData && dataIsTheSameAsBefore || wavePtr == nullptr) {
56+
return Float32List(0);
57+
}
58+
5159
return Float32List.view(
52-
val.cast<Uint8>().asTypedList(256 * 4).buffer,
60+
wavePtr.cast<Uint8>().asTypedList(256 * 4).buffer,
5361
0,
5462
256,
5563
);
5664
}
5765

58-
Float32List getFftAndWave() {
59-
final val = Pointer<Float>.fromAddress(samples1D.address);
60-
if (val == nullptr) return Float32List(0);
66+
Float32List getFftAndWave({bool alwaysReturnData = true}) {
67+
final texturePtr = samples1D.value;
68+
69+
if (!alwaysReturnData && dataIsTheSameAsBefore || texturePtr == nullptr) {
70+
return Float32List(0);
71+
}
6172
return Float32List.view(
62-
val.cast<Uint8>().asTypedList(512 * 4).buffer,
73+
texturePtr.cast<Uint8>().asTypedList(512 * 4).buffer,
6374
0,
6475
512,
6576
);
6677
}
6778

68-
Float32List get2DTexture() {
69-
final val = Pointer<Float>.fromAddress(samples1D.address);
70-
if (val == nullptr) return Float32List(0);
71-
return Float32List.view(
72-
val.cast<Uint8>().asTypedList(512 * 256 * 4).buffer,
79+
Float32List get2DTexture({bool alwaysReturnData = true}) {
80+
final texture2DPtr = samples2D.value;
81+
82+
if (!alwaysReturnData && dataIsTheSameAsBefore || texture2DPtr == nullptr) {
83+
return Float32List(0);
84+
}
85+
86+
final ret = Float32List.view(
87+
texture2DPtr.cast<Uint8>().asTypedList(512 * 256 * 4).buffer,
7388
0,
7489
512 * 256,
7590
);
91+
92+
return ret;
7693
}
7794
}

0 commit comments

Comments
 (0)