diff --git a/analysis_options.yaml b/analysis_options.yaml index f49fa824..31674fe9 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1 +1,4 @@ -include: package:pedantic/analysis_options.1.11.0.yaml \ No newline at end of file +include: package:flutter_lints/flutter.yaml +analyzer: + errors: + constant_identifier_names: ignore diff --git a/android/build.gradle b/android/build.gradle index 9941b80a..2538ee2b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -4,22 +4,23 @@ group 'com.michaeljperri.flutter_sequencer' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.5.21' + ext.kotlin_version = '1.9.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' + classpath 'com.android.tools.build:gradle:8.6.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.eclipse.jgit:org.eclipse.jgit:5.12.0+" + classpath "org.eclipse.jgit:org.eclipse.jgit:7.0.0.202409031743-r" } } rootProject.allprojects { repositories { google() + mavenCentral() } } @@ -27,30 +28,47 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 29 + namespace "com.michaeljperri.flutter_sequencer" + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + compileSdk = 35 + ndkVersion = "27.0.12077973" sourceSets { main.java.srcDirs += 'src/main/kotlin' } + defaultConfig { - minSdkVersion 16 + minSdk 23 externalNativeBuild { cmake { - cppFlags '-std=c++17', '-frelaxed-template-template-args' + arguments "-DCMAKE_CXX_STANDARD=17", "-DCMAKE_CXX_STANDARD_REQUIRED=ON" + cppFlags '-frelaxed-template-template-args' } } + + ndk { + abiFilters "arm64-v8a" + } } lintOptions { disable 'InvalidPackage' } - ndkVersion "22.1.7171670" + externalNativeBuild { cmake { - version "3.18.1" path "CMakeLists.txt" } } + } void cloneThirdPartyRepo(String name, String uri, String commit) { @@ -60,12 +78,12 @@ void cloneThirdPartyRepo(String name, String uri, String commit) { if (!dir.exists()) { dir.mkdirs() def repo = - Git.cloneRepository() - .setDirectory(dir) - .setURI(uri) - .setRemote('origin') - .setCloneSubmodules(true) - .call() + Git.cloneRepository() + .setDirectory(dir) + .setURI(uri) + .setRemote('origin') + .setCloneSubmodules(true) + .call() repo.checkout().setName(commit).call() repo.submoduleUpdate() @@ -74,13 +92,9 @@ void cloneThirdPartyRepo(String name, String uri, String commit) { } task cloneThirdPartyRepos { - cloneThirdPartyRepo('oboe', 'https://github.com/google/oboe', '06ec23e4f6bc00ba7eea9b84e299f9200a598838') - cloneThirdPartyRepo('TinySoundFont', 'https://github.com/schellingb/TinySoundFont.git', 'bf574519e601202c3a9d27a74f345921277eed39') - cloneThirdPartyRepo('sfizz', 'https://github.com/sfztools/sfizz.git', 'fc1f0451cebd8996992cbc4f983fcf76b03295c5') + cloneThirdPartyRepo('oboe', 'https://github.com/google/oboe', 'e40043061cc94df965fda802938b0989f201e86c') + cloneThirdPartyRepo('TinySoundFont', 'https://github.com/schellingb/TinySoundFont.git', '790a219810cb0fca5defa8cdbd88e2487e5efc7a') + cloneThirdPartyRepo('sfizz', 'https://github.com/PROGrand/sfizz.git', 'a5cacdb0abdcabfd3ff1d302d7a2e8784627e340') } preBuild.dependsOn cloneThirdPartyRepos - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c9d0852..53aec703 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-8.14.2-all.zip diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 91156f8a..d072470e 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,3 @@ - + diff --git a/android/src/main/kotlin/com/michaeljperri/flutter_sequencer/FlutterSequencerPlugin.kt b/android/src/main/kotlin/com/michaeljperri/flutter_sequencer/FlutterSequencerPlugin.kt index 8661e275..ecc6be56 100644 --- a/android/src/main/kotlin/com/michaeljperri/flutter_sequencer/FlutterSequencerPlugin.kt +++ b/android/src/main/kotlin/com/michaeljperri/flutter_sequencer/FlutterSequencerPlugin.kt @@ -1,130 +1,109 @@ package com.michaeljperri.flutter_sequencer import android.content.Context -import android.content.res.AssetManager; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat - +import android.content.res.AssetManager import io.flutter.embedding.engine.plugins.FlutterPlugin 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 io.flutter.plugin.common.PluginRegistry.Registrar import java.io.File -import java.io.FileInputStream import java.io.FileOutputStream import java.net.URLDecoder const val flutterAssetRoot = "flutter_assets" /** FlutterSequencerPlugin */ -public class FlutterSequencerPlugin: FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel : MethodChannel - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_sequencer") - channel.setMethodCallHandler(this); - context = flutterPluginBinding.applicationContext - } - - // This static function is optional and equivalent to onAttachedToEngine. It supports the old - // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting - // plugin registration via this function while apps migrate to use the new Android APIs - // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. - // - // It is encouraged to share logic between onAttachedToEngine and registerWith to keep - // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called - // depending on the user's project. onAttachedToEngine or registerWith must both be defined - // in the same class. - companion object { - private lateinit var context : Context - - @JvmStatic - fun registerWith(registrar: Registrar) { - val channel = MethodChannel(registrar.messenger(), "flutter_sequencer") - channel.setMethodCallHandler(FlutterSequencerPlugin()) - context = registrar.context() +class FlutterSequencerPlugin : FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel: MethodChannel + + private lateinit var context: Context + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_sequencer") + channel.setMethodCallHandler(this) + context = flutterPluginBinding.applicationContext } - init { - System.loadLibrary("flutter_sequencer") + companion object { + init { + System.loadLibrary("flutter_sequencer") + } } - } - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - if (call.method == "getPlatformVersion") { - result.success("Android ${android.os.Build.VERSION.RELEASE}") - } else if (call.method == "setupAssetManager") { - setupAssetManager(context.assets) - result.success(null) - } else if (call.method == "normalizeAssetDir") { - val assetDir = call.argument("assetDir")!! - val filesDir = context.filesDir - val isSuccess = copyAssetDirOrFile(assetDir, filesDir) - - if (isSuccess) { - val copiedDir = filesDir.resolve(assetDir).absolutePath - result.success(copiedDir) - } else { - result.success(null) - } - } else if (call.method == "listAudioUnits") { - result.success(emptyList()) - } else { - result.notImplemented() + + override fun onMethodCall(call: MethodCall, result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else if (call.method == "setupAssetManager") { + setupAssetManager(context.assets) + result.success(null) + } else if (call.method == "normalizeAssetDir") { + val assetDir = call.argument("assetDir")!! + val filesDir = context.filesDir + val isSuccess = copyAssetDirOrFile(assetDir, filesDir) + + if (isSuccess) { + val copiedDir = filesDir.resolve(assetDir).absolutePath + result.success(copiedDir) + } else { + result.success(null) + } + } else if (call.method == "listAudioUnits") { + result.success(emptyList()) + } else { + result.notImplemented() + } } - } - - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } - - private fun copyAssetFile(assetFilePath: String, outputDir: File): Boolean { - val inputStream = context.assets.open("$flutterAssetRoot/$assetFilePath") - val decodedAssetFilePath = URLDecoder.decode(assetFilePath, "UTF-8") - val outputFile = outputDir.resolve(decodedAssetFilePath) - var outputStream: FileOutputStream? = null - - try { - outputFile.parentFile.mkdirs() - outputFile.createNewFile() - - outputStream = FileOutputStream(outputFile) - inputStream.copyTo(outputStream, 1024) - } catch (e: SecurityException) { - return false; - } catch (e: java.io.IOException) { - return false; - } finally { - inputStream.close() - outputStream?.flush() - outputStream?.close() + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) } - return true; - } - - private fun copyAssetDirOrFile(assetPath: String, outputDir: File): Boolean { - val paths = context.assets.list("$flutterAssetRoot/$assetPath")!! - var isSuccess = true; - - if (paths.isEmpty()) { - // It's a file. - isSuccess = isSuccess && copyAssetFile(assetPath, outputDir) - } else { - // It's a directory. - paths.forEach { - isSuccess = isSuccess && copyAssetDirOrFile("$assetPath/$it", outputDir) - } + private fun copyAssetFile(assetFilePath: String, outputDir: File): Boolean { + val inputStream = context.assets.open("$flutterAssetRoot/$assetFilePath") + val decodedAssetFilePath = URLDecoder.decode(assetFilePath, "UTF-8") + val outputFile = outputDir.resolve(decodedAssetFilePath) + var outputStream: FileOutputStream? = null + + try { + outputFile.parentFile!!.mkdirs() + outputFile.createNewFile() + + outputStream = FileOutputStream(outputFile) + inputStream.copyTo(outputStream, 1024) + } catch (e: SecurityException) { + return false + } catch (e: java.io.IOException) { + return false + } finally { + inputStream.close() + outputStream?.flush() + outputStream?.close() + } + + return true } - return isSuccess - } + private fun copyAssetDirOrFile(assetPath: String, outputDir: File): Boolean { + val paths = context.assets.list("$flutterAssetRoot/$assetPath")!! + var isSuccess = true + + if (paths.isEmpty()) { + // It's a file. + isSuccess = isSuccess && copyAssetFile(assetPath, outputDir) + } else { + // It's a directory. + paths.forEach { + isSuccess = isSuccess && copyAssetDirOrFile("$assetPath/$it", outputDir) + } + } + + return isSuccess + } - private external fun setupAssetManager(assetManager: AssetManager) + private external fun setupAssetManager(assetManager: AssetManager) } diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index e69de29b..31674fe9 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml +analyzer: + errors: + constant_identifier_names: ignore diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index c98e3289..00000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,63 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 29 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.michaeljperri.flutter_sequencer_example" - minSdkVersion 16 - targetSdkVersion 29 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts new file mode 100644 index 00000000..64dbe25e --- /dev/null +++ b/example/android/app/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("dev.flutter.flutter-gradle-plugin") +} +android { + namespace = "com.michaeljperri.flutter_sequencer_example" + compileSdk = 35 + ndkVersion = "27.0.12077973" + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + + defaultConfig { + applicationId = "com.michaeljperri.flutter_sequencer_example" + minSdk = 25 + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + + ndk { + abiFilters.add("arm64-v8a") + } + } + + buildTypes { + release { + signingConfig = signingConfigs.getByName("debug") + } + } + + signingConfigs { + getByName("debug") { + } + } +} + +flutter { + source = "../.." +} diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index ed4504e8..cf26a91e 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.michaeljperri.flutter_sequencer_example"> + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" + /> + android:name="io.flutter.embedding.android.SplashScreenDrawable" + android:resource="@drawable/launch_background" + /> @@ -44,7 +45,7 @@ + android:name="flutterEmbedding" + android:value="2"/> diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index f797cfb7..00000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.5.21' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.0.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - jcenter() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/example/android/build.gradle.kts b/example/android/build.gradle.kts new file mode 100644 index 00000000..89176ef4 --- /dev/null +++ b/example/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 596278c4..d5173e0b 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-7.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index d3b6a401..00000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts new file mode 100644 index 00000000..cb92a563 --- /dev/null +++ b/example/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.10.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.21" apply false +} + +include(":app") diff --git a/example/android/settings_aar.gradle b/example/android/settings_aar.gradle deleted file mode 100644 index e7b4def4..00000000 --- a/example/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/example/lib/components/drum_machine/drum_machine.dart b/example/lib/components/drum_machine/drum_machine.dart index dacc72b6..dfae560c 100644 --- a/example/lib/components/drum_machine/drum_machine.dart +++ b/example/lib/components/drum_machine/drum_machine.dart @@ -1,15 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_sequencer/track.dart'; - import 'package:flutter_sequencer_example/models/step_sequencer_state.dart'; -import 'volume_slider.dart'; import 'grid/grid.dart'; +import 'volume_slider.dart'; class DrumMachineWidget extends StatefulWidget { const DrumMachineWidget({ - Key? key, + super.key, required this.track, required this.stepCount, required this.currentStep, @@ -19,7 +18,7 @@ class DrumMachineWidget extends StatefulWidget { required this.stepSequencerState, required this.handleVolumeChange, required this.handleVelocitiesChange, - }) : super(key: key); + }); final Track track; final int stepCount; @@ -32,26 +31,18 @@ class DrumMachineWidget extends StatefulWidget { final Function(int, int, int, double) handleVelocitiesChange; @override - _DrumMachineWidgetState createState() => _DrumMachineWidgetState(); + State createState() => _DrumMachineWidgetState(); } -class _DrumMachineWidgetState extends State - with SingleTickerProviderStateMixin { +class _DrumMachineWidgetState extends State with SingleTickerProviderStateMixin { Ticker? ticker; - @override - void dispose() { - super.dispose(); - } - double? getVelocity(int step, int col) { - return widget.stepSequencerState! - .getVelocity(step, widget.columnPitches[col]); + return widget.stepSequencerState!.getVelocity(step, widget.columnPitches[col]); } void handleVelocityChange(int col, int step, double velocity) { - widget.handleVelocitiesChange( - widget.track.id, step, widget.columnPitches[col], velocity); + widget.handleVelocitiesChange(widget.track.id, step, widget.columnPitches[col], velocity); } void handleVolumeChange(double nextVolume) { @@ -59,8 +50,7 @@ class _DrumMachineWidgetState extends State } void handleNoteOn(int col) { - widget.track - .startNoteNow(noteNumber: widget.columnPitches[col], velocity: .75); + widget.track.startNoteNow(noteNumber: widget.columnPitches[col], velocity: .75); } void handleNoteOff(int col) { @@ -70,22 +60,26 @@ class _DrumMachineWidgetState extends State @override Widget build(BuildContext context) { return Expanded( - child: Container( - padding: EdgeInsets.fromLTRB(32, 16, 32, 0), - decoration: BoxDecoration( - color: Colors.black54, + child: Container( + padding: EdgeInsets.fromLTRB(32, 16, 32, 0), + decoration: BoxDecoration(color: Colors.black54), + child: Column( + children: [ + VolumeSlider(value: widget.volume, onChange: handleVolumeChange), + Expanded( + child: Grid( + columnLabels: widget.rowLabels, + getVelocity: getVelocity, + stepCount: widget.stepCount, + currentStep: widget.currentStep, + onChange: handleVelocityChange, + onNoteOn: handleNoteOn, + onNoteOff: handleNoteOff, + ), ), - child: Column(children: [ - VolumeSlider(value: widget.volume, onChange: handleVolumeChange), - Expanded( - child: Grid( - columnLabels: widget.rowLabels, - getVelocity: getVelocity, - stepCount: widget.stepCount, - currentStep: widget.currentStep, - onChange: handleVelocityChange, - onNoteOn: handleNoteOn, - onNoteOff: handleNoteOff)), - ]))); + ], + ), + ), + ); } } diff --git a/example/lib/components/drum_machine/grid/cell.dart b/example/lib/components/drum_machine/grid/cell.dart index 94acfcf5..062c69ba 100644 --- a/example/lib/components/drum_machine/grid/cell.dart +++ b/example/lib/components/drum_machine/grid/cell.dart @@ -1,15 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_sequencer_example/constants.dart'; class Cell extends StatelessWidget { - Cell({ - Key? key, + const Cell({ + super.key, required this.size, required this.velocity, required this.isCurrentStep, required this.onChange, - }) : super(key: key); + }); final double size; final double velocity; @@ -22,17 +21,22 @@ class Cell extends StatelessWidget { width: size, height: size, decoration: BoxDecoration( - color: Color.lerp(isCurrentStep ? Colors.white30 : Colors.black, - isCurrentStep ? Colors.blue : Colors.pink, velocity), + color: Color.lerp( + isCurrentStep ? Colors.white30 : Colors.black, + isCurrentStep ? Colors.blue : Colors.pink, + velocity, + ), border: Border.all(color: Colors.white70), ), child: Transform( - transform: - Matrix4.translationValues(0, (-1 * size * velocity) + 2, 0), - child: Container( - width: size, - decoration: const BoxDecoration( - border: Border(bottom: BorderSide(color: Colors.white))))), + transform: Matrix4.translationValues(0, (-1 * size * velocity) + 2, 0), + child: Container( + width: size, + decoration: const BoxDecoration( + border: Border(bottom: BorderSide(color: Colors.white)), + ), + ), + ), ); return GestureDetector( diff --git a/example/lib/components/drum_machine/grid/grid.dart b/example/lib/components/drum_machine/grid/grid.dart index 51bd4409..3a935076 100644 --- a/example/lib/components/drum_machine/grid/grid.dart +++ b/example/lib/components/drum_machine/grid/grid.dart @@ -6,8 +6,8 @@ import 'package:flutter/widgets.dart'; import 'cell.dart'; class Grid extends StatelessWidget { - Grid({ - Key? key, + const Grid({ + super.key, required this.getVelocity, required this.columnLabels, required this.stepCount, @@ -15,7 +15,7 @@ class Grid extends StatelessWidget { required this.onChange, required this.onNoteOn, required this.onNoteOff, - }) : super(key: key); + }); final Function(int step, int col) getVelocity; final List columnLabels; @@ -33,26 +33,27 @@ class Grid extends StatelessWidget { final cellSize = min(constraints.maxWidth / columnLabels.length, 50.0); return ListView.builder( - padding: EdgeInsets.fromLTRB(0, 0, 0, 16), - shrinkWrap: true, - itemCount: stepCount, - itemBuilder: (BuildContext context, int step) { - final List cellWidgets = []; - - for (var col = 0; col < columnsCount; col++) { - final velocity = getVelocity(step, col); - - final cellWidget = Cell( - size: cellSize, - velocity: velocity, - isCurrentStep: step == currentStep, - onChange: (velocity) => onChange(col, step, velocity), - ); - - cellWidgets.add(cellWidget); - } - return Row(children: cellWidgets); - }); + padding: EdgeInsets.fromLTRB(0, 0, 0, 16), + shrinkWrap: true, + itemCount: stepCount, + itemBuilder: (BuildContext context, int step) { + final List cellWidgets = []; + + for (var col = 0; col < columnsCount; col++) { + final velocity = getVelocity(step, col); + + final cellWidget = Cell( + size: cellSize, + velocity: velocity, + isCurrentStep: step == currentStep, + onChange: (velocity) => onChange(col, step, velocity), + ); + + cellWidgets.add(cellWidget); + } + return Row(children: cellWidgets); + }, + ); }, ); } diff --git a/example/lib/components/drum_machine/grid/label_row.dart b/example/lib/components/drum_machine/grid/label_row.dart index 8740b254..1f2dae72 100644 --- a/example/lib/components/drum_machine/grid/label_row.dart +++ b/example/lib/components/drum_machine/grid/label_row.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class LabelRow extends StatelessWidget { - LabelRow({ - Key? key, + const LabelRow({ + super.key, required this.columnLabels, required this.cellSize, required this.onNoteOn, required this.onNoteOff, - }) : super(key: key); + }); final List columnLabels; final Function(int) onNoteOn; diff --git a/example/lib/components/drum_machine/volume_slider.dart b/example/lib/components/drum_machine/volume_slider.dart index ecaccd60..c3159528 100644 --- a/example/lib/components/drum_machine/volume_slider.dart +++ b/example/lib/components/drum_machine/volume_slider.dart @@ -1,26 +1,19 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class VolumeSlider extends StatelessWidget { - VolumeSlider({ - Key? key, - required this.value, - required this.onChange, - }); + const VolumeSlider({super.key, required this.value, required this.onChange}); final double value; final Function(double) onChange; @override Widget build(BuildContext context) { - return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('Volume:'), - Slider( - min: 0, - max: 1, - value: value, - onChanged: onChange, - ), - ]); + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Volume:'), + Slider(min: 0, max: 1, value: value, onChanged: onChange), + ], + ); } } diff --git a/example/lib/components/position_view.dart b/example/lib/components/position_view.dart index 24bbc1d9..a1c9c67a 100644 --- a/example/lib/components/position_view.dart +++ b/example/lib/components/position_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/widgets.dart'; class PositionView extends StatelessWidget { - PositionView({required this.position}); + const PositionView({super.key, required this.position}); final double position; diff --git a/example/lib/components/step_count_selector.dart b/example/lib/components/step_count_selector.dart index 28327fbb..158ec171 100644 --- a/example/lib/components/step_count_selector.dart +++ b/example/lib/components/step_count_selector.dart @@ -1,21 +1,16 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class StepCountSelector extends StatelessWidget { - const StepCountSelector({ - Key? key, - required this.stepCount, - required this.onChange, - }) : super(key: key); + const StepCountSelector({super.key, required this.stepCount, required this.onChange}); final int stepCount; final Function(int) onChange; - handleLess() { + void handleLess() { onChange(stepCount - 1); } - handleMore() { + void handleMore() { onChange(stepCount + 1); } @@ -25,16 +20,9 @@ class StepCountSelector extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Steps'), - IconButton( - icon: Icon(Icons.arrow_back), - onPressed: handleLess, - ), - Text(stepCount.toString(), - style: TextStyle(fontWeight: FontWeight.bold)), - IconButton( - icon: Icon(Icons.arrow_forward), - onPressed: handleMore, - ), + IconButton(icon: Icon(Icons.arrow_back), onPressed: handleLess), + Text(stepCount.toString(), style: TextStyle(fontWeight: FontWeight.bold)), + IconButton(icon: Icon(Icons.arrow_forward), onPressed: handleMore), ], ); } diff --git a/example/lib/components/tempo_selector.dart b/example/lib/components/tempo_selector.dart index d3b6b81f..53cfa16a 100644 --- a/example/lib/components/tempo_selector.dart +++ b/example/lib/components/tempo_selector.dart @@ -1,18 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; class TempoSelector extends StatefulWidget { - TempoSelector({ - required this.selectedTempo, - required this.handleChange, - }); + TempoSelector({super.key, required this.selectedTempo, required this.handleChange}) { + // TODO: implement TempoSelector + // throw UnimplementedError(); + } final double selectedTempo; final Function(double nextTempo) handleChange; @override - _TempoSelectorState createState() => _TempoSelectorState(); + State createState() => _TempoSelectorState(); } class _TempoSelectorState extends State { @@ -30,11 +29,10 @@ class _TempoSelectorState extends State { @override void initState() { super.initState(); - controller = - TextEditingController(text: widget.selectedTempo.toInt().toString()); + controller = TextEditingController(text: widget.selectedTempo.toInt().toString()); } - handleTextChange(String input) { + void handleTextChange(String input) { final parsedValue = double.tryParse(input); if (parsedValue != null && parsedValue > 0) { @@ -50,23 +48,23 @@ class _TempoSelectorState extends State { @override Widget build(BuildContext context) { - return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - Container( - child: Text('Tempo:'), - margin: EdgeInsets.only(right: 16.0), - ), - Container( - width: 50, - height: 50, - child: TextField( - controller: controller, - maxLines: 1, - keyboardType: TextInputType.number, - onSubmitted: handleTextChange, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: InputDecoration(hintText: "..."), + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container(margin: EdgeInsets.only(right: 16.0), child: Text('Tempo:')), + SizedBox( + width: 50, + height: 50, + child: TextField( + controller: controller, + maxLines: 1, + keyboardType: TextInputType.number, + onSubmitted: handleTextChange, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: InputDecoration(hintText: "..."), + ), ), - ), - ]); + ], + ); } } diff --git a/example/lib/components/track_selector.dart b/example/lib/components/track_selector.dart index acb37685..84e25278 100644 --- a/example/lib/components/track_selector.dart +++ b/example/lib/components/track_selector.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_sequencer/track.dart'; class TrackSelector extends StatelessWidget { - TrackSelector({ + const TrackSelector({ + super.key, required this.selectedTrack, required this.tracks, required this.handleChange, @@ -16,11 +16,11 @@ class TrackSelector extends StatelessWidget { @override Widget build(BuildContext context) { return DropdownButton( - value: selectedTrack, - onChanged: handleChange, - items: tracks.map((track) { - return DropdownMenuItem( - value: track, child: Text(track.instrument.displayName)); - }).toList()); + value: selectedTrack, + onChanged: handleChange, + items: tracks.map((track) { + return DropdownMenuItem(value: track, child: Text(track.instrument.displayName)); + }).toList(), + ); } } diff --git a/example/lib/components/transport.dart b/example/lib/components/transport.dart index 80038271..38ededa0 100644 --- a/example/lib/components/transport.dart +++ b/example/lib/components/transport.dart @@ -1,15 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class Transport extends StatelessWidget { const Transport({ - Key? key, + super.key, required this.isPlaying, required this.isLooping, required this.onTogglePlayPause, required this.onStop, required this.onToggleLoop, - }) : super(key: key); + }); final bool isPlaying; final bool isLooping; @@ -23,14 +22,11 @@ class Transport extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - onPressed: onTogglePlayPause, - color: Colors.pink, - icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow)), - IconButton( - icon: Icon(Icons.stop), - onPressed: onStop, + onPressed: onTogglePlayPause, color: Colors.pink, + icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow), ), + IconButton(icon: Icon(Icons.stop), onPressed: onStop, color: Colors.pink), IconButton( icon: Icon(Icons.repeat), onPressed: onToggleLoop, diff --git a/example/lib/main.dart b/example/lib/main.dart index f43c6f48..e6c3f6ad 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_sequencer/global_state.dart'; -import 'package:flutter_sequencer/models/sfz.dart'; import 'package:flutter_sequencer/models/instrument.dart'; +import 'package:flutter_sequencer/models/sfz.dart'; import 'package:flutter_sequencer/sequence.dart'; import 'package:flutter_sequencer/track.dart'; @@ -12,22 +12,23 @@ import 'components/step_count_selector.dart'; import 'components/tempo_selector.dart'; import 'components/track_selector.dart'; import 'components/transport.dart'; +import 'constants.dart'; import 'models/project_state.dart'; import 'models/step_sequencer_state.dart'; -import 'constants.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { + const MyApp({super.key}); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State with SingleTickerProviderStateMixin { - final sequence = - Sequence(tempo: INITIAL_TEMPO, endBeat: INITIAL_STEP_COUNT.toDouble()); + final sequence = Sequence(tempo: INITIAL_TEMPO, endBeat: INITIAL_STEP_COUNT.toDouble()); Map trackStepSequencerStates = {}; List tracks = []; Map trackVolumes = {}; @@ -53,60 +54,70 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { tuningPath: "assets/sfz/meanquar.scl", ), RuntimeSfzInstrument( - id: "Sampled Synth", - sampleRoot: "assets/wav", - isAsset: true, - sfz: Sfz(groups: [ - SfzGroup(regions: [ - SfzRegion(sample: "D3.wav", key: 62), - SfzRegion(sample: "F3.wav", key: 65), - SfzRegion(sample: "Gsharp3.wav", key: 68), - ]) - ])), + id: "Sampled Synth", + sampleRoot: "assets/wav", + isAsset: true, + sfz: Sfz( + groups: [ + SfzGroup( + regions: [ + SfzRegion(sample: "D3.wav", key: 62), + SfzRegion(sample: "F3.wav", key: 65), + SfzRegion(sample: "Gsharp3.wav", key: 68), + ], + ), + ], + ), + ), RuntimeSfzInstrument( - id: "Generated Synth", - // This SFZ doesn't use any sample files, so just put "/" as a placeholder. - sampleRoot: "/", - isAsset: false, - // Based on the Unison Oscillator example here: - // https://sfz.tools/sfizz/quick_reference#unison-oscillator - sfz: Sfz(groups: [ - SfzGroup(regions: [ - SfzRegion(sample: "*saw", otherOpcodes: { - "oscillator_multi": "5", - "oscillator_detune": "50", - }) - ]) - ])), + id: "Generated Synth", + // This SFZ doesn't use any sample files, so just put "/" as a placeholder. + sampleRoot: "/", + isAsset: false, + // Based on the Unison Oscillator example here: + // https://sfz.tools/sfizz/quick_reference#unison-oscillator + sfz: Sfz( + groups: [ + SfzGroup( + regions: [ + SfzRegion( + sample: "*saw", + otherOpcodes: {"oscillator_multi": "5", "oscillator_detune": "50"}, + ), + ], + ), + ], + ), + ), ]; sequence.createTracks(instruments).then((tracks) { this.tracks = tracks; - tracks.forEach((track) { + for (final track in tracks) { trackVolumes[track.id] = 0.0; trackStepSequencerStates[track.id] = StepSequencerState(); - }); + } setState(() { - this.selectedTrack = tracks[0]; + selectedTrack = tracks[0]; }); }); - ticker = this.createTicker((Duration elapsed) { + ticker = createTicker((Duration elapsed) { setState(() { tempo = sequence.getTempo(); position = sequence.getBeat(); isPlaying = sequence.getIsPlaying(); - tracks.forEach((track) { + for (final track in tracks) { trackVolumes[track.id] = track.getVolume(); - }); + } }); }); ticker.start(); } - handleTogglePlayPause() { + void handleTogglePlayPause() { if (isPlaying) { sequence.pause(); } else { @@ -114,11 +125,11 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { } } - handleStop() { + void handleStop() { sequence.stop(); } - handleSetLoop(bool nextIsLooping) { + void handleSetLoop(bool nextIsLooping) { if (nextIsLooping) { sequence.setLoop(0, stepCount.toDouble()); } else { @@ -130,13 +141,13 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { }); } - handleToggleLoop() { + void handleToggleLoop() { final nextIsLooping = !isLooping; handleSetLoop(nextIsLooping); } - handleStepCountChange(int nextStepCount) { + void handleStepCountChange(int nextStepCount) { if (nextStepCount < 1) return; sequence.setEndBeat(nextStepCount.toDouble()); @@ -149,29 +160,30 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { setState(() { stepCount = nextStepCount; - tracks.forEach((track) => syncTrack(track)); + for (final track in tracks) { + syncTrack(track); + } }); } - handleTempoChange(double nextTempo) { + void handleTempoChange(double nextTempo) { if (nextTempo <= 0) return; sequence.setTempo(nextTempo); } - handleTrackChange(Track? nextTrack) { + void handleTrackChange(Track? nextTrack) { setState(() { selectedTrack = nextTrack; }); } - handleVolumeChange(double nextVolume) { + void handleVolumeChange(double nextVolume) { if (selectedTrack != null) { selectedTrack!.changeVolumeNow(volume: nextVolume); } } - handleVelocitiesChange( - int trackId, int step, int noteNumber, double velocity) { + void handleVelocitiesChange(int trackId, int step, int noteNumber, double velocity) { final track = tracks.firstWhere((track) => track.id == trackId); trackStepSequencerStates[trackId]!.setVelocity(step, noteNumber, velocity); @@ -179,22 +191,22 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { syncTrack(track); } - syncTrack(track) { + void syncTrack(Track track) { track.clearEvents(); - trackStepSequencerStates[track.id]! - .iterateEvents((step, noteNumber, velocity) { + trackStepSequencerStates[track.id]!.iterateEvents((step, noteNumber, velocity) { if (step < stepCount) { track.addNote( - noteNumber: noteNumber, - velocity: velocity, - startBeat: step.toDouble(), - durationBeats: 1.0); + noteNumber: noteNumber, + velocity: velocity, + startBeat: step.toDouble(), + durationBeats: 1.0, + ); } }); track.syncBuffer(); } - loadProjectState(ProjectState projectState) { + void loadProjectState(ProjectState projectState) { handleStop(); trackStepSequencerStates[tracks[0].id] = projectState.drumState; @@ -209,11 +221,11 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { tracks.forEach(syncTrack); } - handleReset() { + void handleReset() { loadProjectState(ProjectState.empty()); } - handleLoadDemo() { + void handleLoadDemo() { loadProjectState(ProjectState.demo()); } @@ -223,56 +235,53 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { final isDrumTrackSelected = selectedTrack == tracks[0]; return Center( - child: Column(children: [ - Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Transport( - isPlaying: isPlaying, - isLooping: isLooping, - onTogglePlayPause: handleTogglePlayPause, - onStop: handleStop, - onToggleLoop: handleToggleLoop, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Transport( + isPlaying: isPlaying, + isLooping: isLooping, + onTogglePlayPause: handleTogglePlayPause, + onStop: handleStop, + onToggleLoop: handleToggleLoop, + ), + PositionView(position: position), + ], ), - PositionView(position: position), - ]), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - StepCountSelector( - stepCount: stepCount, onChange: handleStepCountChange), - TempoSelector( - selectedTempo: tempo, - handleChange: handleTempoChange, - ), - ], - ), - TrackSelector( - tracks: tracks, - selectedTrack: selectedTrack, - handleChange: handleTrackChange, - ), - Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - MaterialButton( - child: Text('Reset'), - onPressed: handleReset, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + StepCountSelector(stepCount: stepCount, onChange: handleStepCountChange), + TempoSelector(selectedTempo: tempo, handleChange: handleTempoChange), + ], ), - MaterialButton( - child: Text('Load Demo'), - onPressed: handleLoadDemo, + TrackSelector( + tracks: tracks, + selectedTrack: selectedTrack, + handleChange: handleTrackChange, ), - ]), - DrumMachineWidget( - track: selectedTrack!, - stepCount: stepCount, - currentStep: position.floor(), - rowLabels: isDrumTrackSelected ? ROW_LABELS_DRUMS : ROW_LABELS_PIANO, - columnPitches: - isDrumTrackSelected ? ROW_PITCHES_DRUMS : ROW_PITCHES_PIANO, - volume: trackVolumes[selectedTrack!.id] ?? 0.0, - stepSequencerState: trackStepSequencerStates[selectedTrack!.id], - handleVolumeChange: handleVolumeChange, - handleVelocitiesChange: handleVelocitiesChange, - ), - ]), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MaterialButton(onPressed: handleReset, child: Text('Reset')), + MaterialButton(onPressed: handleLoadDemo, child: Text('Load Demo')), + ], + ), + DrumMachineWidget( + track: selectedTrack!, + stepCount: stepCount, + currentStep: position.floor(), + rowLabels: isDrumTrackSelected ? ROW_LABELS_DRUMS : ROW_LABELS_PIANO, + columnPitches: isDrumTrackSelected ? ROW_PITCHES_DRUMS : ROW_PITCHES_PIANO, + volume: trackVolumes[selectedTrack!.id] ?? 0.0, + stepSequencerState: trackStepSequencerStates[selectedTrack!.id], + handleVolumeChange: handleVolumeChange, + handleVelocitiesChange: handleVelocitiesChange, + ), + ], + ), ); } @@ -280,9 +289,9 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( - colorScheme: ColorScheme.dark(), - textTheme: - Theme.of(context).textTheme.apply(bodyColor: Colors.white)), + colorScheme: ColorScheme.dark(), + textTheme: Theme.of(context).textTheme.apply(bodyColor: Colors.white), + ), home: Scaffold( appBar: AppBar(title: const Text('Drum machine example')), body: _getMainView(), diff --git a/example/pubspec.lock b/example/pubspec.lock index e41ecfbc..63167100 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,164 +5,224 @@ packages: dependency: "direct main" description: name: async - url: "https://pub.dartlang.org" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.4.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "1.0.8" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.3" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "2.1.4" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_sequencer: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.4.3" + version: "0.4.4" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + url: "https://pub.dev" + source: hosted + version: "6.0.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" source: hosted - version: "0.12.11" + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.16.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" source: hosted - version: "0.4.3" - typed_data: + version: "0.7.4" + vector_math: dependency: transitive description: - name: typed_data - url: "https://pub.dartlang.org" + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "1.3.0" - vector_math: + version: "2.1.4" + vm_service: dependency: transitive description: - name: vector_math - url: "https://pub.dartlang.org" + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "15.0.0" sdks: - dart: ">=2.14.0 <3.0.0" - flutter: ">=1.10.0" + dart: ">=3.8.1 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 81ca7798..13884c4b 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,43 +1,27 @@ name: flutter_sequencer_example description: Demonstrates how to use the flutter_sequencer plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=3.8.1 <4.0.0' dependencies: flutter: sdk: flutter - async: "^2.4.1" + async: ^2.13.0 flutter_sequencer: - # When depending on this package from a real application you should use: - # flutter_sequencer: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. path: ../ - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 + cupertino_icons: ^1.0.8 dev_dependencies: flutter_test: sdk: flutter -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec + flutter_lints: ^6.0.0 -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true assets: @@ -45,29 +29,3 @@ flutter: - assets/sfz/samples/ - assets/sf2/ - assets/wav/ - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/pubspec_overrides.yaml b/example/pubspec_overrides.yaml new file mode 100644 index 00000000..3feb9e0f --- /dev/null +++ b/example/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: flutter_sequencer +dependency_overrides: + flutter_sequencer: + path: .. diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index bb9aa80b..b4754da2 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -5,23 +5,8 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_sequencer_example/main.dart'; - void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => - widget is Text && widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); + testWidgets('Verify Platform version', (WidgetTester tester) async {}); } diff --git a/lib/global_state.dart b/lib/global_state.dart index 63ef8021..b5237da8 100644 --- a/lib/global_state.dart +++ b/lib/global_state.dart @@ -76,9 +76,8 @@ class GlobalState { final shouldPlayEngine = !_getIsPlaying(); sequence.isPlaying = true; - sequence.engineStartFrame = LEAD_FRAMES + - NativeBridge.getPosition() - - sequence.beatToFrames(sequence.pauseBeat); + sequence.engineStartFrame = + LEAD_FRAMES + NativeBridge.getPosition() - sequence.beatToFrames(sequence.pauseBeat); _syncAllBuffers(); @@ -122,7 +121,9 @@ class GlobalState { void _setupEngine() async { sampleRate = await NativeBridge.doSetup(); isEngineReady = true; - onEngineReadyCallbacks.forEach((callback) => callback()); + for (final callback in onEngineReadyCallbacks) { + callback(); + } if (keepEngineRunning) { NativeBridge.play(); @@ -141,7 +142,9 @@ class GlobalState { _topOffTimer = Timer.periodic(Duration(milliseconds: 1000), (_) { _topOffAllBuffers(); - sequenceIdMap.values.forEach((sequence) => sequence.checkIsOver()); + for (final sequence in sequenceIdMap.values) { + sequence.checkIsOver(); + } }); } @@ -171,8 +174,7 @@ class GlobalState { }); } - void _syncAllBuffers( - [int? absoluteStartFrame, int maxEventsToSync = BUFFER_SIZE]) { + void _syncAllBuffers([int? absoluteStartFrame, int maxEventsToSync = BUFFER_SIZE]) { _getAllTracks().forEach((track) { track.syncBuffer(absoluteStartFrame, maxEventsToSync); }); diff --git a/lib/models/events.dart b/lib/models/events.dart index 80905fc3..12260c00 100644 --- a/lib/models/events.dart +++ b/lib/models/events.dart @@ -62,8 +62,9 @@ class MidiEvent extends SchedulerEvent { required int noteNumber, required int velocity, }) { - if (noteNumber > 127 || noteNumber < 0) + if (noteNumber > 127 || noteNumber < 0) { throw 'noteNumber must be in range 0-127'; + } if (velocity > 127 || velocity < 0) throw 'Velocity must be in range 0-127'; return MidiEvent( @@ -78,8 +79,9 @@ class MidiEvent extends SchedulerEvent { required double beat, required int noteNumber, }) { - if (noteNumber > 127 || noteNumber < 0) + if (noteNumber > 127 || noteNumber < 0) { throw 'noteNumber must be in range 0-127'; + } return MidiEvent( beat: beat, @@ -141,8 +143,8 @@ class VolumeEvent extends SchedulerEvent { } @override - ByteData serializeBytes(int sampleRate, double beat, int correctionFrames) { - final data = super.serializeBytes(sampleRate, beat, correctionFrames); + ByteData serializeBytes(int sampleRate, double tempo, int correctionFrames) { + final data = super.serializeBytes(sampleRate, tempo, correctionFrames); data.setFloat32(SCHEDULER_EVENT_DATA_OFFSET, volume!, Endian.host); diff --git a/lib/models/sfz.dart b/lib/models/sfz.dart index b68fc2bb..be523147 100644 --- a/lib/models/sfz.dart +++ b/lib/models/sfz.dart @@ -1,12 +1,9 @@ /// Learn more about the SFZ format here: - String opcodeMapToString(Map? opcodeMap) { if (opcodeMap == null) { return ''; } else { - return opcodeMap.entries - .map((entry) => '${entry.key}=${entry.value}\n') - .join(''); + return opcodeMap.entries.map((entry) => '${entry.key}=${entry.value}\n').join(''); } } @@ -31,16 +28,7 @@ class SfzRegion { Map? otherOpcodes; String buildString() { - return '\n' + - (sample != null ? 'sample=$sample\n' : '') + - (key != null ? 'key=$key\n' : '') + - (lokey != null ? 'lokey=$lokey\n' : '') + - (hikey != null ? 'hikey=$hikey\n' : '') + - (lovel != null ? 'lovel=$lovel\n' : '') + - (hivel != null ? 'hivel=$hivel\n' : '') + - (loopStart != null ? 'loop_start=$loopStart\n' : '') + - (loopEnd != null ? 'loop_end=$loopEnd\n' : '') + - (opcodeMapToString(otherOpcodes)); + return '\n${sample != null ? 'sample=$sample\n' : ''}${key != null ? 'key=$key\n' : ''}${lokey != null ? 'lokey=$lokey\n' : ''}${hikey != null ? 'hikey=$hikey\n' : ''}${lovel != null ? 'lovel=$lovel\n' : ''}${hivel != null ? 'hivel=$hivel\n' : ''}${loopStart != null ? 'loop_start=$loopStart\n' : ''}${loopEnd != null ? 'loop_end=$loopEnd\n' : ''}${opcodeMapToString(otherOpcodes)}'; } } @@ -54,9 +42,7 @@ class SfzGroup { List regions; String buildString() { - return '\n' + - (opcodeMapToString(opcodes)) + - regions.map((r) => r.buildString()).join(''); + return '\n${opcodeMapToString(opcodes)}${regions.map((r) => r.buildString()).join('')}'; } } @@ -68,7 +54,7 @@ class SfzControl { Map? opcodes; String buildString() { - return '\n' + (opcodeMapToString(opcodes)); + return '\n${opcodeMapToString(opcodes)}'; } } @@ -80,7 +66,7 @@ class SfzGlobal { Map? opcodes; String buildString() { - return '\n' + (opcodeMapToString(opcodes)); + return '\n${opcodeMapToString(opcodes)}'; } } @@ -92,7 +78,7 @@ class SfzEffect { Map? opcodes; String buildString() { - return '\n' + (opcodeMapToString(opcodes)); + return '\n${opcodeMapToString(opcodes)}'; } } @@ -104,7 +90,7 @@ class SfzCurve { Map? opcodes; String buildString() { - return '\n' + (opcodeMapToString(opcodes)); + return '\n${opcodeMapToString(opcodes)}'; } } @@ -128,13 +114,14 @@ class Sfz { void _setNoteRanges() { final allRegions = []; - groups.forEach((g) => allRegions.addAll(g.regions)); + for (final g in groups) { + allRegions.addAll(g.regions); + } allRegions.sort((a, b) => a.key - b.key); allRegions.asMap().forEach((index, sd) { final prevSd = index > 0 ? allRegions[index - 1] : null; - final nextSd = - index < allRegions.length - 1 ? allRegions[index + 1] : null; + final nextSd = index < allRegions.length - 1 ? allRegions[index + 1] : null; if (sd.lokey == null) { if (prevSd == null) { diff --git a/lib/native_bridge.dart b/lib/native_bridge.dart index f58213e8..d356aa40 100644 --- a/lib/native_bridge.dart +++ b/lib/native_bridge.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; + import 'package:ffi/ffi.dart'; import 'package:flutter/services.dart'; @@ -12,69 +13,56 @@ final DynamicLibrary nativeLib = Platform.isAndroid : DynamicLibrary.executable(); final nRegisterPostCObject = nativeLib.lookupFunction< - Void Function( - Pointer)>> - functionPointer), - void Function( - Pointer)>> - functionPointer)>('RegisterDart_PostCObject'); + Void Function( + Pointer)>> functionPointer), + void Function( + Pointer)>> functionPointer)>( + 'RegisterDart_PostCObject'); -final nSetupEngine = nativeLib - .lookupFunction('setup_engine'); +final nSetupEngine = + nativeLib.lookupFunction('setup_engine'); -final nDestroyEngine = nativeLib - .lookupFunction('destroy_engine'); +final nDestroyEngine = nativeLib.lookupFunction('destroy_engine'); -final nAddTrackSf2 = nativeLib.lookupFunction< - Void Function(Pointer, Int8, Int32, Int64), +final nAddTrackSf2 = nativeLib.lookupFunction, Int8, Int32, Int64), void Function(Pointer, int, int, int)>('add_track_sf2'); -final nAddTrackSfz = nativeLib.lookupFunction< - Void Function(Pointer, Pointer, Int64), +final nAddTrackSfz = nativeLib.lookupFunction, Pointer, Int64), void Function(Pointer, Pointer, int)>('add_track_sfz'); final nAddTrackSfzString = nativeLib.lookupFunction< Void Function(Pointer, Pointer, Pointer, Int64), - void Function(Pointer, Pointer, Pointer, - int)>('add_track_sfz_string'); + void Function(Pointer, Pointer, Pointer, int)>('add_track_sfz_string'); -final nRemoveTrack = nativeLib - .lookupFunction('remove_track'); +final nRemoveTrack = + nativeLib.lookupFunction('remove_track'); -final nResetTrack = nativeLib - .lookupFunction('reset_track'); +final nResetTrack = + nativeLib.lookupFunction('reset_track'); -final nGetPosition = - nativeLib.lookupFunction('get_position'); +final nGetPosition = nativeLib.lookupFunction('get_position'); final nGetTrackVolume = - nativeLib.lookupFunction( - 'get_track_volume'); + nativeLib.lookupFunction('get_track_volume'); final nGetLastRenderTimeUs = - nativeLib.lookupFunction( - 'get_last_render_time_us'); + nativeLib.lookupFunction('get_last_render_time_us'); -final nGetBufferAvailableCount = - nativeLib.lookupFunction( - 'get_buffer_available_count'); +final nGetBufferAvailableCount = nativeLib + .lookupFunction('get_buffer_available_count'); -final nHandleEventsNow = nativeLib.lookupFunction< - Uint32 Function(Int32?, Pointer?, Uint32), +final nHandleEventsNow = nativeLib.lookupFunction?, Uint32), int Function(int?, Pointer?, int)>('handle_events_now'); -final nScheduleEvents = nativeLib.lookupFunction< - Uint32 Function(Int32?, Pointer?, Uint32), +final nScheduleEvents = nativeLib.lookupFunction?, Uint32), int Function(int?, Pointer?, int)>('schedule_events'); -final nClearEvents = nativeLib.lookupFunction('clear_events'); +final nClearEvents = nativeLib + .lookupFunction('clear_events'); -final nPlay = - nativeLib.lookupFunction('engine_play'); +final nPlay = nativeLib.lookupFunction('engine_play'); -final nPause = - nativeLib.lookupFunction('engine_pause'); +final nPause = nativeLib.lookupFunction('engine_pause'); /// {@macro flutter_sequencer_library_private} /// This class encapsulates the boilerplate code needed to call into native code @@ -112,34 +100,27 @@ class NativeBridge { return audioUnitIds; } - static Future addTrackSf2( - String filename, bool isAsset, int patchNumber) { + static Future addTrackSf2(String filename, bool isAsset, int patchNumber) { final filenameUtf8Ptr = filename.toNativeUtf8(); - return singleResponseFuture((port) => nAddTrackSf2( - filenameUtf8Ptr, isAsset ? 1 : 0, patchNumber, port.nativePort)); + return singleResponseFuture( + (port) => nAddTrackSf2(filenameUtf8Ptr, isAsset ? 1 : 0, patchNumber, port.nativePort)); } static Future addTrackSfz(String sfzPath, String? tuningPath) { final sfzPathUtf8Ptr = sfzPath.toNativeUtf8(); - final tuningPathUtf8Ptr = - tuningPath?.toNativeUtf8() ?? Pointer.fromAddress(0); + final tuningPathUtf8Ptr = tuningPath?.toNativeUtf8() ?? Pointer.fromAddress(0); - return singleResponseFuture((port) => - nAddTrackSfz(sfzPathUtf8Ptr, tuningPathUtf8Ptr, port.nativePort)); + return singleResponseFuture( + (port) => nAddTrackSfz(sfzPathUtf8Ptr, tuningPathUtf8Ptr, port.nativePort)); } - static Future addTrackSfzString( - String sampleRoot, String sfzContent, String? tuningString) { + static Future addTrackSfzString(String sampleRoot, String sfzContent, String? tuningString) { final sampleRootUtf8Ptr = sampleRoot.toNativeUtf8(); final sfzContentUtf8Ptr = sfzContent.toNativeUtf8(); - final tuningStringUtf8Ptr = - tuningString?.toNativeUtf8() ?? Pointer.fromAddress(0); + final tuningStringUtf8Ptr = tuningString?.toNativeUtf8() ?? Pointer.fromAddress(0); return singleResponseFuture((port) => nAddTrackSfzString( - sampleRootUtf8Ptr, - sfzContentUtf8Ptr, - tuningStringUtf8Ptr, - port.nativePort)); + sampleRootUtf8Ptr, sfzContentUtf8Ptr, tuningStringUtf8Ptr, port.nativePort)); } static Future addTrackAudioUnit(String id) async { @@ -176,34 +157,30 @@ class NativeBridge { return nGetBufferAvailableCount(trackIndex); } - static int handleEventsNow(int trackIndex, List events, - int sampleRate, double tempo) { + static int handleEventsNow( + int trackIndex, List events, int sampleRate, double tempo) { if (events.isEmpty) return 0; - final Pointer? nativeArray = - calloc(events.length * SCHEDULER_EVENT_SIZE); + final Pointer nativeArray = calloc(events.length * SCHEDULER_EVENT_SIZE); events.asMap().forEach((eventIndex, e) { final byteData = e.serializeBytes(sampleRate, tempo, 0); for (var byteIndex = 0; byteIndex < byteData.lengthInBytes; byteIndex++) { - nativeArray![eventIndex * SCHEDULER_EVENT_SIZE + byteIndex] = - byteData.getUint8(byteIndex); + nativeArray[eventIndex * SCHEDULER_EVENT_SIZE + byteIndex] = byteData.getUint8(byteIndex); } }); return nHandleEventsNow(trackIndex, nativeArray, events.length); } - static int scheduleEvents(int trackIndex, List events, - int sampleRate, double tempo, int frameOffset) { + static int scheduleEvents( + int trackIndex, List events, int sampleRate, double tempo, int frameOffset) { if (events.isEmpty) return 0; - final Pointer? nativeArray = - calloc(events.length * SCHEDULER_EVENT_SIZE); + final Pointer nativeArray = calloc(events.length * SCHEDULER_EVENT_SIZE); events.asMap().forEach((eventIndex, e) { final byteData = e.serializeBytes(sampleRate, tempo, frameOffset); for (var byteIndex = 0; byteIndex < byteData.lengthInBytes; byteIndex++) { - nativeArray![eventIndex * SCHEDULER_EVENT_SIZE + byteIndex] = - byteData.getUint8(byteIndex); + nativeArray[eventIndex * SCHEDULER_EVENT_SIZE + byteIndex] = byteData.getUint8(byteIndex); } }); diff --git a/lib/sequence.dart b/lib/sequence.dart index f6179385..c997bb00 100644 --- a/lib/sequence.dart +++ b/lib/sequence.dart @@ -29,7 +29,9 @@ class Sequence { /// Call this to remove this sequence and its tracks from the global sequencer /// engine. void destroy() { - _tracks.values.forEach((track) => deleteTrack(track)); + for (final track in _tracks.values) { + deleteTrack(track); + } globalState.unregisterSequence(this); } @@ -78,10 +80,10 @@ class Sequence { } }); - keysToRemove.forEach((key) { + for (final key in keysToRemove) { NativeBridge.removeTrack(key); _tracks.remove(key); - }); + } return _tracks.values.toList(); } @@ -103,9 +105,9 @@ class Sequence { void pause() { if (!globalState.isEngineReady) return; - _tracks.values.forEach((track) { + for (final track in _tracks.values) { NativeBridge.resetTrack(track.id); - }); + } globalState.pauseSequence(id); } @@ -113,19 +115,18 @@ class Sequence { void stop() { pause(); setBeat(0.0); - _tracks.values.forEach((track) { + for (final track in _tracks.values) { List.generate(128, (noteNumber) { track.stopNoteNow(noteNumber: noteNumber); }); - }); + } } /// Sets the tempo. void setTempo(double nextTempo) { // Update engine start frame to remove excess loops - final loopsElapsed = loopState == LoopState.BeforeLoopEnd - ? getLoopsElapsed(_getFramesRendered()) - : 0; + final loopsElapsed = + loopState == LoopState.BeforeLoopEnd ? getLoopsElapsed(_getFramesRendered()) : 0; engineStartFrame += loopsElapsed * getLoopLengthFrames(); // Update engine start frame to adjust to new tempo @@ -148,9 +149,8 @@ class Sequence { checkIsOver(); // Update engine start frame to remove excess loops - final loopsElapsed = loopState == LoopState.BeforeLoopEnd - ? getLoopsElapsed(_getFramesRendered()) - : 0; + final loopsElapsed = + loopState == LoopState.BeforeLoopEnd ? getLoopsElapsed(_getFramesRendered()) : 0; engineStartFrame += loopsElapsed * getLoopLengthFrames(); // Update loop state and bounds @@ -194,12 +194,11 @@ class Sequence { void setBeat(double beat) { if (!globalState.isEngineReady) return; - _tracks.values.forEach((track) { + for (final track in _tracks.values) { NativeBridge.resetTrack(track.id); - }); + } - final leadFrames = - getIsPlaying() ? min(_getFramesRendered(), LEAD_FRAMES) : 0; + final leadFrames = getIsPlaying() ? min(_getFramesRendered(), LEAD_FRAMES) : 0; final frame = beatToFrames(beat) - leadFrames; @@ -212,9 +211,7 @@ class Sequence { if (loopState != LoopState.Off) { final loopEndFrame = beatToFrames(loopEndBeat); - loopState = frame < loopEndFrame - ? LoopState.BeforeLoopEnd - : LoopState.AfterLoopEnd; + loopState = frame < loopEndFrame ? LoopState.BeforeLoopEnd : LoopState.AfterLoopEnd; } } @@ -315,10 +312,9 @@ class Sequence { if (!globalState.isEngineReady) return 0; if (isPlaying) { - final frame = _getFramesRendered() + - (estimateFramesSinceLastRender ? _getFramesSinceLastRender() : 0); - final loopedFrame = - loopState == LoopState.Off ? frame : getLoopedFrame(frame); + final frame = + _getFramesRendered() + (estimateFramesSinceLastRender ? _getFramesSinceLastRender() : 0); + final loopedFrame = loopState == LoopState.Off ? frame : getLoopedFrame(frame); return max(min(loopedFrame, beatToFrames(endBeat)), 0); } else { @@ -329,10 +325,8 @@ class Sequence { /// Returns the number of frames elapsed since the last audio render callback /// was called. int _getFramesSinceLastRender() { - final microsecondsSinceLastRender = max( - 0, - DateTime.now().microsecondsSinceEpoch - - NativeBridge.getLastRenderTimeUs()); + final microsecondsSinceLastRender = + max(0, DateTime.now().microsecondsSinceEpoch - NativeBridge.getLastRenderTimeUs()); return globalState.usToFrames(microsecondsSinceLastRender); } @@ -348,8 +342,7 @@ class Sequence { } Future> _createTracks(List instruments) async { - final tracks = await Future.wait( - instruments.map((instrument) => _createTrack(instrument))); + final tracks = await Future.wait(instruments.map((instrument) => _createTrack(instrument))); final nonNullTracks = tracks.whereType().toList(); return nonNullTracks; diff --git a/lib/track.dart b/lib/track.dart index 8808dedc..adff5cc5 100644 --- a/lib/track.dart +++ b/lib/track.dart @@ -4,8 +4,8 @@ import 'dart:math'; import 'package:path/path.dart' as p; import 'constants.dart'; -import 'models/instrument.dart'; import 'models/events.dart'; +import 'models/instrument.dart'; import 'native_bridge.dart'; import 'sequence.dart'; @@ -18,12 +18,10 @@ class Track { final events = []; int lastFrameSynced = 0; - Track._withId( - {required this.sequence, required this.id, required this.instrument}); + Track._withId({required this.sequence, required this.id, required this.instrument}); /// Creates a track in the underlying sequencer engine. - static Future build( - {required Sequence sequence, required Instrument instrument}) async { + static Future build({required Sequence sequence, required Instrument instrument}) async { int? id; if (instrument is Sf2Instrument) { @@ -34,30 +32,27 @@ class Track { String? normalizedSfzPath; if (instrument.isAsset) { - final normalizedSfzDir = - await NativeBridge.normalizeAssetDir(sfzFile.parent.path); + final normalizedSfzDir = await NativeBridge.normalizeAssetDir(sfzFile.parent.path); - if (normalizedSfzDir == null) - throw Exception( - 'Could not normalize asset dir for ${sfzFile.parent.path}'); + if (normalizedSfzDir == null) { + throw Exception('Could not normalize asset dir for ${sfzFile.parent.path}'); + } normalizedSfzPath = '$normalizedSfzDir/${p.basename(sfzFile.path)}'; } else { normalizedSfzPath = sfzFile.path; } - id = await NativeBridge.addTrackSfz( - normalizedSfzPath, instrument.tuningPath); + id = await NativeBridge.addTrackSfz(normalizedSfzPath, instrument.tuningPath); } else if (instrument is RuntimeSfzInstrument) { final sfzContent = instrument.sfz.buildString(); String? normalizedSampleRoot; if (instrument.isAsset) { - normalizedSampleRoot = - await NativeBridge.normalizeAssetDir(instrument.sampleRoot); + normalizedSampleRoot = await NativeBridge.normalizeAssetDir(instrument.sampleRoot); - if (normalizedSampleRoot == null) - throw Exception( - 'Could not normalize asset dir for ${instrument.sampleRoot}'); + if (normalizedSampleRoot == null) { + throw Exception('Could not normalize asset dir for ${instrument.sampleRoot}'); + } } else { normalizedSampleRoot = instrument.sampleRoot; } @@ -65,8 +60,7 @@ class Track { // Sfizz uses the parent path of this (line 73 of Parser.cpp) final fakeSfzDir = '$normalizedSampleRoot/does_not_exist.sfz'; - id = await NativeBridge.addTrackSfzString( - fakeSfzDir, sfzContent, instrument.tuningString); + id = await NativeBridge.addTrackSfzString(fakeSfzDir, sfzContent, instrument.tuningString); } else if (instrument is AudioUnitInstrument) { id = await NativeBridge.addTrackAudioUnit(instrument.idOrPath); } else { @@ -87,12 +81,9 @@ class Track { void startNoteNow({required int noteNumber, required double velocity}) { final nextBeat = sequence.getBeat(); final event = MidiEvent.ofNoteOn( - beat: nextBeat, - noteNumber: noteNumber, - velocity: _velocityToMidi(velocity)); + beat: nextBeat, noteNumber: noteNumber, velocity: _velocityToMidi(velocity)); - NativeBridge.handleEventsNow( - id, [event], Sequence.globalState.sampleRate!, sequence.tempo); + NativeBridge.handleEventsNow(id, [event], Sequence.globalState.sampleRate!, sequence.tempo); } /// Handles a Note Off event on this track immediately. @@ -101,19 +92,16 @@ class Track { final nextBeat = sequence.getBeat(); final event = MidiEvent.ofNoteOff(beat: nextBeat, noteNumber: noteNumber); - NativeBridge.handleEventsNow( - id, [event], Sequence.globalState.sampleRate!, sequence.tempo); + NativeBridge.handleEventsNow(id, [event], Sequence.globalState.sampleRate!, sequence.tempo); } /// Handles a MIDI CC event on this track immediately. /// The event will not be added to this track's events. void midiCCNow({required int ccNumber, required int ccValue}) { final nextBeat = sequence.getBeat(); - final event = - MidiEvent.cc(beat: nextBeat, ccNumber: ccNumber, ccValue: ccValue); + final event = MidiEvent.cc(beat: nextBeat, ccNumber: ccNumber, ccValue: ccValue); - NativeBridge.handleEventsNow( - id, [event], Sequence.globalState.sampleRate!, sequence.tempo); + NativeBridge.handleEventsNow(id, [event], Sequence.globalState.sampleRate!, sequence.tempo); } /// Handles a MIDI pitch bend event on this track immediately. @@ -122,8 +110,7 @@ class Track { final nextBeat = sequence.getBeat(); final event = MidiEvent.pitchBend(beat: nextBeat, value: value); - NativeBridge.handleEventsNow( - id, [event], Sequence.globalState.sampleRate!, sequence.tempo); + NativeBridge.handleEventsNow(id, [event], Sequence.globalState.sampleRate!, sequence.tempo); } /// Handles a Volume Change event on this track immediately. @@ -132,8 +119,7 @@ class Track { final nextBeat = sequence.getBeat(); final event = VolumeEvent(beat: nextBeat, volume: volume); - NativeBridge.handleEventsNow( - id, [event], Sequence.globalState.sampleRate!, sequence.tempo); + NativeBridge.handleEventsNow(id, [event], Sequence.globalState.sampleRate!, sequence.tempo); } /// Adds a Note On and Note Off event to this track. @@ -157,10 +143,7 @@ class Track { /// Adds a Note On event to this track. /// This does not sync the events to the backend. - void addNoteOn( - {required int noteNumber, - required double velocity, - required double beat}) { + void addNoteOn({required int noteNumber, required double velocity, required double beat}) { assert(velocity > 0 && velocity <= 1); final noteOnEvent = MidiEvent.ofNoteOn( @@ -185,10 +168,8 @@ class Track { /// Adds a MIDI CC event to this track. /// This does not sync the events to the backend. - void addMidiCC( - {required int ccNumber, required int ccValue, required double beat}) { - final ccEvent = - MidiEvent.cc(beat: beat, ccNumber: ccNumber, ccValue: ccValue); + void addMidiCC({required int ccNumber, required int ccValue, required double beat}) { + final ccEvent = MidiEvent.cc(beat: beat, ccNumber: ccNumber, ccValue: ccValue); _addEvent(ccEvent); } @@ -223,8 +204,7 @@ class Track { /// Syncs events to the backend. This should be called after making changes to /// track events to ensure that the changes are synced immediately. - void syncBuffer( - [int? absoluteStartFrame, int maxEventsToSync = BUFFER_SIZE]) { + void syncBuffer([int? absoluteStartFrame, int maxEventsToSync = BUFFER_SIZE]) { final position = NativeBridge.getPosition(); if (absoluteStartFrame == null) { @@ -268,8 +248,7 @@ class Track { if (events.isEmpty) { index = 0; } else { - final indexWhereResult = - events.indexWhere((e) => _compareEvents(e, eventToAdd) == 1); + final indexWhereResult = events.indexWhere((e) => _compareEvents(e, eventToAdd) == 1); if (indexWhereResult == -1) { index = events.length; @@ -286,15 +265,13 @@ class Track { void _scheduleEvents(int startFrame, [int maxEventsToSync = BUFFER_SIZE]) { final isBeforeLoopEnd = sequence.loopState == LoopState.BeforeLoopEnd; final loopLength = sequence.getLoopLengthFrames(); - final loopsElapsed = sequence.loopState == LoopState.Off - ? 0 - : sequence.getLoopsElapsed(startFrame); + final loopsElapsed = + sequence.loopState == LoopState.Off ? 0 : sequence.getLoopsElapsed(startFrame); var eventsSyncedCount = _scheduleEventsInRange( maxEventsToSync, isBeforeLoopEnd ? sequence.getLoopedFrame(startFrame) : startFrame, - sequence.beatToFrames( - isBeforeLoopEnd ? sequence.loopEndBeat : sequence.endBeat), + sequence.beatToFrames(isBeforeLoopEnd ? sequence.loopEndBeat : sequence.endBeat), loopLength * loopsElapsed); if (isBeforeLoopEnd) { @@ -305,11 +282,8 @@ class Track { while (eventsSyncedCount < maxEventsToSync) { // Schedule all events in one loop range - lastBatchCount = _scheduleEventsInRange( - maxEventsToSync - eventsSyncedCount, - loopStartFrame, - loopEndFrame, - loopLength * loopIndex); + lastBatchCount = _scheduleEventsInRange(maxEventsToSync - eventsSyncedCount, loopStartFrame, + loopEndFrame, loopLength * loopIndex); eventsSyncedCount += lastBatchCount; if (lastBatchCount == 0) break; @@ -320,8 +294,7 @@ class Track { /// Schedules this track's events that start on or after startBeat and end /// on or before endBeat. Adds frameOffset to every scheduled event. - int _scheduleEventsInRange( - int maxEventsToSync, int startFrame, int? endFrame, int frameOffset) { + int _scheduleEventsInRange(int maxEventsToSync, int startFrame, int? endFrame, int frameOffset) { final eventsToSync = []; for (var eventIndex = 0; eventIndex < events.length; eventIndex++) { @@ -336,12 +309,8 @@ class Track { eventsToSync.add(event); } - final eventsSyncedCount = NativeBridge.scheduleEvents( - id, - eventsToSync, - Sequence.globalState.sampleRate!, - sequence.tempo, - sequence.engineStartFrame + frameOffset); + final eventsSyncedCount = NativeBridge.scheduleEvents(id, eventsToSync, + Sequence.globalState.sampleRate!, sequence.tempo, sequence.engineStartFrame + frameOffset); if (eventsSyncedCount > 0) { lastFrameSynced = sequence.engineStartFrame + @@ -361,10 +330,10 @@ class Track { } else { // Beats are the same - if (eventA is VolumeEvent && !(eventB is VolumeEvent)) { + if (eventA is VolumeEvent && eventB is! VolumeEvent) { // Volume should come before anything else return -1; - } else if (eventB is VolumeEvent && !(eventA is VolumeEvent)) { + } else if (eventB is VolumeEvent && eventA is! VolumeEvent) { return 1; } else if (eventA is MidiEvent && eventB is MidiEvent) { // Note off should come before note on if the note is the same diff --git a/lib/utils/isolate.dart b/lib/utils/isolate.dart index b4e5f401..849b1aef 100644 --- a/lib/utils/isolate.dart +++ b/lib/utils/isolate.dart @@ -44,10 +44,10 @@ void _castComplete(Completer completer, Object value) { Future singleResponseFuture(void Function(SendPort responsePort) action, {Duration? timeout, R? timeoutValue}) { - var completer = Completer.sync(); - var responsePort = RawReceivePort(); + final completer = Completer.sync(); + final responsePort = RawReceivePort(); Timer? timer; - var zone = Zone.current; + final zone = Zone.current; responsePort.handler = (Object response) { responsePort.close(); timer?.cancel(); diff --git a/melos_flutter_sequencer.iml b/melos_flutter_sequencer.iml new file mode 100644 index 00000000..9fc8ce79 --- /dev/null +++ b/melos_flutter_sequencer.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pubspec.lock b/pubspec.lock index 4c5757bd..0905a4bf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,157 +5,217 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.19.1" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.3" ffi: dependency: "direct main" description: name: ffi - url: "https://pub.dartlang.org" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "2.1.4" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + url: "https://pub.dev" + source: hosted + version: "6.0.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.16.0" path: dependency: "direct main" description: name: path - url: "https://pub.dartlang.org" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" pedantic: dependency: "direct dev" description: name: pedantic - url: "https://pub.dartlang.org" + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" source: hosted - version: "0.3.0" - typed_data: + version: "0.7.4" + vector_math: dependency: transitive description: - name: typed_data - url: "https://pub.dartlang.org" + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "1.3.0" - vector_math: + version: "2.1.4" + vm_service: dependency: transitive description: - name: vector_math - url: "https://pub.dartlang.org" + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "15.0.0" sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.10.0" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 350e90b9..d8584b38 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,23 +12,16 @@ environment: dependencies: flutter: sdk: flutter - ffi: ^1.0.0 - path: ^1.8.0 + ffi: ^2.1.4 + path: ^1.9.1 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.11.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. + pedantic: ^1.11.1 + flutter_lints: ^6.0.0 + flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' and Android 'package' identifiers should not ordinarily - # be modified. They are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: platforms: android: @@ -36,34 +29,3 @@ flutter: pluginClass: FlutterSequencerPlugin ios: pluginClass: FlutterSequencerPlugin - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/test/flutter_sequencer_test.dart b/test/flutter_sequencer_test.dart index 3e824801..d30207dd 100644 --- a/test/flutter_sequencer_test.dart +++ b/test/flutter_sequencer_test.dart @@ -1,18 +1,3 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -void main() { - const channel = MethodChannel('flutter_sequencer'); - TestWidgetsFlutterBinding.ensureInitialized(); - - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; - }); - }); - - tearDown(() { - channel.setMockMethodCallHandler(null); - }); -} +void main() {}