Skip to content

Commit

Permalink
Merge pull request #137 from alnitak/feat_waveform
Browse files Browse the repository at this point in the history
Read samples from audio
  • Loading branch information
alnitak authored Oct 11, 2024
2 parents 2deacca + 3c2c90a commit 39c7603
Show file tree
Hide file tree
Showing 30 changed files with 2,463 additions and 1,922 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "Flutter debug",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"program": "lib/wave_data/wave_data.dart",
"flutterMode": "debug",
"cwd": "${workspaceFolder}/example"
},
Expand Down
6 changes: 3 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"tasks": [
{
"label": "compile linux debug verbose",
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/filters/pitchshift.dart --debug --verbose",
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/main.dart --debug --verbose",
// "args": ["build", "linux", "--verbose"],
"type": "shell"
},
{
"label": "compile linux debug",
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/main.dart --debug",
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/wave_data/wave_data.dart --debug",
"type": "shell"
},
{
Expand All @@ -29,7 +29,7 @@
},
{
"label": "compile web debug",
"command": "cd ${workspaceFolder}/example; flutter run -d chrome --web-renderer canvaskit --web-browser-flag '--disable-web-security' -t lib/main.dart --release",
"command": "cd ${workspaceFolder}/example; flutter run -d chrome --web-renderer canvaskit --web-browser-flag '--disable-web-security' -t lib/wave_data/wave_data.dart --release",
"type": "shell"
}
]
Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 2.1.5 (11 Oct 2024)
- added `readSamplesFrom*()` methods to read N audio data within a time range from a file or memory #75. Example in `example/lib/wave_data/wave_data.dart`.

### 2.1.4 (18 Sep 2024)
- fixed waveform generation which somehow oscillate frequencies after some time #129.
- fixed iOS compilation by rising minimum iOS version to 13 #128.
Expand All @@ -13,7 +16,7 @@
- bug fix when loading multiple audio files asynchronously.

### 2.1.1 (28 Aug 2024)
- added `bool isActive` and `int iindex` getters to filters.
- added `bool isActive` and `int index` getters to filters.
- added a `timeStretch()` method to single pitchshift filter.
- fixed building error on Windows.
- updated examples.
Expand Down
1 change: 1 addition & 0 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ list(APPEND PLUGIN_SOURCES
"${SRC_DIR}/player.cpp"
"${SRC_DIR}/analyzer.cpp"
"${SRC_DIR}/synth/basic_wave.cpp"
"${SRC_DIR}/waveform/waveform.cpp"
"${SRC_DIR}/filters/filters.cpp"
"${SRC_DIR}/filters/pitch_shift_filter.cpp"
"${SRC_DIR}/filters/smbPitchShift.cpp"
Expand Down
176 changes: 176 additions & 0 deletions example/lib/wave_data/wave_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import 'dart:developer' as dev;
import 'dart:io';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_soloud/flutter_soloud.dart';
import 'package:logging/logging.dart';

void main() async {
// The `flutter_soloud` package logs everything
// (from severe warnings to fine debug messages)
// using the standard `package:logging`.
// You can listen to the logs as shown below.
Logger.root.level = kDebugMode ? Level.FINE : Level.INFO;
Logger.root.onRecord.listen((record) {
dev.log(
record.message,
time: record.time,
level: record.level.value,
name: record.loggerName,
zone: record.zone,
error: record.error,
stackTrace: record.stackTrace,
);
});

WidgetsFlutterBinding.ensureInitialized();

/// Initialize the player.
await SoLoud.instance.init();

runApp(
const MaterialApp(
home: HelloFlutterSoLoud(),
),
);
}

/// Simple usecase of flutter_soloud plugin
class HelloFlutterSoLoud extends StatefulWidget {
const HelloFlutterSoLoud({super.key});

@override
State<HelloFlutterSoLoud> createState() => _HelloFlutterSoLoudState();
}

class _HelloFlutterSoLoudState extends State<HelloFlutterSoLoud> {
List<PlatformFile>? paths;
AudioSource? currentSound;
Float32List? data;
bool average = false;

@override
void dispose() {
SoLoud.instance.deinit();
super.dispose();
}

@override
Widget build(BuildContext context) {
if (!SoLoud.instance.isInitialized) return const SizedBox.shrink();

final width = MediaQuery.sizeOf(context).width.toInt();
return Scaffold(
body: Center(
child: Column(
children: [
const SizedBox(height: 16),
const Text(
'Read N audio samples (here N = width) from audio file.',
),
OutlinedButton(
onPressed: () async {
paths = (await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['mp3', 'wav', 'flac'],
onFileLoading: print,
dialogTitle: 'Pick audio file',
))
?.files;

await _loadPath(width);
if (context.mounted) setState(() {});
},
child: const Text('pickFile'),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Average samples '),
Switch(
value: average,
onChanged: (value) async {
average = value;
await _loadPath(width);
if (context.mounted) setState(() {});
},
),
],
),
if (data != null)
Expanded(
child: Container(
color: Colors.black,
width: width.toDouble(),
child: RepaintBoundary(
child: ClipRRect(
child: CustomPaint(
key: UniqueKey(),
painter: WavePainter(
data: data!,
),
),
),
),
),
),
],
),
),
);
}

Future<void> _loadPath(int width) async {
if (paths != null) {
if (kIsWeb) {
// on web we can't read the bytes from the file.
data = await SoLoud.instance.readSamplesFromMem(
paths!.first.bytes!,
width * 4,
average: average,
);
} else {
final f = File(paths!.first.path!);
final bytes = f.readAsBytesSync();
data = await SoLoud.instance.readSamplesFromMem(
bytes,
width * 4,
average: average,
);
}
}
}
}

/// Custom painter to draw the wave data.
class WavePainter extends CustomPainter {
const WavePainter({
required this.data,
});

final Float32List data;

@override
void paint(Canvas canvas, Size size) {
final barWidth = size.width / data.length;
final paint = Paint()
..color = Colors.yellowAccent
..strokeWidth = barWidth;

for (var i = 0; i < data.length; i++) {
final barHeight = size.height * data[i] * 2;
canvas.drawLine(
Offset(barWidth * i, (size.height - barHeight) / 2),
Offset(barWidth * i, (size.height + barHeight) / 2),
paint,
);
}
}

@override
bool shouldRepaint(WavePainter oldDelegate) {
return true;
}
}
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ packages:
path: ".."
relative: true
source: path
version: "2.1.3"
version: "2.1.5"
flutter_test:
dependency: "direct dev"
description: flutter
Expand Down
5 changes: 2 additions & 3 deletions lib/src/bindings/audio_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter_soloud/src/bindings/audio_data_ffi.dart'
if (dart.library.js_interop) 'audio_data_web.dart';
import 'package:flutter_soloud/src/bindings/soloud_controller.dart';
import 'package:flutter_soloud/src/exceptions/exceptions.dart';
import 'package:flutter_soloud/src/soloud.dart';
import 'package:meta/meta.dart';

/// The way the audio data should be acquired.
Expand Down Expand Up @@ -89,9 +90,7 @@ enum GetSamplesKind {
/// }
/// ```
///
/// To smooth FFT values use [SoLoud.instance.setFftSmoothing].
///
// TODO(all): make AudioData singleton?
/// To smooth FFT values use [SoLoud.setFftSmoothing].
@experimental
class AudioData {
/// Initialize the way the audio data should be acquired.
Expand Down
24 changes: 24 additions & 0 deletions lib/src/bindings/bindings_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,30 @@ abstract class FlutterSoLoud {
/// You can change the doppler factor of a live 3d audio source.
@mustBeOverridden
void set3dSourceDopplerFactor(SoundHandle handle, double dopplerFactor);

// ///////////////////////////////////////
// waveform audio data
// ///////////////////////////////////////

/// See SoLoud.readSamplesFromFile for details.
@mustBeOverridden
Float32List readSamplesFromFile(
String completeFileName,
int numSamplesNeeded, {
double startTime = 0,
double endTime = -1,
bool average = false,
});

/// See SoLoud.readSamplesFromMem for details.
@mustBeOverridden
Float32List readSamplesFromMem(
Uint8List buffer,
int numSamplesNeeded, {
double startTime = 0,
double endTime = -1,
bool average = false,
});
}

/// Used for easier conversion from [double] to [Duration].
Expand Down
Loading

0 comments on commit 39c7603

Please sign in to comment.