From ca55d0f646d032fc57846cf5ff848730f0448faf Mon Sep 17 00:00:00 2001 From: Roland Volskaya Date: Tue, 15 Feb 2022 12:18:29 +0200 Subject: [PATCH 1/3] Add error handling to the method call handler --- lib/just_waveform.dart | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/just_waveform.dart b/lib/just_waveform.dart index c609ffa..9f31a58 100644 --- a/lib/just_waveform.dart +++ b/lib/just_waveform.dart @@ -22,21 +22,28 @@ class JustWaveform { final progressController = StreamController.broadcast(); 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)"); - Waveform? waveform; - if (progress == 100) { - waveform = await parse(waveOutFile); - } - progressController.add(WaveformProgress._(progress / 100, waveform)); - if (progress == 100) { - progressController.close(); - } - break; + if (progressController.isClosed) return; + int progress = call.arguments; + Waveform? waveform; + try { + switch (call.method) { + case 'onProgress': + if (progress == 100) { + waveform = await parse(waveOutFile); + } + progressController + .add(WaveformProgress._(progress / 100, waveform)); + if (progress == 100) { + progressController.close(); + } + break; + } + } on RangeError catch (_) { + // This one is expected, if the waveform file is too short. + progressController.add(WaveformProgress._(progress / 100, waveform)); + progressController.close(); + } catch (e, t) { + progressController.addError(e, t); } }); _channel.invokeMethod('extract', { From 62e20b63c4df02206cb8fa3f1d81b50d132d768c Mon Sep 17 00:00:00 2001 From: Roland Volskaya Date: Tue, 15 Feb 2022 12:18:39 +0200 Subject: [PATCH 2/3] Send the last "100" progress event after the waveform file is written --- .../com/ryanheise/just_waveform/WaveformExtractor.java | 7 ++++--- darwin/Classes/JustWaveformPlugin.m | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java b/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java index a7140ad..3554cc7 100644 --- a/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java +++ b/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java @@ -228,8 +228,6 @@ void processAudio() { frameCount++; // not really frame count anymore } - onProgressListener.onProgress(100); - onProgressListener.onComplete(); System.out.println("End. (" + presentationTime/1000000.0 + "sec) frameCount = " + frameCount + ", totalSampleSize = " + totalSampleSize); System.out.println("waitingToDecode: " + waitingToDecode); System.out.println("waitingForDecoded: " + waitingForDecoded); @@ -239,7 +237,7 @@ void processAudio() { try (FileOutputStream fout = new FileOutputStream(new File(wavePath))) { FileChannel channel = fout.getChannel(); int waveHeaderLength = 20; // in bytes - int waveHeaderLengthInShorts = waveHeaderLength / 2; // in shorts + int waveHeaderLengthInShorts = waveHeaderLength / 2; // in shorts ByteBuffer waveHeaderBytes = ByteBuffer.allocate(waveHeaderLength); waveHeaderBytes.order(ByteOrder.LITTLE_ENDIAN); IntBuffer waveHeader = waveHeaderBytes.asIntBuffer(); @@ -257,6 +255,9 @@ void processAudio() { channel.write(scaledByteSamples); System.out.println("Total scaled samples: " + scaledSampleIdx); } + + onProgressListener.onProgress(100); + onProgressListener.onComplete(); } catch (Exception e) { e.printStackTrace(); diff --git a/darwin/Classes/JustWaveformPlugin.m b/darwin/Classes/JustWaveformPlugin.m index 06d5a8e..5f61bc3 100644 --- a/darwin/Classes/JustWaveformPlugin.m +++ b/darwin/Classes/JustWaveformPlugin.m @@ -121,7 +121,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { UInt32 scaledSampleIdx = 0; int progress = 0; - while (frameCount > 0) { + while (frameCount > 0 && progress < 100) { status = ExtAudioFileRead(audioFileRef, &frameCount, &convertedData); if (status != noErr) { NSLog(@"ExtAudioFileRead error: %i", status); @@ -154,6 +154,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { int newProgress = (int)(100 * scaledSampleIdx / waveLength); if (newProgress != progress && newProgress <= 100) { progress = newProgress; + if (progress >= 100) break; //NSLog(@"Progress: %d percent", progress); dispatch_async(dispatch_get_main_queue(), ^{ [_channel invokeMethod:@"onProgress" arguments:@(progress)]; @@ -183,6 +184,10 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { status = ExtAudioFileDispose(audioFileRef); + dispatch_async(dispatch_get_main_queue(), ^{ + [_channel invokeMethod:@"onProgress" arguments:@(100)]; + }); + dispatch_async(dispatch_get_main_queue(), ^{ result(nil); }); From 68208c2bb87c25ec6ad65bd3c93bb71fad7081e3 Mon Sep 17 00:00:00 2001 From: Ryan Heise Date: Tue, 16 May 2023 10:50:07 +1000 Subject: [PATCH 3/3] Update to flutter 3.0, AGP 7.3.0. --- CHANGELOG.md | 6 ++++++ analysis_options.yaml | 12 ++++++++++-- android/build.gradle | 8 ++++++-- android/gradle/wrapper/gradle-wrapper.properties | 2 +- .../just_waveform/JustWaveformPlugin.java | 12 +++++++----- .../ryanheise/just_waveform/WaveformExtractor.java | 14 +++++++------- example/android/app/build.gradle | 3 ++- example/android/app/src/main/AndroidManifest.xml | 9 --------- example/android/build.gradle | 11 +++++++++-- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/lib/main.dart | 4 ++-- lib/just_waveform.dart | 4 ++-- pubspec.yaml | 11 +++++++---- 13 files changed, 60 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2035662..8197865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.0.5 + +* Fix timing of progress completion events (@volskaya). +* Update minimum flutter version to 3.0. +* Update AGP to 7.3.0. + ## 0.0.4 * Allow extracting multiple files simultaneously (@AlexSmirnov9107). diff --git a/analysis_options.yaml b/analysis_options.yaml index a5744c1..46c8448 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,12 @@ include: package:flutter_lints/flutter.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + +linter: + rules: + prefer_single_quotes: false + unawaited_futures: false diff --git a/android/build.gradle b/android/build.gradle index f6a7061..5a4b208 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.3.0' } } @@ -22,7 +22,11 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + // Conditional for compatibility with AGP <4.2. + if (project.android.hasProperty("namespace")) { + namespace 'com.ryanheise.just_waveform' + } + compileSdkVersion 33 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c9d085..3c472b9 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/android/src/main/java/com/ryanheise/just_waveform/JustWaveformPlugin.java b/android/src/main/java/com/ryanheise/just_waveform/JustWaveformPlugin.java index 9869dfa..a5acdd4 100644 --- a/android/src/main/java/com/ryanheise/just_waveform/JustWaveformPlugin.java +++ b/android/src/main/java/com/ryanheise/just_waveform/JustWaveformPlugin.java @@ -3,23 +3,25 @@ import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import android.os.Handler; +import android.os.Looper; import java.util.List; -import io.flutter.plugin.common.PluginRegistry.Registrar; import java.util.HashMap; /** JustWaveformPlugin */ public class JustWaveformPlugin implements FlutterPlugin, MethodCallHandler { private MethodChannel channel; - private Handler handler = new Handler(); + private Handler handler = new Handler(Looper.getMainLooper()); @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "com.ryanheise.just_waveform"); + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + BinaryMessenger messenger = binding.getBinaryMessenger(); + channel = new MethodChannel(messenger, "com.ryanheise.just_waveform"); channel.setMethodCallHandler(this); } @@ -35,7 +37,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) waveformExtractor.start(new WaveformExtractor.OnProgressListener() { @Override public void onProgress(int progress) { - HashMap args = new HashMap(); + HashMap args = new HashMap<>(); args.put("progress", progress); args.put("waveOutFile", waveOutPath); diff --git a/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java b/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java index ff2bdc4..79dec85 100644 --- a/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java +++ b/android/src/main/java/com/ryanheise/just_waveform/WaveformExtractor.java @@ -19,7 +19,7 @@ public class WaveformExtractor { private static final int TIMEOUT = 5000; - private static final int MAX_SAMPLE_SIZE = 256 * 1024; + //private static final int MAX_SAMPLE_SIZE = 256 * 1024; private String inPath; private String wavePath; @@ -65,7 +65,7 @@ public void run() { extractor.setDataSource(inPath); inFormat = selectAudioTrack(extractor); - int trackCount = extractor.getTrackCount(); + //int trackCount = extractor.getTrackCount(); //System.out.println("extractor format = " + inFormat); inMime = inFormat.getString(MediaFormat.KEY_MIME); processAudio(); @@ -85,17 +85,17 @@ void processAudio() { int sampleRate = inFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); //System.out.println("sample rate = " + sampleRate); long duration = inFormat.getLong(MediaFormat.KEY_DURATION); - int durationMs = (int)(duration/1000); + //int durationMs = (int)(duration/1000); long expectedSampleCount = duration * sampleRate / 1000000; // If we hear 2 stereo samples at the same time, we count that as 1 sample here. //System.out.println("expected sample count = " + expectedSampleCount); boolean sawInputEOS = false; int decoderIdleCount = 0; - int bufferSize = MAX_SAMPLE_SIZE; + //int bufferSize = MAX_SAMPLE_SIZE; int frameCount = 0; - int offset = 100; + //int offset = 100; - ByteBuffer buffer = ByteBuffer.allocate(bufferSize); + //ByteBuffer buffer = ByteBuffer.allocate(bufferSize); BufferInfo bufferInfo = new BufferInfo(); // For the wave @@ -126,7 +126,7 @@ void processAudio() { int waitingToDecode = 0; int waitingForDecoded = 0; long presentationTime = 0L; - long stopwatchStart = System.currentTimeMillis(); + //long stopwatchStart = System.currentTimeMillis(); decoder = MediaCodec.createDecoderByType(inMime); decoder.configure(inFormat, null, null, 0); decoder.start(); diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 88f9cae..f278c21 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -25,7 +25,8 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + namespace 'com.ryanheise.just_waveform_example' + compileSdkVersion 33 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index bdd5f7b..03a29dc 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -21,15 +21,6 @@ android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> - - diff --git a/example/android/build.gradle b/example/android/build.gradle index 622ddc5..4f5bdd8 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.3.0' } } @@ -14,6 +14,13 @@ allprojects { google() mavenCentral() } + + gradle.projectsEvaluated{ + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:deprecation" + options.compilerArgs << "-Xlint:unchecked" + } + } } rootProject.buildDir = '../build' @@ -22,6 +29,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..6b66533 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/example/lib/main.dart b/example/lib/main.dart index ab1bf85..78b7ca0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -68,7 +68,7 @@ class _MyAppState extends State { return Center( child: Text( 'Error: ${snapshot.error}', - style: Theme.of(context).textTheme.headline6, + style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), ); @@ -79,7 +79,7 @@ class _MyAppState extends State { return Center( child: Text( '${(100 * progress).toInt()}%', - style: Theme.of(context).textTheme.headline6, + style: Theme.of(context).textTheme.titleLarge, ), ); } diff --git a/lib/just_waveform.dart b/lib/just_waveform.dart index bea3814..46bb549 100644 --- a/lib/just_waveform.dart +++ b/lib/just_waveform.dart @@ -12,8 +12,8 @@ class JustWaveform { switch (call.method) { case 'onProgress': final args = call.arguments; - int progress = args['progress']; - String waveOutFilePath = args['waveOutFile']; + int progress = args['progress'] as int; + String waveOutFilePath = args['waveOutFile'] as String; final progressController = _progressControllers[waveOutFilePath]; if (progressController == null) break; if (progressController.isClosed) break; diff --git a/pubspec.yaml b/pubspec.yaml index 00383a7..acd9284 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,14 @@ name: just_waveform description: Extracts waveform data from an audio file suitable for visually rendering the waveform. -version: 0.0.4 +version: 0.0.5 homepage: https://github.com/ryanheise/just_waveform +topics: + - audio + - waveform environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + sdk: ">=2.14.0 <4.0.0" + flutter: ">=3.0.0" dependencies: flutter: @@ -16,7 +19,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^2.0.1 flutter: plugin: