Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Ability to extract files simultaneously #12

Merged
merged 11 commits into from
Dec 30, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.os.Handler;
import java.util.List;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import java.util.HashMap;

/** JustWaveformPlugin */
public class JustWaveformPlugin implements FlutterPlugin, MethodCallHandler {
Expand All @@ -18,48 +19,56 @@ public class JustWaveformPlugin implements FlutterPlugin, MethodCallHandler {

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "com.ryanheise.just_waveform");
channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(),
"com.ryanheise.just_waveform");
channel.setMethodCallHandler(this);
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) {
switch (call.method) {
case "extract":
String audioInPath = call.argument("audioInPath");
String waveOutPath = call.argument("waveOutPath");
Integer samplesPerPixel = call.argument("samplesPerPixel");
Integer pixelsPerSecond = call.argument("pixelsPerSecond");
WaveformExtractor waveformExtractor = new WaveformExtractor(audioInPath, waveOutPath, samplesPerPixel, pixelsPerSecond);
waveformExtractor.start(new WaveformExtractor.OnProgressListener() {
@Override
public void onProgress(int progress) {
invokeMethod("onProgress", progress);
}
@Override
public void onComplete() {
handler.post(new Runnable() {
@Override
public void run() {
result.success(null);
}
});
}
@Override
public void onError(final String message) {
invokeMethod("onError", message);
handler.post(new Runnable() {
@Override
public void run() {
result.error(message, null, null);
}
});
}
});
break;
default:
result.notImplemented();
break;
case "extract":
String audioInPath = call.argument("audioInPath");
String waveOutPath = call.argument("waveOutPath");
Integer samplesPerPixel = call.argument("samplesPerPixel");
Integer pixelsPerSecond = call.argument("pixelsPerSecond");
WaveformExtractor waveformExtractor = new WaveformExtractor(audioInPath, waveOutPath, samplesPerPixel,
pixelsPerSecond);
waveformExtractor.start(new WaveformExtractor.OnProgressListener() {
@Override
public void onProgress(int progress) {
HashMap<String, Object> args = new HashMap();
args.put("progress", progress);
args.put("waveOutFile", waveOutPath);

invokeMethod("onProgress", args);
}

@Override
public void onComplete() {
handler.post(new Runnable() {
@Override
public void run() {
result.success(null);
}
});
}

@Override
public void onError(final String message) {
invokeMethod("onError", message);
handler.post(new Runnable() {
@Override
public void run() {
result.error(message, null, null);
}
});
}
});
break;
default:
result.notImplemented();
break;
}
}

Expand Down
2 changes: 1 addition & 1 deletion darwin/Classes/JustWaveformPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
progress = newProgress;
//NSLog(@"Progress: %d percent", progress);
dispatch_async(dispatch_get_main_queue(), ^{
[_channel invokeMethod:@"onProgress" arguments:@(progress)];
[_channel invokeMethod:@"onProgress" arguments:@{@"progress" : @(progress), @"waveOutFile" : waveOutPath}];
});
}
//NSLog(@"pixel[%d] %d: %d\t%d", scaledSampleIdx - 2, sampleIdx, minSample, maxSample);
Expand Down
33 changes: 17 additions & 16 deletions lib/just_waveform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,43 @@ import 'package:flutter/services.dart';
/// A utility for extracting a [Waveform] from an audio file suitable for visual
/// rendering.
class JustWaveform {
static const MethodChannel _channel =
MethodChannel('com.ryanheise.just_waveform');
static final MethodChannel _channel = MethodChannel('com.ryanheise.just_waveform');

/// Extract a [Waveform] from [audioInFile] and write it to [waveOutFile] at
/// the specified [zoom] level.
// XXX: It would be better to return a stream of the actual [Waveform], with
// progress => wave.data.length / (wave.length*2)
static Map<String, StreamController<WaveformProgress>> _map = {};
static Stream<WaveformProgress> extract({
required File audioInFile,
required File waveOutFile,
WaveformZoom zoom = const WaveformZoom.pixelsPerSecond(100),
}) {
final progressController = StreamController<WaveformProgress>.broadcast();
_map[waveOutFile.path] = progressController;

progressController.add(WaveformProgress._(0.0, null));
_channel.setMethodCallHandler((MethodCall call) async {
switch (call.method) {
case 'onProgress':
// ignore: avoid_print
print("received onProgress: ${call.arguments}}");
int progress = call.arguments;
//print("_progressSubject.add($progress)");
final args = call.arguments;
int progress = args['progress'];
String file = args['waveOutFile'];
Waveform? waveform;

if (progress == 100) {
waveform = await parse(waveOutFile);
waveform = await parse(File(file));
}
progressController.add(WaveformProgress._(progress / 100, waveform));

_map[file]?.add(WaveformProgress._(progress / 100, waveform));
if (progress == 100) {
progressController.close();
_map[file]?.close();
_map.remove(file);
}
break;
}
});
// print('Started extract $_filename');
_channel.invokeMethod('extract', {
'audioInPath': audioInFile.path,
'waveOutPath': waveOutFile.path,
Expand All @@ -54,9 +59,7 @@ class JustWaveform {
const headerLength = 20;
final header = Uint32List.view(bytes, 0, headerLength);
final flags = header[1];
final data = flags == 0
? Int16List.view(bytes, headerLength ~/ 2)
: Int8List.view(bytes, headerLength);
final data = flags == 0 ? Int16List.view(bytes, headerLength ~/ 2) : Int8List.view(bytes, headerLength);
return Waveform(
version: header[0],
flags: flags,
Expand Down Expand Up @@ -123,14 +126,12 @@ class Waveform {
int getPixelMax(int i) => this[2 * i + 1];

/// The duration of audio, inferred from the length of the waveform data.
Duration get duration => Duration(
microseconds: 1000 * 1000 * length * samplesPerPixel ~/ sampleRate);
Duration get duration => Duration(microseconds: 1000 * 1000 * length * samplesPerPixel ~/ sampleRate);

/// Converts an audio position to a pixel position. The returned position is a
/// [double] for accuracy, but can be converted `toInt` and used to access the
/// nearest pixel value via [getPixelMin]/[getPixelMax].
double positionToPixel(Duration position) =>
position.inMicroseconds * sampleRate / (samplesPerPixel * 1000000);
double positionToPixel(Duration position) => position.inMicroseconds * sampleRate / (samplesPerPixel * 1000000);
}

/// The resolution at which a [Waveform] should be generated.
Expand Down