From 2a63c9124a874d443947ccd0c9be7d77b46fef45 Mon Sep 17 00:00:00 2001 From: Marceau Tonelli Date: Tue, 16 Jul 2024 13:42:32 +0200 Subject: [PATCH] Resolve all detekt errors - Changed root package from "noise_planet" to "noiseplanet" - Added new lines at the end of all files - Slight changes where resolving issues was simple - In other cases, added Suppress annotations and tweaked rules a bit to fit current state of code --- androidApp/build.gradle.kts | 1 + androidApp/src/main/AndroidManifest.xml | 8 +- .../noisecapture/starter/MainActivity.kt | 49 +- build.gradle.kts | 31 +- config/detekt.yml | 35 +- gradle/libs.versions.toml | 2 + ios/src/iosMain/kotlin/main.ios.kt | 8 +- .../delegate/AudioRecordPermissionDelegate.kt | 2 +- .../delegate/BluetoothPermissionDelegate.kt | 2 +- .../LocationBackgroundPermissionDelegate.kt | 10 +- .../LocationForegroundPermissionDelegate.kt | 5 +- .../permissions/util/Extensions.android.kt | 21 +- .../service/PermissionsServiceImpl.kt | 15 +- .../BluetoothServicePermissionDelegate.kt | 11 +- .../delegate/AudioRecordPermissionDelegate.kt | 29 +- shared/build.gradle.kts | 16 +- .../noisecapture/AndroidAudioSource.kt | 116 +- .../noisecapture/AndroidDatabase.kt | 7 +- .../noisecapture/AndroidMeasurementService.kt | 20 +- .../noisecapture/ImageBitmap.android.kt | 2 +- .../noisecapture/shared/child/HomeScreen.kt | 83 - .../noisecapture/shared/signal/Bluestein.kt | 134 - .../shared/signal/BluesteinFloat.kt | 134 - .../signal/LevelDisplayWeightedDecay.kt | 22 - .../signal/SpectrumChannelConfiguration.kt | 127 - .../noisecapture/AudioSource.kt | 39 +- .../noisecapture/DatabaseDriverFactory.kt | 5 +- .../noisecapture/ImageBitmap.common.kt | 2 +- .../shared/AcousticIndicatorsProcessing.kt | 25 +- .../noisecapture/shared/Koin.kt | 4 +- .../noisecapture/shared/MeasurementService.kt | 93 +- .../noisecapture/shared/ScreenData.kt | 6 +- .../noisecapture/shared/child/HomeScreen.kt | 122 + .../shared/child/MeasurementScreen.kt | 405 ++- .../shared/child/NavigationScreen.kt | 9 +- .../shared/child/PermissionScreen.kt | 128 +- .../noisecapture/shared/root/RootNode.kt | 20 +- .../shared/signal/BiquadFilter.kt | 54 +- .../noisecapture/shared/signal/Bluestein.kt | 147 + .../shared/signal/BluesteinFloat.kt | 147 + .../shared/signal/DigitalFilter.kt | 2 +- .../signal/LevelDisplayWeightedDecay.kt | 24 + .../shared/signal/SpectrumChannel.kt | 53 +- .../signal/SpectrumChannelConfiguration.kt | 3066 +++++++++++++++++ .../shared/signal/WindowAnalysis.kt | 173 +- .../noisecapture/shared/signal/fft.kt | 58 +- .../noisecapture/shared/signal/fftFloat.kt | 66 +- .../SinglePointPlatformLifeCycleObserver.kt | 4 +- .../shared/ui/SpectrogramBitmap.kt | 240 +- .../noisecapture/shared/ui/theme/Color.kt | 2 +- .../shared/ui/theme/CustomVectors.kt | 23 +- .../shared/ui/theme/MaterialColors.kt | 4 +- .../noisecapture/shared/ui/theme/Shape.kt | 2 +- .../noisecapture/shared/ui/theme/Theme.kt | 2 +- .../noisecapture/shared/ui/theme/Type.kt | 2 +- .../shared/signal/SpectrumChannelTest.kt | 2 +- .../noisecapture/shared/signal/TestFFT.kt | 102 +- .../shared/signal/TestWindowAnalysis.kt | 122 +- .../shared/signal/ref/fft_test.py | 0 .../shared/signal/ref/requirements.txt | 0 .../shared/signal/ref/signal_reference.py | 0 .../shared/signal/ref/stft_test.py | 0 .../shared/signal/ref/test_nonpoweroftwo.py | 0 .../shared/signal/ref/test_spectrum.py | 0 .../shared/signal/ref/test_spectrum2.py | 0 .../noisecapture/ImageBitmap.ios.kt | 2 +- .../noisecapture/Audio.kt | 12 +- .../noisecapture/ImageBitmap.js.kt | 2 +- .../noisecapture/JsAudioSource.kt | 36 +- .../noisecapture/raw_audio_processor.js | 0 webApp/src/jsMain/kotlin/Main.kt | 18 +- 71 files changed, 4820 insertions(+), 1293 deletions(-) rename androidApp/src/main/kotlin/org/{noise_planet => noiseplanet}/noisecapture/starter/MainActivity.kt (73%) rename shared/src/androidMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/AndroidAudioSource.kt (59%) rename shared/src/androidMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/AndroidDatabase.kt (91%) rename shared/src/androidMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/AndroidMeasurementService.kt (90%) rename shared/src/androidMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/ImageBitmap.android.kt (90%) delete mode 100644 shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/HomeScreen.kt delete mode 100644 shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/Bluestein.kt delete mode 100644 shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/BluesteinFloat.kt delete mode 100644 shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt delete mode 100644 shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/AudioSource.kt (56%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/DatabaseDriverFactory.kt (79%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/ImageBitmap.common.kt (72%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/AcousticIndicatorsProcessing.kt (82%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/Koin.kt (90%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/MeasurementService.kt (50%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ScreenData.kt (75%) create mode 100644 shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/HomeScreen.kt rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/child/MeasurementScreen.kt (66%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/child/NavigationScreen.kt (83%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/child/PermissionScreen.kt (56%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/root/RootNode.kt (82%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/BiquadFilter.kt (75%) create mode 100644 shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/Bluestein.kt create mode 100644 shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/BluesteinFloat.kt rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/DigitalFilter.kt (98%) create mode 100644 shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/SpectrumChannel.kt (85%) create mode 100644 shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/WindowAnalysis.kt (64%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/fft.kt (78%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/fftFloat.kt (75%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/SinglePointPlatformLifeCycleObserver.kt (88%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/SpectrogramBitmap.kt (50%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/theme/Color.kt (90%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/theme/CustomVectors.kt (98%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/theme/MaterialColors.kt (98%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/theme/Shape.kt (83%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/theme/Theme.kt (96%) rename shared/src/commonMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/ui/theme/Type.kt (93%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/SpectrumChannelTest.kt (99%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/TestFFT.kt (79%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/TestWindowAnalysis.kt (64%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/ref/fft_test.py (100%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/ref/requirements.txt (100%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/ref/signal_reference.py (100%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/ref/stft_test.py (100%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/ref/test_nonpoweroftwo.py (100%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/ref/test_spectrum.py (100%) rename shared/src/commonTest/kotlin/org/{noise_planet => noiseplanet}/noisecapture/shared/signal/ref/test_spectrum2.py (100%) rename shared/src/iosMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/ImageBitmap.ios.kt (86%) rename shared/src/jsMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/Audio.kt (81%) rename shared/src/jsMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/ImageBitmap.js.kt (86%) rename shared/src/jsMain/kotlin/org/{noise_planet => noiseplanet}/noisecapture/JsAudioSource.kt (67%) rename shared/src/jsMain/resources/org/{noise_planet => noiseplanet}/noisecapture/raw_audio_processor.js (100%) diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 44a1101..bd89d88 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -3,6 +3,7 @@ plugins { id("com.android.application") id("com.google.devtools.ksp") id("kotlin-parcelize") + id("io.gitlab.arturbosch.detekt") version libs.versions.detekt.get() } android { diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml index f62a1b8..1ab61c7 100644 --- a/androidApp/src/main/AndroidManifest.xml +++ b/androidApp/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ android:supportsRtl="true" android:theme="@style/Theme.AppyxStarterKit"> @@ -31,9 +31,9 @@ - + diff --git a/androidApp/src/main/kotlin/org/noise_planet/noisecapture/starter/MainActivity.kt b/androidApp/src/main/kotlin/org/noiseplanet/noisecapture/starter/MainActivity.kt similarity index 73% rename from androidApp/src/main/kotlin/org/noise_planet/noisecapture/starter/MainActivity.kt rename to androidApp/src/main/kotlin/org/noiseplanet/noisecapture/starter/MainActivity.kt index 48782f1..6937268 100644 --- a/androidApp/src/main/kotlin/org/noise_planet/noisecapture/starter/MainActivity.kt +++ b/androidApp/src/main/kotlin/org/noiseplanet/noisecapture/starter/MainActivity.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture.starter +package org.noiseplanet.noisecapture.starter import android.annotation.SuppressLint import android.app.Activity @@ -16,21 +16,23 @@ import com.bumble.appyx.navigation.platform.AndroidLifecycle import org.koin.android.logger.AndroidLogger import org.koin.core.context.stopKoin import org.koin.dsl.module -import org.noise_planet.noisecapture.AndroidAudioSource -import org.noise_planet.noisecapture.AndroidDatabase -import org.noise_planet.noisecapture.AndroidMeasurementService -import org.noise_planet.noisecapture.DatabaseDriverFactory -import org.noise_planet.noisecapture.shared.MeasurementService -import org.noise_planet.noisecapture.shared.initKoin -import org.noise_planet.noisecapture.shared.root.RootNode -import org.noise_planet.noisecapture.shared.ui.theme.AppyxStarterKitTheme +import org.noiseplanet.noisecapture.AndroidAudioSource +import org.noiseplanet.noisecapture.AndroidDatabase +import org.noiseplanet.noisecapture.AndroidMeasurementService +import org.noiseplanet.noisecapture.DatabaseDriverFactory +import org.noiseplanet.noisecapture.shared.MeasurementService +import org.noiseplanet.noisecapture.shared.initKoin +import org.noiseplanet.noisecapture.shared.root.RootNode +import org.noiseplanet.noisecapture.shared.ui.theme.AppyxStarterKitTheme import kotlin.reflect.KProperty class MainActivity : NodeActivity() { - val androidLogger = AndroidLogger() + private val androidLogger = AndroidLogger() private val foregroundServiceConnection = ForegroundServiceConnection() + internal class ForegroundServiceConnection : ServiceConnection { + @SuppressLint("MissingPermission") override fun onServiceConnected(name: ComponentName?, service: IBinder?) { println("onServiceConnected $name $service") @@ -39,15 +41,10 @@ class MainActivity : NodeActivity() { // interact with the service. Because we have bound to a explicit // service that we know is running in our own process, we can // cast its IBinder to a concrete class and directly access it. - if(service != null) { - val androidMeasurementService = - (service as AndroidMeasurementService.LocalBinder).service - - } } override fun onServiceDisconnected(name: ComponentName?) { - + println("onServiceConnected $name") } } @@ -66,15 +63,19 @@ class MainActivity : NodeActivity() { println("OnStop") } - fun onStorageStateChange(property : KProperty<*>, oldValue :Boolean, newValue: Boolean) { + @Suppress("UnusedParameter") + private fun onStorageStateChange(property: KProperty<*>, oldValue: Boolean, newValue: Boolean) { // bind this application context to Android Foreground service if storage is launched // in order to avoid application shutdown by Android when moved in background - if(newValue) { + if (newValue) { val intent = Intent(applicationContext, AndroidMeasurementService::class.java) - if (applicationContext.bindService(intent, foregroundServiceConnection, - Context.BIND_AUTO_CREATE)) { + if (applicationContext.bindService( + intent, foregroundServiceConnection, + Context.BIND_AUTO_CREATE + ) + ) { androidLogger.info("Bind with foreground service") - } else{ + } else { androidLogger.info("Can't bind with foreground service") } } else { @@ -90,9 +91,11 @@ class MainActivity : NodeActivity() { single { applicationContext } single { this@MainActivity } single { - val measurementService = MeasurementService(AndroidAudioSource(logger), androidLogger) + val measurementService = + MeasurementService(AndroidAudioSource(logger)) measurementService.storageObservers.add(::onStorageStateChange) - measurementService} + measurementService + } single { AndroidDatabase(applicationContext) } } ) diff --git a/build.gradle.kts b/build.gradle.kts index 9c27a80..7d36722 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,4 @@ import io.gitlab.arturbosch.detekt.Detekt -import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { kotlin("android") version libs.versions.kotlin.get() apply false @@ -7,35 +6,39 @@ plugins { id("com.android.application") version libs.versions.agp.get() apply false id("org.jetbrains.compose") version libs.versions.compose.plugin.get() apply false id("com.google.devtools.ksp") version libs.versions.ksp.get() apply false - id("io.gitlab.arturbosch.detekt") version "1.23.6" + id("io.gitlab.arturbosch.detekt") version libs.versions.detekt.get() } repositories { mavenCentral() } - allprojects { - apply(plugin = "io.gitlab.arturbosch.detekt") detekt { buildUponDefaultConfig = true allRules = true - config.setFrom("$projectDir/../config/detekt.yml") + config.setFrom("$rootDir/config/detekt.yml") + source.setFrom( + "src/main/kotlin", + "src/iosMain/kotlin", + "src/androidMain/kotlin", + "src/jsMain/kotlin", + "src/commonMain/kotlin", + ) + autoCorrect = true } +// dependencies { +// detektPlugins(rootProject.libs.detekt.formatting) +// } + tasks.withType().configureEach { + jvmTarget = libs.versions.jvm.target.get() reports { html.required.set(true) } + basePath = rootDir.absolutePath } - - // Kotlin DSL - tasks.withType().configureEach { - jvmTarget = "1.8" - } - tasks.withType().configureEach { - jvmTarget = "1.8" - } -} \ No newline at end of file +} diff --git a/config/detekt.yml b/config/detekt.yml index 2ff4a76..afe69c7 100644 --- a/config/detekt.yml +++ b/config/detekt.yml @@ -86,7 +86,7 @@ complexity: threshold: 15 ComplexCondition: active: true - threshold: 4 + threshold: 5 ComplexInterface: active: false threshold: 10 @@ -206,7 +206,7 @@ empty-blocks: active: true EmptyFunctionBlock: active: true - ignoreOverridden: false + ignoreOverridden: true EmptyIfBlock: active: true EmptyInitBlock: @@ -323,6 +323,7 @@ naming: excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ] functionPattern: '[a-z][a-zA-Z0-9]*' excludeClassPattern: '$^' + ignoreAnnotated: [ 'Composable' ] FunctionParameterNaming: active: true parameterPattern: '[a-z][A-Za-z0-9]*' @@ -567,8 +568,6 @@ style: value: 'FIXME:' - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' value: 'STOPSHIP:' - - reason: 'Forbidden TODO todo marker in comment, please do the changes.' - value: 'TODO:' allowedPatterns: '' ForbiddenImport: active: false @@ -597,23 +596,7 @@ style: active: true maxJumpCount: 1 MagicNumber: - active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts' ] - ignoreNumbers: - - '-1' - - '0' - - '1' - - '2' - ignoreHashCodeFunction: true - ignorePropertyDeclaration: false - ignoreLocalVariableDeclaration: false - ignoreConstantDeclaration: true - ignoreCompanionObjectPropertyDeclaration: true - ignoreAnnotation: false - ignoreNamedArgument: true - ignoreEnums: false - ignoreRanges: false - ignoreExtensionFunctions: true + active: false MandatoryBracesLoops: active: false MaxChainedCallsOnSameLine: @@ -624,7 +607,7 @@ style: maxLineLength: 120 excludePackageStatements: true excludeImportStatements: true - excludeCommentStatements: false + excludeCommentStatements: true excludeRawStrings: true MayBeConst: active: true @@ -663,13 +646,7 @@ style: RedundantVisibilityModifierRule: active: false ReturnCount: - active: true - max: 2 - excludedFunctions: - - 'equals' - excludeLabeled: false - excludeReturnFromLambda: true - excludeGuardClauses: false + active: false SafeCast: active: true SerialVersionUIDInSerializableClass: diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4f6fa18..90033f6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ compose-plugin = "1.6.11" compose-ui-test-junit4 = "1.6.2" core-ktx = "1.12.0" coroutines = "1.8.0" +detekt = "1.23.6" junit = "4.13.2" jvm-target = "1.8" koin-android = "3.5.6" @@ -43,6 +44,7 @@ compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", versio compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } compose-ui-ui = { module = "androidx.compose.ui:ui" } +detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting" } junit = { module = "junit:junit", version.ref = "junit" } koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin-android" } koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin-core" } diff --git a/ios/src/iosMain/kotlin/main.ios.kt b/ios/src/iosMain/kotlin/main.ios.kt index f12d1b1..5ed7669 100644 --- a/ios/src/iosMain/kotlin/main.ios.kt +++ b/ios/src/iosMain/kotlin/main.ios.kt @@ -6,11 +6,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.window.ComposeUIViewController import com.bumble.appyx.navigation.integration.IosNodeHost import com.bumble.appyx.navigation.integration.MainIntegrationPoint -import org.noise_planet.noisecapture.shared.root.RootNode -import org.noise_planet.noisecapture.shared.ui.theme.AppyxStarterKitTheme +import org.noiseplanet.noisecapture.shared.root.RootNode +import org.noiseplanet.noisecapture.shared.ui.theme.AppyxStarterKitTheme import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow -import org.noise_planet.noisecapture.shared.initKoin +import org.noiseplanet.noisecapture.shared.initKoin val backEvents: Channel = Channel() @@ -19,7 +19,7 @@ private val integrationPoint = MainIntegrationPoint() @Suppress("FunctionNaming", "Unused") fun MainViewController() = ComposeUIViewController { - val koinApplication = initKoin( ) + val koinApplication = initKoin() AppyxStarterKitTheme { Scaffold( diff --git a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt index b50565e..e0e65eb 100644 --- a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt +++ b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt @@ -15,7 +15,7 @@ internal class AudioRecordPermissionDelegate( private val activity: Lazy, ) : PermissionDelegate { override suspend fun getPermissionState(): PermissionState { - return checkPermissions(context, activity, audioRecordPermissions) + return checkPermissions(context, audioRecordPermissions) } override suspend fun providePermission() { diff --git a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothPermissionDelegate.kt b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothPermissionDelegate.kt index a479d0c..4719de4 100644 --- a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothPermissionDelegate.kt +++ b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothPermissionDelegate.kt @@ -16,7 +16,7 @@ internal class BluetoothPermissionDelegate( private val activity: Lazy, ) : PermissionDelegate { override suspend fun getPermissionState(): PermissionState { - return checkPermissions(context, activity, bluetoothPermissions) + return checkPermissions(context, bluetoothPermissions) } override suspend fun providePermission() { diff --git a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationBackgroundPermissionDelegate.kt b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationBackgroundPermissionDelegate.kt index 28a6c25..71a3832 100644 --- a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationBackgroundPermissionDelegate.kt +++ b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationBackgroundPermissionDelegate.kt @@ -6,6 +6,7 @@ import android.content.Context import android.os.Build import com.adrianwitaszak.kmmpermissions.permissions.model.Permission import com.adrianwitaszak.kmmpermissions.permissions.model.PermissionState +import com.adrianwitaszak.kmmpermissions.permissions.util.PermissionRequestException import com.adrianwitaszak.kmmpermissions.permissions.util.checkPermissions import com.adrianwitaszak.kmmpermissions.permissions.util.openAppSettingsPage import com.adrianwitaszak.kmmpermissions.permissions.util.providePermissions @@ -18,14 +19,15 @@ internal class LocationBackgroundPermissionDelegate( override suspend fun getPermissionState(): PermissionState { return when (locationForegroundPermissionDelegate.getPermissionState()) { PermissionState.GRANTED -> - checkPermissions(context, activity, backgroundLocationPermissions) + checkPermissions(context, backgroundLocationPermissions) + else -> PermissionState.NOT_DETERMINED } } override suspend fun providePermission() { activity.value.providePermissions(backgroundLocationPermissions) { - throw Exception( + throw PermissionRequestException( it.localizedMessage ?: "Failed to request background location permission" ) } @@ -40,4 +42,6 @@ internal class LocationBackgroundPermissionDelegate( private val backgroundLocationPermissions: List = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { listOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION) - } else emptyList() + } else { + emptyList() + } diff --git a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationForegroundPermissionDelegate.kt b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationForegroundPermissionDelegate.kt index 4b590ee..187b9a6 100644 --- a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationForegroundPermissionDelegate.kt +++ b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/LocationForegroundPermissionDelegate.kt @@ -6,6 +6,7 @@ import android.content.Context import android.os.Build import com.adrianwitaszak.kmmpermissions.permissions.model.Permission import com.adrianwitaszak.kmmpermissions.permissions.model.PermissionState +import com.adrianwitaszak.kmmpermissions.permissions.util.PermissionRequestException import com.adrianwitaszak.kmmpermissions.permissions.util.checkPermissions import com.adrianwitaszak.kmmpermissions.permissions.util.openAppSettingsPage import com.adrianwitaszak.kmmpermissions.permissions.util.providePermissions @@ -15,12 +16,12 @@ internal class LocationForegroundPermissionDelegate( private val activity: Lazy, ) : PermissionDelegate { override suspend fun getPermissionState(): PermissionState { - return checkPermissions(context, activity, fineLocationPermissions) + return checkPermissions(context, fineLocationPermissions) } override suspend fun providePermission() { activity.value.providePermissions(fineLocationPermissions) { - throw Exception( + throw PermissionRequestException( it.localizedMessage ?: "Failed to request foreground location permission" ) } diff --git a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/util/Extensions.android.kt b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/util/Extensions.android.kt index e00a3d4..6beb00e 100644 --- a/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/util/Extensions.android.kt +++ b/permissions/src/androidMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/util/Extensions.android.kt @@ -1,3 +1,5 @@ +@file:Suppress("TooGenericExceptionCaught") + package com.adrianwitaszak.kmmpermissions.permissions.util import android.app.Activity @@ -28,26 +30,35 @@ internal fun Context.openPage( internal fun checkPermissions( context: Context, - activity: Lazy, permissions: List, ): PermissionState { - permissions.ifEmpty { return PermissionState.GRANTED } // no permissions needed + permissions.ifEmpty { + return PermissionState.GRANTED + } // no permissions needed val status: List = permissions.map { context.checkSelfPermission(it) } - val isAllGranted: Boolean = status.all { it == PackageManager.PERMISSION_GRANTED } - if (isAllGranted) return PermissionState.GRANTED + val isAllGranted: Boolean = status.all { + it == PackageManager.PERMISSION_GRANTED + } + if (isAllGranted) { + return PermissionState.GRANTED + } return PermissionState.DENIED } +private const val REQUEST_PERMISSION_CODE = 100 + internal fun Activity.providePermissions( permissions: List, onError: (Throwable) -> Unit, ) { try { ActivityCompat.requestPermissions( - this, permissions.toTypedArray(), 100 + this, + permissions.toTypedArray(), + REQUEST_PERMISSION_CODE ) } catch (t: Throwable) { onError(t) diff --git a/permissions/src/commonMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/service/PermissionsServiceImpl.kt b/permissions/src/commonMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/service/PermissionsServiceImpl.kt index 0526555..5782fd6 100644 --- a/permissions/src/commonMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/service/PermissionsServiceImpl.kt +++ b/permissions/src/commonMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/service/PermissionsServiceImpl.kt @@ -1,3 +1,5 @@ +@file:Suppress("TooGenericExceptionCaught") + package com.adrianwitaszak.kmmpermissions.permissions.service import com.adrianwitaszak.kmmpermissions.permissions.model.Permission @@ -8,14 +10,13 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import org.koin.core.logger.Logger import org.koin.mp.KoinPlatformTools internal class PermissionsServiceImpl : PermissionsService, KoinComponent { - val logger : Logger = KoinPlatformTools.defaultLogger() + private val logger: Logger = KoinPlatformTools.defaultLogger() - suspend fun checkPermission(permission: Permission): PermissionState { + private suspend fun checkPermission(permission: Permission): PermissionState { return try { return getPermissionDelegate(permission).getPermissionState() } catch (e: Exception) { @@ -38,8 +39,8 @@ internal class PermissionsServiceImpl : PermissionsService, KoinComponent { try { getPermissionDelegate(permission).providePermission() } catch (e: Exception) { - println("Failed to request permission $permission") - e.printStackTrace() + logger.error("Failed to request permission $permission") + logger.error(e.stackTraceToString()) } } @@ -48,8 +49,8 @@ internal class PermissionsServiceImpl : PermissionsService, KoinComponent { try { getPermissionDelegate(permission).openSettingPage() } catch (e: Exception) { - println("Failed to open settings for permission $permission") - e.printStackTrace() + logger.error("Failed to open settings for permission $permission") + logger.error(e.stackTraceToString()) } } } diff --git a/permissions/src/iosMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothServicePermissionDelegate.kt b/permissions/src/iosMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothServicePermissionDelegate.kt index d402c0d..8ef5951 100644 --- a/permissions/src/iosMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothServicePermissionDelegate.kt +++ b/permissions/src/iosMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/BluetoothServicePermissionDelegate.kt @@ -24,9 +24,14 @@ internal class BluetoothServicePermissionDelegate : PermissionDelegate { CBCentralManager.authorization == CBManagerAuthorizationAllowedAlways || CBCentralManager.authorization == CBManagerAuthorizationRestricted return if (hasBluetoothPermissionGranted) { - if (cbCentralManager.state() == CBManagerStatePoweredOn) - PermissionState.GRANTED else PermissionState.DENIED - } else PermissionState.NOT_DETERMINED + if (cbCentralManager.state() == CBManagerStatePoweredOn) { + PermissionState.GRANTED + } else { + PermissionState.DENIED + } + } else { + PermissionState.NOT_DETERMINED + } } override suspend fun providePermission() { diff --git a/permissions/src/jsMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt b/permissions/src/jsMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt index 4569963..a2ce610 100644 --- a/permissions/src/jsMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt +++ b/permissions/src/jsMain/kotlin/com/adrianwitaszak/kmmpermissions/permissions/delegate/AudioRecordPermissionDelegate.kt @@ -4,45 +4,36 @@ import com.adrianwitaszak.kmmpermissions.permissions.model.PermissionState import js.errors.TypeError import js.promise.await import web.audio.AudioContext -import web.audio.MediaStreamAudioSourceNode -import web.media.devices.MediaDevices import web.navigator.navigator -import web.window.window - -class AudioRecordPermissionDelegate : PermissionDelegate { - var overrideState = PermissionState.NOT_DETERMINED - - companion object { - - } +class AudioRecordPermissionDelegate : PermissionDelegate { + private var overrideState = PermissionState.NOT_DETERMINED override suspend fun getPermissionState(): PermissionState { - if(overrideState == PermissionState.GRANTED) { + if (overrideState == PermissionState.GRANTED) { return overrideState } return try { - when (navigator.permissions.query(js("{ name: \"microphone\" }")) - .await().state.toString()) { + when ( + navigator.permissions.query(js("{ name: \"microphone\" }")) + .await().state.toString() + ) { "granted" -> PermissionState.GRANTED else -> PermissionState.DENIED } - } catch (e : TypeError) { + } catch (ignore: TypeError) { PermissionState.NOT_DETERMINED } } override suspend fun providePermission() { val audioContext = AudioContext() - navigator.mediaDevices.getUserMedia(js("{ audio: true, video: false }")).then { - stream -> audioContext.createMediaStreamSource(stream) + navigator.mediaDevices.getUserMedia(js("{ audio: true, video: false }")).then { stream -> + audioContext.createMediaStreamSource(stream) overrideState = PermissionState.GRANTED } } override fun openSettingPage() { - } - - } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 9969161..fb7839c 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -21,9 +21,7 @@ kotlin { } listOf( - iosX64(), - iosArm64(), - iosSimulatorArm64() + iosX64(), iosArm64(), iosSimulatorArm64() ).forEach { iosTarget -> iosTarget.binaries.framework { baseName = "shared" @@ -41,8 +39,9 @@ kotlin { api(compose.foundation) implementation(libs.appyx.navigation) api(libs.appyx.components.backstack) - @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) - implementation(compose.components.resources) + @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) implementation( + compose.components.resources + ) implementation(libs.koin.core) } } @@ -52,8 +51,9 @@ kotlin { implementation(kotlin("test")) implementation(libs.kotlinx.coroutines.test) - @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) - implementation(compose.components.resources) + @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) implementation( + compose.components.resources + ) } } val iosX64Main by getting @@ -115,4 +115,4 @@ sqldelight { packageName.set("org.noise_planet.noisecapture") } } -} \ No newline at end of file +} diff --git a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidAudioSource.kt b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidAudioSource.kt similarity index 59% rename from shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidAudioSource.kt rename to shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidAudioSource.kt index 4dd7775..f76e34d 100644 --- a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidAudioSource.kt +++ b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidAudioSource.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import android.annotation.SuppressLint import android.media.AudioFormat @@ -16,18 +16,21 @@ import org.koin.core.logger.Logger import java.util.concurrent.atomic.AtomicBoolean const val BUFFER_SIZE_TIME = 0.1 -class AndroidAudioSource(val logger : Logger) : AudioSource { + +class AndroidAudioSource(private val logger: Logger) : AudioSource { + private val recording = AtomicBoolean(false) override suspend fun setup(): Flow { - val audioSamplesChannel = Channel(onBufferOverflow = BufferOverflow.DROP_OLDEST) + val audioSamplesChannel = + Channel(onBufferOverflow = BufferOverflow.DROP_OLDEST) val audioThread = AudioThread(recording, audioSamplesChannel, logger) audioThread.startAudioRecord() return audioSamplesChannel.consumeAsFlow() } - fun readErrorCodeToString(errorCode : Int) : String { - return when(errorCode) { + fun readErrorCodeToString(errorCode: Int): String { + return when (errorCode) { ERROR_INVALID_OPERATION -> "ERROR_INVALID_OPERATION" ERROR_BAD_VALUE -> "ERROR_BAD_VALUE" ERROR -> "Other Error" @@ -42,19 +45,22 @@ class AndroidAudioSource(val logger : Logger) : AudioSource { override fun getMicrophoneLocation(): AudioSource.MicrophoneLocation { return AudioSource.MicrophoneLocation.LOCATION_UNKNOWN } - } -class AudioThread(val recording: AtomicBoolean, val audioSamplesChannel : Channel, val logger : Logger) : Runnable { +class AudioThread( + private val recording: AtomicBoolean, + private val audioSamplesChannel: Channel, + private val logger: Logger, +) : Runnable { private lateinit var audioRecord: AudioRecord private var bufferSize = -1 private var sampleRate = -1 - private var thread : Thread? = null + private var thread: Thread? = null @SuppressLint("MissingPermission") fun startAudioRecord() { - if(this.bufferSize == -1) { + if (this.bufferSize == -1) { val mSampleRates = intArrayOf(48000, 44100) val channel = AudioFormat.CHANNEL_IN_MONO val encoding = AudioFormat.ENCODING_PCM_FLOAT @@ -75,7 +81,7 @@ class AudioThread(val recording: AtomicBoolean, val audioSamplesChannel : Channe ) thread = Thread(this) thread!!.start() - break + return } } } @@ -84,48 +90,17 @@ class AudioThread(val recording: AtomicBoolean, val audioSamplesChannel : Channe recording.set(true) try { Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO) - } catch (ex: IllegalArgumentException) { + } catch (ignore: IllegalArgumentException) { // Ignore - } catch (ex: SecurityException) { + } catch (ignore: SecurityException) { // Ignore } try { audioRecord.startRecording() - println("Capture microphone") - var buffer = FloatArray(bufferSize / 4) + logger.debug("Capture microphone") while (recording.get()) { - val read: Int = audioRecord.read( - buffer, 0, buffer.size, - AudioRecord.READ_BLOCKING - ) - if (read < buffer.size) { - if (read > 0) { - buffer = buffer.copyOfRange(0, read) - audioSamplesChannel.trySend( - AudioSamples( - System.currentTimeMillis(), - buffer, - AudioSamples.ErrorCode.OK, - sampleRate - ) - ) - } else { - audioSamplesChannel.trySend( - AudioSamples( - System.currentTimeMillis(), - buffer.clone(), AudioSamples.ErrorCode.ABORTED, sampleRate - ) - ) - recording.set(false) - break - } - } else { - audioSamplesChannel.trySend( - AudioSamples( - System.currentTimeMillis(), - buffer.clone(), AudioSamples.ErrorCode.OK, sampleRate - ) - ) + if (!processRecord()) { + break } } bufferSize = -1 @@ -136,11 +111,54 @@ class AudioThread(val recording: AtomicBoolean, val audioSamplesChannel : Channe ) ) audioRecord.stop() - } catch (e : IllegalStateException) { + } catch (e: IllegalStateException) { logger.error("${e.localizedMessage}\n${e.stackTraceToString()}") } recording.set(false) - println("Release microphone") + logger.debug("Release microphone") } -} \ No newline at end of file + private fun processRecord(): Boolean { + var buffer = FloatArray(bufferSize / 4) + val read: Int = audioRecord.read( + buffer, + 0, + buffer.size, + AudioRecord.READ_BLOCKING + ) + if (read < buffer.size) { + if (read > 0) { + buffer = buffer.copyOfRange(0, read) + audioSamplesChannel.trySend( + AudioSamples( + System.currentTimeMillis(), + buffer, + AudioSamples.ErrorCode.OK, + sampleRate + ) + ) + } else { + audioSamplesChannel.trySend( + AudioSamples( + System.currentTimeMillis(), + buffer.clone(), + AudioSamples.ErrorCode.ABORTED, + sampleRate + ) + ) + recording.set(false) + return false + } + } else { + audioSamplesChannel.trySend( + AudioSamples( + System.currentTimeMillis(), + buffer.clone(), + AudioSamples.ErrorCode.OK, + sampleRate + ) + ) + } + return true + } +} diff --git a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidDatabase.kt b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidDatabase.kt similarity index 91% rename from shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidDatabase.kt rename to shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidDatabase.kt index 0572cfe..ba3653c 100644 --- a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidDatabase.kt +++ b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidDatabase.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import android.content.Context import androidx.sqlite.db.SupportSQLiteDatabase @@ -9,8 +9,9 @@ import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.android.AndroidSqliteDriver class AndroidDatabase(private val context: Context) : DatabaseDriverFactory { + override suspend fun provideDbDriver(): SqlDriver { - val callback = object: Callback(DATABASE_VERSION) { + val callback = object : Callback(DATABASE_VERSION) { override fun onCreate(db: SupportSQLiteDatabase) { } @@ -27,4 +28,4 @@ class AndroidDatabase(private val context: Context) : DatabaseDriverFactory { ) ) } -} \ No newline at end of file +} diff --git a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidMeasurementService.kt b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidMeasurementService.kt similarity index 90% rename from shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidMeasurementService.kt rename to shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidMeasurementService.kt index 9140a05..4c03897 100644 --- a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/AndroidMeasurementService.kt +++ b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/AndroidMeasurementService.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture; +package org.noiseplanet.noisecapture; import android.R import android.app.Notification @@ -18,19 +18,24 @@ import androidx.core.app.NotificationCompat /** * Android service that will not close when app is sent in background */ -typealias AudioCallBack = (audioSamples : AudioSamples) -> Unit +typealias AudioCallBack = (audioSamples: AudioSamples) -> Unit class AndroidMeasurementService : Service() { + private val binder: IBinder = LocalBinder() private var mNM: NotificationManager? = null private var notificationInstance: Notification? = null private var bindingIntent: Intent? = null - // Unique Identification Number for the Notification. - // We use it on Notification start, and to cancel it. - private val NOTIFICATION: Int = 1 + private companion object { + + // Unique Identification Number for the Notification. + // We use it on Notification start, and to cancel it. + const val NOTIFICATION: Int = 1 + } inner class LocalBinder : Binder() { + val service: AndroidMeasurementService get() = this@AndroidMeasurementService } @@ -66,8 +71,6 @@ class AndroidMeasurementService : Service() { override fun onDestroy() { println("Destroy MeasurementService") - - // Cancel the persistent notification. mNM?.cancel(NOTIFICATION) } @@ -83,7 +86,7 @@ class AndroidMeasurementService : Service() { this, 0, bindingIntent, PendingIntent.FLAG_IMMUTABLE ) - if(notificationInstance == null) { + if (notificationInstance == null) { var channelId = "" // If earlier version channel ID is not used @@ -93,6 +96,7 @@ class AndroidMeasurementService : Service() { } // Set the info for the views that show in the notification panel. notificationInstance = NotificationCompat.Builder(this, channelId) + // TODO: Use KMP compose resources .setSmallIcon(R.drawable.stat_notify_voicemail) // the status icon .setTicker(text) // the status text .setWhen(System.currentTimeMillis()) // the time stamp diff --git a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.android.kt b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.android.kt similarity index 90% rename from shared/src/androidMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.android.kt rename to shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.android.kt index baf5cdd..372ea78 100644 --- a/shared/src/androidMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.android.kt +++ b/shared/src/androidMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.android.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import android.graphics.Bitmap import android.graphics.BitmapFactory diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/HomeScreen.kt b/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/HomeScreen.kt deleted file mode 100644 index 6bcc6d5..0000000 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/HomeScreen.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.noise_planet.noisecapture.shared.child - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.material.Button -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.unit.dp -import com.bumble.appyx.components.backstack.BackStack -import com.bumble.appyx.components.backstack.operation.push -import com.bumble.appyx.navigation.modality.NodeContext -import com.bumble.appyx.navigation.node.LeafNode -import org.noise_planet.noisecapture.shared.ScreenData -import org.noise_planet.noisecapture.shared.ui.theme.* - -class HomeScreen( - nodeContext: NodeContext, - private val backStack: BackStack -) : LeafNode(nodeContext) { - - @Composable - override fun Content(modifier: Modifier) { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colors.background - ) { - val menuItems = arrayOf( - MenuItem("Test label", Mic, - onClick = { backStack.push(ScreenData.PermissionTarget) }), - MenuItem("Measurement History", overview(), - onClick = {}), - MenuItem("Measurement feedback", clinicalNotes(), - onClick = {}), - MenuItem("Measurement statistics", ShowChart, - onClick = {}), - MenuItem("Last measurement map", Map, - onClick = {}), - MenuItem("Help", Help, - onClick = {}), - MenuItem("About", Info, - onClick = {}), - MenuItem("Calibration", CenterFocusWeak, - onClick = {}), - MenuItem("Settings", Settings, - onClick = {}) - ) - - LazyVerticalGrid( - columns = GridCells.Adaptive(minSize = 96.dp), - contentPadding = PaddingValues( - start = 24.dp, - top = 24.dp, - end = 24.dp, - bottom = 24.dp - ), - content = { - items(menuItems.size) { - index -> - Button( - onClick = menuItems[index].onClick, - modifier = Modifier.aspectRatio(1f).padding(12.dp) - ) { - Icon( - imageVector = menuItems[index].imageVector, - menuItems[index].label, modifier.fillMaxSize() - ) - } - - } - }) - } - } -} - -data class MenuItem(val label:String, val imageVector: ImageVector, val onClick: () -> Unit) diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/Bluestein.kt b/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/Bluestein.kt deleted file mode 100644 index 0a91138..0000000 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/Bluestein.kt +++ /dev/null @@ -1,134 +0,0 @@ -package org.noise_planet.noisecapture.shared.signal - -import kotlin.math.PI -import kotlin.math.atan -import kotlin.math.cos -import kotlin.math.exp -import kotlin.math.ln -import kotlin.math.log -import kotlin.math.max -import kotlin.math.pow -import kotlin.math.sin -import kotlin.math.sqrt - -/** - * Chirp transform for non power of two fft - * @see https://gist.github.com/fnielsen/99b981b9da34ae3d5035 - */ -class Bluestein(val window_length : Int) { - companion object { - operator fun Double.plus(other: Complex) = Complex(this + other.real, other.imag) - operator fun Double.minus(other: Complex) = Complex(this - other.real, -other.imag) - operator fun Double.times(other: Complex) = Complex( - this * other.real - this * other.imag, - this * other.imag + this * other.real - ) - operator fun Double.div(other: Complex) = Complex((this * other.real) / - (other.real * other.real + other.imag * other.imag), - (- this * other.imag) / (other.real * other.real + other.imag * other.imag)) - - data class Complex(val real: Double, val imag: Double) { - operator fun plus(other: Complex) = Complex(real + other.real, imag + other.imag) - operator fun minus(other: Complex) = Complex(real - other.real, imag - other.imag) - operator fun times(other: Complex) = Complex( - real * other.real - imag * other.imag, - real * other.imag + imag * other.real - ) - operator fun times(d: Double):Complex =Complex(real * d, imag * d ) - - operator fun div(c: Double):Complex = Complex(real / c, imag / c) - operator fun div(other: Complex) = Complex( - (real * other.real + imag * other.imag) / (other.real * other.real + other.imag * other.imag), - (imag * other.real - real * other.imag) / (other.real * other.real + other.imag * other.imag) - ) - - fun module():Double = sqrt(real.pow(2) + imag.pow(2)) - fun arg(): Double { - return when { - this.real==0.0 && this.imag>0.0 -> PI/2 - this.real==0.0 && this.imag<0.0 -> -PI/2 - this.real>0.0 -> atan(this.imag/this.real) - this.real<0.0 && this.imag>=0.0 -> atan(this.imag/this.real)+ PI - this.real<0.0 && this.imag<0.0 -> atan(this.imag/this.real)-PI - else -> 0.0 - } - } - fun ln():Complex { - return Complex(ln(this.module()),arg()) - } - - fun exp():Complex { - val r: Double = exp(this.real) - return Complex(r*cos(this.imag),r*sin(this.imag)) - } - fun pow(c:Complex):Complex = (c*this.ln()).exp() - fun pow(c:Double):Complex = (this.ln()*c).exp() - - operator fun unaryMinus() = Complex(-real, -imag) - - override fun toString() = "$real ${if(imag>=0)'+' else ' '} ${imag}i" - } - - val Double.im get() = Complex(0.0, this) - } - val n = window_length - val m = n - val w = (Complex(0.0, -2.0) * PI / m.toDouble()).exp() - val a = 1.0 - val chirp = ((1-n).. val c = w.pow(i.toDouble().pow(2) / 2.0) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - realImagArray - } - val n2 = nextPowerOfTwo(m + n - 1) - val ichirp = (0..< n2).foldIndexed(DoubleArray(n2*2)) { - index, realImagArray, i -> - if(i < m+n-1) { - val c = 1.0 / Complex(chirp[index*2], chirp[index*2+1]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - } - realImagArray - } - - init { - fft(ichirp.size/2, ichirp) - } - - fun fft(x : DoubleArray) : DoubleArray { - require(x.size == window_length * 2) - val xp = (0..< n2).foldIndexed(DoubleArray(n2*2)) { - index, realImagArray, i -> - if(i < n) { - val realIndex = index*2 - val imIndex = index*2+1 - val chirpOffset = (n-1)*2 - val c = Complex(x[realIndex], x[imIndex]) * Complex(chirp[chirpOffset+realIndex], chirp[chirpOffset+imIndex]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - } - realImagArray - } - fft(xp.size/2, xp) - val r = (0..< n2).foldIndexed(DoubleArray(n2*2)) { - index, realImagArray, i -> - val realIndex = index*2 - val imIndex = index*2+1 - val c = Complex(xp[realIndex], xp[imIndex]) * Complex(ichirp[realIndex], ichirp[imIndex]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - realImagArray - } - iFFT(r.size/2, r) - return (n-1..< m+n-1).foldIndexed(DoubleArray(window_length*2)) { - index, realImagArray, i -> - val realIndex = i*2 - val imIndex = i*2+1 - val c = Complex(r[realIndex], r[imIndex]) * Complex(chirp[realIndex], chirp[imIndex]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - realImagArray - } - } -} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/BluesteinFloat.kt b/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/BluesteinFloat.kt deleted file mode 100644 index 9185fd5..0000000 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/BluesteinFloat.kt +++ /dev/null @@ -1,134 +0,0 @@ -package org.noise_planet.noisecapture.shared.signal - -import kotlin.math.PI -import kotlin.math.atan -import kotlin.math.cos -import kotlin.math.exp -import kotlin.math.ln -import kotlin.math.log -import kotlin.math.max -import kotlin.math.pow -import kotlin.math.sin -import kotlin.math.sqrt - -/** - * Chirp transform for non power of two fft - * @see https://gist.github.com/fnielsen/99b981b9da34ae3d5035 - */ -class BluesteinFloat(val window_length : Int) { - companion object { - - val PIF = PI.toFloat() - operator fun Float.plus(other: Complex) = Complex(this + other.real, other.imag) - operator fun Float.minus(other: Complex) = Complex(this - other.real, -other.imag) - operator fun Float.times(other: Complex) = Complex( - this * other.real - this * other.imag, - this * other.imag + this * other.real - ) - operator fun Float.div(other: Complex) = Complex(this,0.0F) / other - - data class Complex(val real: Float, val imag: Float) { - operator fun plus(other: Complex) = Complex(real + other.real, imag + other.imag) - operator fun minus(other: Complex) = Complex(real - other.real, imag - other.imag) - operator fun times(other: Complex) = Complex( - real * other.real - imag * other.imag, - real * other.imag + imag * other.real - ) - operator fun times(d: Float):Complex =Complex(real * d, imag * d ) - - operator fun div(c: Float):Complex = Complex(real / c, imag / c) - operator fun div(other: Complex) = Complex( - (real * other.real + imag * other.imag) / (other.real * other.real + other.imag * other.imag), - (imag * other.real - real * other.imag) / (other.real * other.real + other.imag * other.imag) - ) - - fun module():Float = sqrt(real.pow(2) + imag.pow(2)) - fun arg(): Float { - return when { - this.real==0.0F && this.imag>0.0 -> PIF/2 - this.real==0.0F && this.imag<0.0 -> -PIF/2 - this.real>0.0F -> atan(this.imag/this.real) - this.real<0.0F && this.imag>=0.0 -> atan(this.imag/this.real)+ PIF - this.real<0.0F && this.imag<0.0 -> atan(this.imag/this.real)-PIF - else -> 0.0F - } - } - fun ln():Complex { - return Complex(ln(this.module()),arg()) - } - - fun exp():Complex { - val r: Float = exp(this.real) - return Complex(r*cos(this.imag),r*sin(this.imag)) - } - fun pow(c:Complex):Complex = (c*this.ln()).exp() - fun pow(c:Float):Complex = (this.ln()*c).exp() - - operator fun unaryMinus() = Complex(-real, -imag) - - override fun toString() = "$real ${if(imag>=0)'+' else ' '} ${imag}i" - } - - val Float.im get() = Complex(0.0F, this) - } - val n = window_length - val m = n - val w = (Complex(0.0F, -2.0F) * PIF / m.toFloat()).exp() - val a = 1.0F - val chirp = ((1-n).. val c = w.pow(i.toFloat().pow(2) / 2.0F) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - realImagArray - } - val n2 = nextPowerOfTwo(m + n - 1) - val ichirp = (0..< n2).foldIndexed(FloatArray(n2*2)) { - index, realImagArray, i -> - if(i < m+n-1) { - val c = 1.0F / Complex(chirp[index*2], chirp[index*2+1]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - } - realImagArray - } - - init { - fftFloat(ichirp.size/2, ichirp) - } - - fun fft(x : FloatArray) : FloatArray { - require(x.size == window_length * 2) - val xp = (0..< n2).foldIndexed(FloatArray(n2*2)) { - index, realImagArray, i -> - if(i < n) { - val realIndex = index*2 - val imIndex = index*2+1 - val chirpOffset = (n-1)*2 - val c = Complex(x[realIndex], x[imIndex]) * a.pow(-i) * Complex(chirp[chirpOffset+realIndex], chirp[chirpOffset+imIndex]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - } - realImagArray - } - fftFloat(xp.size/2, xp) - val r = (0..< n2).foldIndexed(FloatArray(n2*2)) { - index, realImagArray, i -> - val realIndex = index*2 - val imIndex = index*2+1 - val c = Complex(xp[realIndex], xp[imIndex]) * Complex(ichirp[realIndex], ichirp[imIndex]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - realImagArray - } - iFFTFloat(r.size/2, r) - return (n-1..< m+n-1).foldIndexed(FloatArray(window_length*2)) { - index, realImagArray, i -> - val realIndex = i*2 - val imIndex = i*2+1 - val c = Complex(r[realIndex], r[imIndex]) * Complex(chirp[realIndex], chirp[imIndex]) - realImagArray[index*2] = c.real - realImagArray[index*2+1] = c.imag - realImagArray - } - } -} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt b/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt deleted file mode 100644 index 02fa8e7..0000000 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.noise_planet.noisecapture.shared.signal - -import kotlin.math.log10 -import kotlin.math.pow - - -const val FAST_DECAY_RATE = -34.7 -const val SLOW_DECAY_RATE = -4.3 - -/** - * IEC 61672-1 standard for displayed sound level decay - */ -class LevelDisplayWeightedDecay(decibelDecayPerSecond:Double, newValueTimeInterval : Double) { - val time_weight = 10.0.pow(decibelDecayPerSecond * newValueTimeInterval / 10.0) - var time_integration = 0.0 - - fun getWeightedValue(newValue : Double) : Double { - time_integration = time_integration * time_weight + 10.0.pow(newValue/10.0) * (1-time_weight) - return 10*log10(time_integration) - } - -} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt b/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt deleted file mode 100644 index c9f42b0..0000000 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt +++ /dev/null @@ -1,127 +0,0 @@ -package org.noise_planet.noisecapture.shared.signal - - - -data class SpectrumChannelConfiguration ( - val bandpass: List, - val antiAliasing: AntiAliasing, - val aWeighting: DigitalFilterConfiguration?, - val cWeighting: DigitalFilterConfiguration?, - val configuration: GeneralConfiguration -) - -data class DigitalFilterConfiguration( - val filterDenominator: DoubleArray, - val filterNumerator: DoubleArray -) - -data class AntiAliasing( - val a1: DoubleArray, - val a2: DoubleArray, - val b0: DoubleArray, - val b1: DoubleArray, - val b2: DoubleArray, - val sampleRatio: Int -) - -data class Bandpass( - val centerFrequency: Double, - val maxFrequency: Double, - val minFrequency: Double, - val nominalFrequency: Double, - val subsamplingDepth: Int, - val sos: Sos, - val subsamplingFilter: SubsamplingFilter -) - -data class GeneralConfiguration( - val sampleRate: Int -) - -data class Sos( - val a1: DoubleArray, - val a2: DoubleArray, - val b0: DoubleArray, - val b1: DoubleArray, - val b2: DoubleArray -) - -data class SubsamplingFilter( - val centerFrequency: Double, - val maxFrequency: Double, - val minFrequency: Double, - val nominalFrequency: Double, - val sos: Sos -) - - -fun get44100HZ() : SpectrumChannelConfiguration { - return SpectrumChannelConfiguration( - bandpass=listOf( - Bandpass(49.60628287400625,55.681169883771226,44.19417382415923,50.0,0,Sos(a1=doubleArrayOf(-1.9983198086546723,-1.9984209770619399,-1.99868995344204,-1.9988956714388693,-1.9994669879419251,-1.9995836340086606), a2=doubleArrayOf(0.9983728225106281,0.9984679700150535,0.9987487770098534,0.9989380410501625,0.9995294236302933,0.999623581666138), b0=doubleArrayOf(2.993188043570381e-19,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.986376087140762e-19,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.993188043570381e-19,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(49.60628287400625,55.681169883771226,44.19417382415923,50.0,sos=Sos(a1=doubleArrayOf(-1.9983198086546723,-1.9984209770619399,-1.99868995344204,-1.9988956714388693,-1.9994669879419251,-1.9995836340086606), a2=doubleArrayOf(0.9983728225106281,0.9984679700150535,0.9987487770098534,0.9989380410501625,0.9995294236302933,0.999623581666138), b0=doubleArrayOf(2.993188043570381e-19,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.986376087140762e-19,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.993188043570381e-19,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(62.5,70.15387801933582,55.681169883771204,63.0,0,Sos(a1=doubleArrayOf(-1.997866182386905,-1.9979955649826082,-1.9983304542326876,-1.9985949509811112,-1.9993080437878314,-1.9994623556213569), a2=doubleArrayOf(0.9979503186377896,0.9980701466331582,0.9984238153827981,0.9986621991296034,0.9994071479024906,0.9995257653492752), b0=doubleArrayOf(1.196292648558012e-18,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.392585297116024e-18,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.196292648558012e-18,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(62.5,70.15387801933582,55.681169883771204,63.0,sos=Sos(a1=doubleArrayOf(-1.997866182386905,-1.9979955649826082,-1.9983304542326876,-1.9985949509811112,-1.9993080437878314,-1.9994623556213569), a2=doubleArrayOf(0.9979503186377896,0.9980701466331582,0.9984238153827981,0.9986621991296034,0.9994071479024906,0.9995257653492752), b0=doubleArrayOf(1.196292648558012e-18,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.392585297116024e-18,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.196292648558012e-18,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(78.74506561842958,88.38834764831844,70.15387801933582,80.0,0,Sos(a1=doubleArrayOf(-1.9972847298727139,-1.9974507843363472,-1.9978663704766202,-1.9982080364995807,-1.9990958070523397,-1.9993018875143298), a2=doubleArrayOf(0.997418251755548,0.9975691452302478,0.9980145410531994,0.9983147673823352,0.9992531121476155,0.999402537668409), b0=doubleArrayOf(4.780224578976738e-18,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.560449157953475e-18,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.780224578976738e-18,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(78.74506561842958,88.38834764831844,70.15387801933582,80.0,sos=Sos(a1=doubleArrayOf(-1.9972847298727139,-1.9974507843363472,-1.9978663704766202,-1.9982080364995807,-1.9990958070523397,-1.9993018875143298), a2=doubleArrayOf(0.997418251755548,0.9975691452302478,0.9980145410531994,0.9983147673823352,0.9992531121476155,0.999402537668409), b0=doubleArrayOf(4.780224578976738e-18,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.560449157953475e-18,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.780224578976738e-18,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(99.21256574801247,111.36233976754241,88.38834764831843,100.0,0,Sos(a1=doubleArrayOf(-1.9965364131606704,-1.996750453198385,-1.9972639850393383,-1.9977078138459268,-1.9988093943603864,-1.9990875422352576), a2=doubleArrayOf(0.996748293529012,0.9969382790329787,0.9974991288319082,0.9978772006004107,0.999059074482199,0.9992473012061489), b0=doubleArrayOf(1.9096008867615595e-17,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.819201773523119e-17,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.9096008867615595e-17,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(99.21256574801247,111.36233976754241,88.38834764831843,100.0,sos=Sos(a1=doubleArrayOf(-1.9965364131606704,-1.996750453198385,-1.9972639850393383,-1.9977078138459268,-1.9988093943603864,-1.9990875422352576), a2=doubleArrayOf(0.996748293529012,0.9969382790329787,0.9974991288319082,0.9978772006004107,0.999059074482199,0.9992473012061489), b0=doubleArrayOf(1.9096008867615595e-17,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.819201773523119e-17,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.9096008867615595e-17,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(125.0,140.30775603867164,111.36233976754241,125.0,0,Sos(a1=doubleArrayOf(-1.9955686463431954,-1.9958459676577474,-1.9964769915778326,-1.9970573610880828,-1.9984183696347848,-1.9987981731888196), a2=doubleArrayOf(0.9959048400703698,0.9961440013871193,0.9968501336181875,0.9973261694669386,0.9988146588348366,0.999051747964678), b0=doubleArrayOf(7.625883439127173e-17,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.5251766878254346e-16,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.625883439127173e-17,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(125.0,140.30775603867164,111.36233976754241,125.0,sos=Sos(a1=doubleArrayOf(-1.9955686463431954,-1.9958459676577474,-1.9964769915778326,-1.9970573610880828,-1.9984183696347848,-1.9987981731888196), a2=doubleArrayOf(0.9959048400703698,0.9961440013871193,0.9968501336181875,0.9973261694669386,0.9988146588348366,0.999051747964678), b0=doubleArrayOf(7.625883439127173e-17,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.5251766878254346e-16,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.625883439127173e-17,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(157.49013123685916,176.7766952966369,140.30775603867164,160.0,0,Sos(a1=doubleArrayOf(-1.9943097900886524,-1.9946713116001737,-1.9954409879611368,-1.9962057847910375,-1.997877844403779,-1.9984029467524744), a2=doubleArrayOf(0.9948431721342493,0.9951441671210961,0.9960330611133081,0.9966323377918527,0.9985068052423528,0.9988054170367806), b0=doubleArrayOf(3.044058450203638e-16,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(6.088116900407276e-16,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.044058450203638e-16,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(157.49013123685916,176.7766952966369,140.30775603867164,160.0,sos=Sos(a1=doubleArrayOf(-1.9943097900886524,-1.9946713116001737,-1.9954409879611368,-1.9962057847910375,-1.997877844403779,-1.9984029467524744), a2=doubleArrayOf(0.9948431721342493,0.9951441671210961,0.9960330611133081,0.9966323377918527,0.9985068052423528,0.9988054170367806), b0=doubleArrayOf(3.044058450203638e-16,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(6.088116900407276e-16,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.044058450203638e-16,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(198.42513149602493,222.72467953508482,176.77669529663686,200.0,0,Sos(a1=doubleArrayOf(-1.9926610639212046,-1.993135746076362,-1.9940652391539457,-1.995082032908412,-1.997120891752752,-1.997856368425131), a2=doubleArrayOf(0.9935071670438599,0.9938858674931755,0.995004585865703,0.995758833577239,0.9981190804995169,0.9984951384537887), b0=doubleArrayOf(1.214460313047029e-15,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.428920626094058e-15,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.214460313047029e-15,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(198.42513149602493,222.72467953508482,176.77669529663686,200.0,sos=Sos(a1=doubleArrayOf(-1.9926610639212046,-1.993135746076362,-1.9940652391539457,-1.995082032908412,-1.997120891752752,-1.997856368425131), a2=doubleArrayOf(0.9935071670438599,0.9938858674931755,0.995004585865703,0.995758833577239,0.9981190804995169,0.9984951384537887), b0=doubleArrayOf(1.214460313047029e-15,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.428920626094058e-15,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.214460313047029e-15,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(250.0,280.6155120773433,222.72467953508482,250.0,0,Sos(a1=doubleArrayOf(-1.990484543589923,-1.9911129858369792,-1.9922202506858138,-1.9935856131585172,-1.9960467535953117,-1.9970905797877736), a2=doubleArrayOf(0.9918264632393188,0.9923027422852423,0.9937103364002111,0.9946593406583514,0.9976308150496469,0.9981043341042959), b0=doubleArrayOf(4.841959696459329e-15,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.683919392918658e-15,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.841959696459329e-15,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(250.0,280.6155120773433,222.72467953508482,250.0,sos=Sos(a1=doubleArrayOf(-1.990484543589923,-1.9911129858369792,-1.9922202506858138,-1.9935856131585172,-1.9960467535953117,-1.9970905797877736), a2=doubleArrayOf(0.9918264632393188,0.9923027422852423,0.9937103364002111,0.9946593406583514,0.9976308150496469,0.9981043341042959), b0=doubleArrayOf(4.841959696459329e-15,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.683919392918658e-15,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.841959696459329e-15,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(314.9802624737183,353.5533905932738,280.6155120773433,315.0,0,Sos(a1=doubleArrayOf(-1.9875851916090912,-1.9884250270785415,-1.9897188898480878,-1.9915725553870065,-1.9945024522551325,-1.9960033805175177), a2=doubleArrayOf(0.9897129626266903,0.9903116554314061,0.9920821533812527,0.9932757218314784,0.9970160247033005,0.997612139139008), b0=doubleArrayOf(1.9288175446367492e-14,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.8576350892734984e-14,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.9288175446367492e-14,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(314.9802624737183,353.5533905932738,280.6155120773433,315.0,sos=Sos(a1=doubleArrayOf(-1.9875851916090912,-1.9884250270785415,-1.9897188898480878,-1.9915725553870065,-1.9945024522551325,-1.9960033805175177), a2=doubleArrayOf(0.9897129626266903,0.9903116554314061,0.9920821533812527,0.9932757218314784,0.9970160247033005,0.997612139139008), b0=doubleArrayOf(1.9288175446367492e-14,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.8576350892734984e-14,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.9288175446367492e-14,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(396.8502629920499,445.44935907016975,353.5533905932738,400.0,0,Sos(a1=doubleArrayOf(-1.983683760843643,-1.9848177973188836,-1.9862875464023864,-1.9888340386407348,-1.9922540564683868,-1.9944395456030453), a2=doubleArrayOf(0.9870565418362137,0.9878086030156703,0.9900347138507755,0.9915350658113805,0.996242067775943,0.9969922977121467), b0=doubleArrayOf(7.675358798811291e-14,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.5350717597622583e-13,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.675358798811291e-14,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(396.8502629920499,445.44935907016975,353.5533905932738,400.0,sos=Sos(a1=doubleArrayOf(-1.983683760843643,-1.9848177973188836,-1.9862875464023864,-1.9888340386407348,-1.9922540564683868,-1.9944395456030453), a2=doubleArrayOf(0.9870565418362137,0.9878086030156703,0.9900347138507755,0.9915350658113805,0.996242067775943,0.9969922977121467), b0=doubleArrayOf(7.675358798811291e-14,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.5350717597622583e-13,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.675358798811291e-14,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(500.0,561.2310241546866,445.44935907016963,500.0,0,Sos(a1=doubleArrayOf(-1.978375713144662,-1.9799242729184654,-1.9815219064760534,-1.9850636820103815,-1.9889417297107825,-1.9921616221501939), a2=doubleArrayOf(0.9837198346904802,0.9846637063121387,0.9874614057534,0.9893460286738955,0.9952679803456205,0.9962117744393044), b0=doubleArrayOf(3.050178389757522e-13,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(6.100356779515044e-13,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.050178389757522e-13,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(500.0,561.2310241546866,445.44935907016963,500.0,sos=Sos(a1=doubleArrayOf(-1.978375713144662,-1.9799242729184654,-1.9815219064760534,-1.9850636820103815,-1.9889417297107825,-1.9921616221501939), a2=doubleArrayOf(0.9837198346904802,0.9846637063121387,0.9874614057534,0.9893460286738955,0.9952679803456205,0.9962117744393044), b0=doubleArrayOf(3.050178389757522e-13,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(6.100356779515044e-13,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.050178389757522e-13,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(629.9605249474366,707.1067811865476,561.2310241546866,630.0,0,Sos(a1=doubleArrayOf(-1.9710687181060893,-1.9732083856639946,-1.97481898635759,-1.9798073097638182,-1.984009403826708,-1.9888042278234839), a2=doubleArrayOf(0.9795319509086966,0.9807151306083703,0.9842293382669933,0.9865943148708757,0.9940424264123358,0.9952290061875053), b0=doubleArrayOf(1.2101032033538562e-12,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.4202064067077124e-12,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.2101032033538562e-12,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(629.9605249474366,707.1067811865476,561.2310241546866,630.0,sos=Sos(a1=doubleArrayOf(-1.9710687181060893,-1.9732083856639946,-1.97481898635759,-1.9798073097638182,-1.984009403826708,-1.9888042278234839), a2=doubleArrayOf(0.9795319509086966,0.9807151306083703,0.9842293382669933,0.9865943148708757,0.9940424264123358,0.9952290061875053), b0=doubleArrayOf(1.2101032033538562e-12,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.4202064067077124e-12,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.2101032033538562e-12,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(793.7005259840998,890.8987181403395,707.1067811865476,800.0,0,Sos(a1=doubleArrayOf(-1.9608874446844493,-1.963879545064532,-1.965272672649919,-1.9723856664756494,-1.97659489715021,-1.9838025235597203), a2=doubleArrayOf(0.9742810207967747,0.9757617669177364,0.9801733960432047,0.9831371116044616,0.9925011997680455,0.9939916952442285), b0=doubleArrayOf(4.790763704092158e-12,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.581527408184316e-12,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.790763704092158e-12,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(793.7005259840998,890.8987181403395,707.1067811865476,800.0,sos=Sos(a1=doubleArrayOf(-1.9608874446844493,-1.963879545064532,-1.965272672649919,-1.9723856664756494,-1.97659489715021,-1.9838025235597203), a2=doubleArrayOf(0.9742810207967747,0.9757617669177364,0.9801733960432047,0.9831371116044616,0.9925011997680455,0.9939916952442285), b0=doubleArrayOf(4.790763704092158e-12,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.581527408184316e-12,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.790763704092158e-12,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(1000.0,1122.4620483093731,890.8987181403393,1000.0,0,Sos(a1=doubleArrayOf(-1.9465287395721516,-1.9507624588529555,-1.9515135594143518,-1.9617755601288898,-1.9653587645094899,-1.9762804069485385), a2=doubleArrayOf(0.9677055193767125,0.9695545234083117,0.9750893095099044,0.9787962486896891,0.9905642285037186,0.9924340097579212), b0=doubleArrayOf(1.8916509736126712e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.7833019472253423e-11,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.8916509736126712e-11,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(1000.0,1122.4620483093731,890.8987181403393,1000.0,sos=Sos(a1=doubleArrayOf(-1.9465287395721516,-1.9507624588529555,-1.9515135594143518,-1.9617755601288898,-1.9653587645094899,-1.9762804069485385), a2=doubleArrayOf(0.9677055193767125,0.9695545234083117,0.9750893095099044,0.9787962486896891,0.9905642285037186,0.9924340097579212), b0=doubleArrayOf(1.8916509736126712e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.7833019472253423e-11,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.8916509736126712e-11,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(1259.9210498948732,1414.213562373095,1122.4620483093731,1250.0,0,Sos(a1=doubleArrayOf(-1.9260424262170373,-1.9320996286683723,-1.9314647877308484,-1.9464276256666304,-1.9482192536879337,-1.9648762820351429), a2=doubleArrayOf(0.959484453052192,0.9617861010981933,0.9687258551697253,0.9733497927864203,0.9881320776548521,0.9904730011558084), b0=doubleArrayOf(7.444638326450626e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.4889276652901253e-10,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.444638326450626e-11,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(1259.9210498948732,1414.213562373095,1122.4620483093731,1250.0,sos=Sos(a1=doubleArrayOf(-1.9260424262170373,-1.9320996286683723,-1.9314647877308484,-1.9464276256666304,-1.9482192536879337,-1.9648762820351429), a2=doubleArrayOf(0.959484453052192,0.9617861010981933,0.9687258551697253,0.9733497927864203,0.9881320776548521,0.9904730011558084), b0=doubleArrayOf(7.444638326450626e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.4889276652901253e-10,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.444638326450626e-11,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(1587.4010519681995,1781.7974362806785,1414.2135623730949,1600.0,0,Sos(a1=doubleArrayOf(-1.8965027818651838,-1.9052541730305297,-1.9019737635216158,-1.9239887853351916,-1.9219458962629674,-1.9474731148985207), a2=doubleArrayOf(0.9492267296230263,0.95207917414863,0.9607765846022719,0.9665216737012422,0.9850820532134703,0.9880039518087411), b0=doubleArrayOf(2.917804238031064e-10,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.835608476062128e-10,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.917804238031064e-10,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(1587.4010519681995,1781.7974362806785,1414.2135623730949,1600.0,sos=Sos(a1=doubleArrayOf(-1.8965027818651838,-1.9052541730305297,-1.9019737635216158,-1.9239887853351916,-1.9219458962629674,-1.9474731148985207), a2=doubleArrayOf(0.9492267296230263,0.95207917414863,0.9607765846022719,0.9665216737012422,0.9850820532134703,0.9880039518087411), b0=doubleArrayOf(2.917804238031064e-10,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.835608476062128e-10,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.917804238031064e-10,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(2000.0,2244.9240966187463,1781.7974362806785,2000.0,0,Sos(a1=doubleArrayOf(-1.853524646230276,-1.8662693215227235,-1.8582666776011347,-1.8908847111532185,-1.881546589134322,-1.9207843344407987), a2=doubleArrayOf(0.9364604437606376,0.9399729439539108,0.9508720272624499,0.9579687210223301,0.9812642472067368,0.9848941893771724), b0=doubleArrayOf(1.1377293181749668e-09,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.2754586363499337e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.1377293181749668e-09,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(2000.0,2244.9240966187463,1781.7974362806785,2000.0,sos=Sos(a1=doubleArrayOf(-1.853524646230276,-1.8662693215227235,-1.8582666776011347,-1.8908847111532185,-1.881546589134322,-1.9207843344407987), a2=doubleArrayOf(0.9364604437606376,0.9399729439539108,0.9508720272624499,0.9579687210223301,0.9812642472067368,0.9848941893771724), b0=doubleArrayOf(1.1377293181749668e-09,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.2754586363499337e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.1377293181749668e-09,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(2519.8420997897465,2828.42712474619,2244.9240966187463,2500.0,0,Sos(a1=doubleArrayOf(-1.7905716270112106,-1.809232149768823,-1.7931653070496998,-1.8417048438803578,-1.8193689745652162,-1.8797292043052978), a2=doubleArrayOf(0.9206234986422084,0.9249080336194182,0.9385743411553938,0.9472640245029827,0.9764983624230299,0.9809745604874052), b0=doubleArrayOf(4.408111664350121e-09,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(8.816223328700242e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.408111664350121e-09,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(2519.8420997897465,2828.42712474619,2244.9240966187463,2500.0,sos=Sos(a1=doubleArrayOf(-1.7905716270112106,-1.809232149768823,-1.7931653070496998,-1.8417048438803578,-1.8193689745652162,-1.8797292043052978), a2=doubleArrayOf(0.9206234986422084,0.9249080336194182,0.9385743411553938,0.9472640245029827,0.9764983624230299,0.9809745604874052), b0=doubleArrayOf(4.408111664350121e-09,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(8.816223328700242e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.408111664350121e-09,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(3174.8021039363994,3563.594872561358,2828.42712474619,3150.0,0,Sos(a1=doubleArrayOf(-1.698018049211772,-1.7253955989854663,-1.6960230821791877,-1.768329526959257,-1.7238475768878447,-1.8165175010628238), a2=doubleArrayOf(0.9010581254789203,0.9062094836371017,0.9233783287144209,0.9338745051748574,0.9705731974939211,0.9760270510168769), b0=doubleArrayOf(1.6944944571208185e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.388988914241637e-08,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.6944944571208185e-08,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(3174.8021039363994,3563.594872561358,2828.42712474619,3150.0,sos=Sos(a1=doubleArrayOf(-1.698018049211772,-1.7253955989854663,-1.6960230821791877,-1.768329526959257,-1.7238475768878447,-1.8165175010628238), a2=doubleArrayOf(0.9010581254789203,0.9062094836371017,0.9233783287144209,0.9338745051748574,0.9705731974939211,0.9760270510168769), b0=doubleArrayOf(1.6944944571208185e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.388988914241637e-08,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.6944944571208185e-08,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(4000.0,4489.8481932374925,3563.594872561357,4000.0,0,Sos(a1=doubleArrayOf(-1.5620018562313807,-1.6020680617209626,-1.5514295488390417,-1.6587733605130148,-1.5779211014645125,-1.7193763872935146), a2=doubleArrayOf(0.8770137966311882,0.8830668098889431,0.9047265072461494,0.9171282371849951,0.9632528802297299,0.9697655086311445), b0=doubleArrayOf(6.450735040335462e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.2901470080670925e-07,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(6.450735040335462e-08,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(4000.0,4489.8481932374925,3563.594872561357,4000.0,sos=Sos(a1=doubleArrayOf(-1.5620018562313807,-1.6020680617209626,-1.5514295488390417,-1.6587733605130148,-1.5779211014645125,-1.7193763872935146), a2=doubleArrayOf(0.8770137966311882,0.8830668098889431,0.9047265072461494,0.9171282371849951,0.9632528802297299,0.9697655086311445), b0=doubleArrayOf(6.450735040335462e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.2901470080670925e-07,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(6.450735040335462e-08,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(5039.684199579493,5656.85424949238,4489.8481932374925,5000.0,0,Sos(a1=doubleArrayOf(-1.3633369519581289,-1.4214643083634073,-1.3380146172088572,-1.495861751008069,-1.3574610576475574,-1.570954880985224), a2=doubleArrayOf(0.8476665728706247,0.8545076730564747,0.8820537169725038,0.896161432848994,0.954298805785257,0.9618028595678236), b0=doubleArrayOf(2.426697477611551e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(4.853394955223102e-07,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.426697477611551e-07,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(5039.684199579493,5656.85424949238,4489.8481932374925,5000.0,sos=Sos(a1=doubleArrayOf(-1.3633369519581289,-1.4214643083634073,-1.3380146172088572,-1.495861751008069,-1.3574610576475574,-1.570954880985224), a2=doubleArrayOf(0.8476665728706247,0.8545076730564747,0.8820537169725038,0.896161432848994,0.954298805785257,0.9618028595678236), b0=doubleArrayOf(2.426697477611551e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(4.853394955223102e-07,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.426697477611551e-07,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(6349.604207872798,7127.189745122714,5656.8542494923795,6300.0,0,Sos(a1=doubleArrayOf(-1.0773395486328345,-1.1602031096288796,-1.0283985417327675,-1.256274714648876,-1.0309072558826313,-1.3467894695867646), a2=doubleArrayOf(0.8121705498843197,0.8193547936396985,0.8548947631581487,0.869820439177322,0.9435278418933211,0.9515891151377003), b0=doubleArrayOf(8.998148499223296e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.7996296998446593e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(8.998148499223296e-07,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(6349.604207872798,7127.189745122714,5656.8542494923795,6300.0,sos=Sos(a1=doubleArrayOf(-1.0773395486328345,-1.1602031096288796,-1.0283985417327675,-1.256274714648876,-1.0309072558826313,-1.3467894695867646), a2=doubleArrayOf(0.8121705498843197,0.8193547936396985,0.8548947631581487,0.869820439177322,0.9435278418933211,0.9515891151377003), b0=doubleArrayOf(8.998148499223296e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.7996296998446593e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(8.998148499223296e-07,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(8000.0,8979.696386474985,7127.189745122714,8000.0,0,Sos(a1=doubleArrayOf(-0.6767266245218572,-0.7912873195095531,-0.5929092846277738,-0.9115385133184579,-0.563294468815372,-1.0151634969766976), a2=doubleArrayOf(0.7697779369631321,0.7761347143350338,0.8231381617779963,0.8364515353139432,0.9309588850819872,0.9382780377204392), b0=doubleArrayOf(3.279097356293735e-06,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(6.55819471258747e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.279097356293735e-06,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(8000.0,8979.696386474985,7127.189745122714,8000.0,sos=Sos(a1=doubleArrayOf(-0.6767266245218572,-0.7912873195095531,-0.5929092846277738,-0.9115385133184579,-0.563294468815372,-1.0151634969766976), a2=doubleArrayOf(0.7697779369631321,0.7761347143350338,0.8231381617779963,0.8364515353139432,0.9309588850819872,0.9382780377204392), b0=doubleArrayOf(3.279097356293735e-06,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(6.55819471258747e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.279097356293735e-06,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(10079.368399158986,11313.70849898476,8979.696386474985,10000.0,0,Sos(a1=doubleArrayOf(-0.14222192561841182,-0.29277856105906586,-0.012595844464301289,-0.4348904602476619,0.06824248192606285,-0.5419579256858204), a2=doubleArrayOf(0.7201408449235422,0.7228312366826449,0.7876833682787011,0.7933613741944567,0.9172048817957851,0.9203880227993824), b0=doubleArrayOf(1.170610624091675e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.34122124818335e-05,2.0,-2.0,-2.0,2.0,-2.0), b2=doubleArrayOf(1.170610624091675e-05,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(10079.368399158986,11313.70849898476,8979.696386474985,10000.0,sos=Sos(a1=doubleArrayOf(-0.14222192561841182,-0.29277856105906586,-0.012595844464301289,-0.4348904602476619,0.06824248192606285,-0.5419579256858204), a2=doubleArrayOf(0.7201408449235422,0.7228312366826449,0.7876833682787011,0.7933613741944567,0.9172048817957851,0.9203880227993824), b0=doubleArrayOf(1.170610624091675e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.34122124818335e-05,2.0,-2.0,-2.0,2.0,-2.0), b2=doubleArrayOf(1.170610624091675e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,0,Sos(a1=doubleArrayOf(0.32848784493062516,0.5111660791529813,0.17733074339349109,0.6895165716585276,0.09114717463945445,0.8350589885757965), a2=doubleArrayOf(0.656000255318543,0.6643131072250139,0.7350225079427642,0.7526237525351431,0.8946980509545702,0.9047450752107424), b0=doubleArrayOf(4.079796937465318e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-8.159593874930636e-05,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(4.079796937465318e-05,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,sos=Sos(a1=doubleArrayOf(0.32848784493062516,0.5111660791529813,0.17733074339349109,0.6895165716585276,0.09114717463945445,0.8350589885757965), a2=doubleArrayOf(0.656000255318543,0.6643131072250139,0.7350225079427642,0.7526237525351431,0.8946980509545702,0.9047450752107424), b0=doubleArrayOf(4.079796937465318e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-8.159593874930636e-05,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(4.079796937465318e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(16000.0,17959.39277294997,14254.379490245428,16000.0,0,Sos(a1=doubleArrayOf(0.982782646682872,1.1871163009075574,0.8498066536564547,1.3967580458961404,0.837324208994016,1.5838701207898134), a2=doubleArrayOf(0.5651700023975551,0.6111614520796955,0.6434040840366426,0.7378609025916056,0.8507018518753098,0.9043557997025693), b0=doubleArrayOf(0.00013834805505964022,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00027669611011928044,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(0.00013834805505964022,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(16000.0,17959.39277294997,14254.379490245428,16000.0,sos=Sos(a1=doubleArrayOf(0.982782646682872,1.1871163009075574,0.8498066536564547,1.3967580458961404,0.837324208994016,1.5838701207898134), a2=doubleArrayOf(0.5651700023975551,0.6111614520796955,0.6434040840366426,0.7378609025916056,0.8507018518753098,0.9043557997025693), b0=doubleArrayOf(0.00013834805505964022,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00027669611011928044,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(0.00013834805505964022,1.0,1.0,1.0,1.0,1.0))))), - antiAliasing=AntiAliasing(a1=doubleArrayOf(-1.2446640940133593e-16,-1.630640067418199e-16,-1.387778780781446e-16,-2.4980018054066027e-16,-1.9428902930940244e-16,-1.6653345369377353e-16,-2.775557561562892e-16,-1.1102230246251565e-16,-2.220446049250313e-16,-2.2204460492503136e-16), a2=doubleArrayOf(0.001543712508674131,0.014008568735804144,0.03956612989658006,0.07954045177320418,0.13610158059098823,0.21252682194185937,0.31363013821503233,0.4464626921716894,0.6214743340943358,0.8544977810681018), b0=doubleArrayOf(8.555155699386506e-06,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.7110311398773013e-05,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.555155699386506e-06,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0),sampleRatio=2), - aWeighting=DigitalFilterConfiguration(filterDenominator=doubleArrayOf(1.0,-4.01958210258393,6.18944019937876,-4.453264993081893,1.420898591796445,-0.14184390392723248,0.004352219710019002), filterNumerator=doubleArrayOf(0.25574354103517516,-0.5114870820703503,-0.2557435410351751,1.0229741641407009,-0.25574354103517505,-0.5114870820703503,0.25574354103517516)), - cWeighting=DigitalFilterConfiguration(filterDenominator=doubleArrayOf(1.0,-2.134692386318322,1.279369764820266,-0.14957989013271009,0.004909935845510762), filterNumerator=doubleArrayOf(0.21700689624574065,0.0,-0.4340137924914813,0.0,0.21700689624574065)), - configuration=GeneralConfiguration(sampleRate=44100)) -} - - -fun get48000HZ() : SpectrumChannelConfiguration { - return SpectrumChannelConfiguration( - bandpass=listOf( - Bandpass(49.60628287400625,55.681169883771226,44.19417382415923,50.0,4,Sos(a1=doubleArrayOf(-1.9984601797146522,-1.998552690675498,-1.998800724570471,-1.9989885175100113,-1.999514946443491,-1.9996204400445106), a2=doubleArrayOf(0.9985049317808966,0.9985923599926457,0.9988503801969798,0.9990242833273026,0.9995676495557392,0.9996541604577023), b0=doubleArrayOf(1.8006511759597513e-19,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.6013023519195027e-19,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.8006511759597513e-19,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(793.7005259840998,890.8987181403395,707.1067811865476,800.0,sos=Sos(a1=doubleArrayOf(-1.965026429970155,-1.967668597254471,-1.9691783807266252,-1.9754146534405037,-1.979674406907778,-1.9858749551591182), a2=doubleArrayOf(0.9763456895906536,0.9777097806866464,0.9817686375136939,0.9844974374007367,0.9931078103210836,0.9944788918207288), b0=doubleArrayOf(2.893016243936537e-12,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.786032487873074e-12,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.893016243936537e-12,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(62.5,70.15387801933582,55.681169883771204,63.0,4,Sos(a1=doubleArrayOf(-1.9980456727590987,-1.9981638490205884,-1.998472975728957,-1.9987140616601067,-1.9993716476778767,-1.9995107633757623), a2=doubleArrayOf(0.9981166982879324,0.998226808502417,0.9985517871612583,0.9987708290608652,0.9994553037380881,0.9995642887013393), b0=doubleArrayOf(7.1971733989403265e-19,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.4394346797880653e-18,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.1971733989403265e-19,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(1000.0,1122.4620483093731,890.8987181403393,1000.0,sos=Sos(a1=doubleArrayOf(-1.9523863069067073,-1.956108973076721,-1.9571616552873032,-1.9661212030625006,-1.9700353114553955,-1.979405001055496), a2=doubleArrayOf(0.9702899031224652,0.9719947859444387,0.977088153176812,0.9805039267142269,0.9913264267319474,0.9930473480576114), b0=doubleArrayOf(1.1435038769653862e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.2870077539307723e-11,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.1435038769653862e-11,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(78.74506561842958,88.38834764831844,70.15387801933582,80.0,4,Sos(a1=doubleArrayOf(-1.9975150516721296,-1.997666513582322,-1.9980506301304577,-1.9983614893563468,-1.999180989630783,-1.9993661073959705), a2=doubleArrayOf(0.9976277697102935,0.9977664322112939,0.9981757114692286,0.998451587351376,0.9993137754070069,0.9994510685212221), b0=doubleArrayOf(2.876135110287808e-18,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.752270220575616e-18,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.876135110287808e-18,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(1259.9210498948732,1414.213562373095,1122.4620483093731,1250.0,sos=Sos(a1=doubleArrayOf(-1.9344269032418544,-1.9397319953310073,-1.9397192012263036,-1.9527343232383667,-1.955364536130128,-1.969623329484771), a2=doubleArrayOf(0.962713847798208,0.9648387911194759,0.9712264142580659,0.9754919104957473,0.9890888216359307,0.9912451584685964), b0=doubleArrayOf(4.506127850247666e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.012255700495332e-11,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.506127850247666e-11,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(99.21256574801247,111.36233976754241,88.38834764831843,100.0,4,Sos(a1=doubleArrayOf(-1.9968332267646434,-1.9970281309710414,-1.9975035831956809,-1.997906519235211,-1.9989247264019108,-1.9991735803604036), a2=doubleArrayOf(0.9970120994122506,0.9971866951423459,0.9977020891971378,0.9980495114976516,0.9991354905208201,0.9993084375601545), b0=doubleArrayOf(1.1490779436069693e-17,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.2981558872139386e-17,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.1490779436069693e-17,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(1587.4010519681995,1781.7974362806785,1414.2135623730949,1600.0,sos=Sos(a1=doubleArrayOf(-1.9086271549871285,-1.916265810798378,-1.9141456550592422,-1.9332356686147505,-1.9329115625897635,-1.954729191654851), a2=doubleArrayOf(0.9532534031282983,0.9558916290464894,0.9638981867815446,0.9692065253993708,0.9862812992117321,0.9889762139111492), b0=doubleArrayOf(1.7689636295331422e-10,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.5379272590662843e-10,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.7689636295331422e-10,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(125.0,140.30775603867164,111.36233976754241,125.0,4,Sos(a1=doubleArrayOf(-1.9959531145933485,-1.996205137556247,-1.99679067441179,-1.997316227634658,-1.9985763869742814,-1.9989147095139637), a2=doubleArrayOf(0.9962369446432664,0.9964567486353729,0.9971056859894822,0.9975431546301207,0.9989109131331084,0.9991287613275411), b0=doubleArrayOf(4.5893885449978465e-17,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.178777089995693e-17,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.5893885449978465e-17,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(2000.0,2244.9240966187463,1781.7974362806785,2000.0,sos=Sos(a1=doubleArrayOf(-1.871205384376715,-1.8823000944590687,-1.8763388947791837,-1.904558436948833,-1.8984178521059683,-1.9319247221181628), a2=doubleArrayOf(0.941467504228004,0.944724780930314,0.954757924553905,0.9613309174454501,0.9827643692222471,0.9861189380456511), b0=doubleArrayOf(6.91156650726858e-10,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.382313301453716e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(6.91156650726858e-10,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(157.49013123685916,176.7766952966369,140.30775603867164,160.0,4,Sos(a1=doubleArrayOf(-1.9948108434480472,-1.995138608941705,-1.9958549254625118,-1.99654543832831,-1.9980970962795688,-1.998562682476333), a2=doubleArrayOf(0.9952611693790621,0.9955378281154749,0.9963547796390351,0.9969055435967796,0.9986280396391475,0.9989024265317586), b0=doubleArrayOf(1.832273856726884e-16,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.664547713453768e-16,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.832273856726884e-16,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(2519.8420997897465,2828.42712474619,2244.9240966187463,2500.0,sos=Sos(a1=doubleArrayOf(-1.8165097119283033,-1.8327278364989417,-1.8201108586209922,-1.8620520708389903,-1.8453324823283772,-1.8968763455852142), a2=doubleArrayOf(0.9268280488124225,0.9308168891662456,0.9433931969114127,0.9514711240169829,0.9783691390918853,0.9825187465670534), b0=doubleArrayOf(2.684574372673378e-09,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.369148745346756e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.684574372673378e-09,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(198.42513149602493,222.72467953508482,176.77669529663686,200.0,4,Sos(a1=doubleArrayOf(-1.9933187384467437,-1.9937479129040492,-1.9946164416668248,-1.9955313782127055,-1.9974291162822255,-1.998078105503138), a2=doubleArrayOf(0.9940331313329048,0.9943812547620593,0.9954095160792754,0.996102770070116,0.9982717642479774,0.9986173297121667), b0=doubleArrayOf(7.31159711822096e-16,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.462319423644192e-15,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.31159711822096e-16,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(3174.8021039363994,3563.594872561358,2828.42712474619,3150.0,sos=Sos(a1=doubleArrayOf(-1.7361720647072763,-1.7599595298520214,-1.7362282609156778,-1.7987101471164402,-1.7636941439008764,-1.8429155834449047), a2=doubleArrayOf(0.9087125903565318,0.9135375076733302,0.9293227322337309,0.9391361195874267,0.9728955015176468,0.97797732690065), b0=doubleArrayOf(1.0351518141960393e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.0703036283920787e-08,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.0351518141960393e-08,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(250.0,280.6155120773433,222.72467953508482,250.0,4,Sos(a1=doubleArrayOf(-1.9913549482178523,-1.9919213473960666,-1.992961734561673,-1.9941856866943861,-1.9964858191009422,-1.9974024456375816), a2=doubleArrayOf(0.9924880597760736,0.9929259516314002,0.9942198634821799,0.9950922308001783,0.997823084005514,0.998258234393528), b0=doubleArrayOf(2.915850405403221e-15,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.831700810806442e-15,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.915850405403221e-15,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(4000.0,4489.8481932374925,3563.594872561357,4000.0,sos=Sos(a1=doubleArrayOf(-1.6180238146105717,-1.6528894834371832,-1.6111845676577818,-1.7041154148090003,-1.638653915827305,-1.7599015767615853), a2=doubleArrayOf(0.8864034769752915,0.8921284562827796,0.9120050759349341,0.9237096620549838,0.9661151955423802,0.9722364024371097), b0=doubleArrayOf(3.955684226935954e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(7.911368453871908e-08,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.955684226935954e-08,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(314.9802624737183,353.5533905932738,280.6155120773433,315.0,4,Sos(a1=doubleArrayOf(-1.9887479618777701,-1.9895021967303281,-1.9907275393254193,-1.992382364609799,-1.99513606793578,-1.996447847380435), a2=doubleArrayOf(0.9905448150503697,0.9910953670304777,0.9927230754790283,0.9938204340344118,0.997258104736648,0.9978059631311219), b0=doubleArrayOf(1.1619301238020158e-14,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.3238602476040316e-14,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.1619301238020158e-14,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(5039.684199579493,5656.85424949238,4489.8481932374925,5000.0,sos=Sos(a1=doubleArrayOf(-1.444929044061489,-1.4957170043735097,-1.4258973038465004,-1.563146402998311,-1.4488230224705272,-1.6327258020473276), a2=doubleArrayOf(0.8590998950465403,0.8656805160974435,0.8908697742725937,0.9044065217137992,0.95778643697265,0.9649510549793255), b0=doubleArrayOf(1.4950003260908323e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.9900006521816645e-07,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.4950003260908323e-07,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(396.8502629920499,445.44935907016975,353.5533905932738,400.0,4,Sos(a1=doubleArrayOf(-1.985253318813393,-1.9862677916946907,-1.9876761395004403,-1.9899394676176885,-1.993179826793365,-1.9950812653258392), a2=doubleArrayOf(0.9881019056669826,0.9887936803667451,0.9908405610202708,0.9922202715949308,0.9965468014759469,0.9972363820904895), b0=doubleArrayOf(4.625615616925849e-14,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.251231233851698e-14,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.625615616925849e-14,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(6349.604207872798,7127.189745122714,5656.8542494923795,6300.0,sos=Sos(a1=doubleArrayOf(-1.1941149006127025,-1.2670743205284096,-1.1550415228967335,-1.3547778680343292,-1.1652346750852467,-1.4396698084572528), a2=doubleArrayOf(0.8259548961648948,0.8330985104737876,0.8653960229572148,0.8801955148623766,0.9476951530180628,0.9556413101528509), b0=doubleArrayOf(5.574640991696916e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.1149281983393831e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(5.574640991696916e-07,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(500.0,561.2310241546866,445.44935907016963,500.0,4,Sos(a1=doubleArrayOf(-1.9805183605881083,-1.9818978551492608,-1.9834575729707877,-1.9865911374749832,-1.9903100156946432,-1.9930996785658013), a2=doubleArrayOf(0.9850326275872681,0.985901148759386,0.9884740475498774,0.9902076356199037,0.9956514788832681,0.9965191245238226), b0=doubleArrayOf(1.8391819354137517e-13,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.6783638708275035e-13,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.8391819354137517e-13,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(8000.0,8979.696386474985,7127.189745122714,8000.0,sos=Sos(a1=doubleArrayOf(-0.8385656218768355,-0.9407668149562102,-0.7689289266746798,-1.0520645541182183,-0.7531924297464213,-1.1514952746822522), a2=doubleArrayOf(0.786163458984544,0.7930340162566856,0.8352970884716027,0.8496435477623626,0.935759288200993,0.9435931378701163), b0=doubleArrayOf(2.0452147968403255e-06,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(4.090429593680651e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.0452147968403255e-06,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(629.9605249474366,707.1067811865476,561.2310241546866,630.0,4,Sos(a1=doubleArrayOf(-1.9740286599079484,-1.9759264207270635,-1.9775515830152486,-1.981944694593259,-1.9860528263876591,-1.9901913146712302), a2=doubleArrayOf(0.981179187823968,0.9822684388078915,0.9855009207967405,0.9876772349651166,0.9945248701796774,0.9956159845310506), b0=doubleArrayOf(7.301444354678124e-13,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.4602888709356248e-12,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.301444354678124e-13,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(10079.368399158986,11313.70849898476,8979.696386474985,10000.0,sos=Sos(a1=doubleArrayOf(-0.35405154763185304,-0.49128251707257314,-0.24223050428288867,-0.6261645675731202,-0.18240420331818913,-0.7338155809134382), a2=doubleArrayOf(0.7391734396217206,0.7437188888138072,0.8009751006663193,0.8105429251807174,0.9222982330633457,0.927623171295239), b0=doubleArrayOf(7.359484224824578e-06,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.4718968449649156e-05,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.359484224824578e-06,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(793.7005259840998,890.8987181403395,707.1067811865476,800.0,4,Sos(a1=doubleArrayOf(-1.965026429970155,-1.967668597254471,-1.9691783807266252,-1.9754146534405037,-1.979674406907778,-1.9858749551591182), a2=doubleArrayOf(0.9763456895906536,0.9777097806866464,0.9817686375136939,0.9844974374007367,0.9931078103210836,0.9944788918207288), b0=doubleArrayOf(2.893016243936537e-12,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.786032487873074e-12,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.893016243936537e-12,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,sos=Sos(a1=doubleArrayOf(0.08931454551996315,0.26142519691480276,-0.06094975521093459,0.4225931350065996,-0.15897989987756816,0.5440792188366831), a2=doubleArrayOf(0.6824341186617071,0.6852999529629943,0.7588006314601975,0.7648681667234911,0.9053884151557717,0.9088313442453133), b0=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-5.177306444963838e-05,2.0,-2.0,2.0,-2.0,2.0), b2=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(1000.0,1122.4620483093731,890.8987181403393,1000.0,4,Sos(a1=doubleArrayOf(-1.9523863069067073,-1.956108973076721,-1.9571616552873032,-1.9661212030625006,-1.9700353114553955,-1.979405001055496), a2=doubleArrayOf(0.9702899031224652,0.9719947859444387,0.977088153176812,0.9805039267142269,0.9913264267319474,0.9930473480576114), b0=doubleArrayOf(1.1435038769653862e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.2870077539307723e-11,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.1435038769653862e-11,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(16000.0,17959.39277294997,14254.379490245428,16000.0,sos=Sos(a1=doubleArrayOf(0.7511127884288892,0.9474349259843516,0.6077435414237824,1.1492032880591414,0.5590818870997089,1.3287692023317226), a2=doubleArrayOf(0.6029568566338351,0.6285948136865629,0.6836472041890249,0.7374497853902116,0.8705313801288671,0.9013601600372116), b0=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00017740280306388184,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(1259.9210498948732,1414.213562373095,1122.4620483093731,1250.0,4,Sos(a1=doubleArrayOf(-1.9344269032418544,-1.9397319953310073,-1.9397192012263036,-1.9527343232383667,-1.955364536130128,-1.969623329484771), a2=doubleArrayOf(0.962713847798208,0.9648387911194759,0.9712264142580659,0.9754919104957473,0.9890888216359307,0.9912451584685964), b0=doubleArrayOf(4.506127850247666e-11,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(9.012255700495332e-11,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(4.506127850247666e-11,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(20158.73679831797,22627.416997969518,17959.392772949966,20000.0,sos=Sos(a1=doubleArrayOf(1.2413737222383092,1.1545812920689111,1.5913330473935272,1.2692295742308106,1.7813978059062259,1.9088059626392415), a2=doubleArrayOf(0.4389237157990629,0.5162530772033582,0.6535489122217201,0.7880153266268755,0.8189707787110215,0.9408658628045891), b0=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.0005903384891348293,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(1587.4010519681995,1781.7974362806785,1414.2135623730949,1600.0,3,Sos(a1=doubleArrayOf(-1.9086271549871285,-1.916265810798378,-1.9141456550592422,-1.9332356686147505,-1.9329115625897635,-1.954729191654851), a2=doubleArrayOf(0.9532534031282983,0.9558916290464894,0.9638981867815446,0.9692065253993708,0.9862812992117321,0.9889762139111492), b0=doubleArrayOf(1.7689636295331422e-10,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(3.5379272590662843e-10,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.7689636295331422e-10,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,sos=Sos(a1=doubleArrayOf(0.08931454551996315,0.26142519691480276,-0.06094975521093459,0.4225931350065996,-0.15897989987756816,0.5440792188366831), a2=doubleArrayOf(0.6824341186617071,0.6852999529629943,0.7588006314601975,0.7648681667234911,0.9053884151557717,0.9088313442453133), b0=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-5.177306444963838e-05,2.0,-2.0,2.0,-2.0,2.0), b2=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(2000.0,2244.9240966187463,1781.7974362806785,2000.0,3,Sos(a1=doubleArrayOf(-1.871205384376715,-1.8823000944590687,-1.8763388947791837,-1.904558436948833,-1.8984178521059683,-1.9319247221181628), a2=doubleArrayOf(0.941467504228004,0.944724780930314,0.954757924553905,0.9613309174454501,0.9827643692222471,0.9861189380456511), b0=doubleArrayOf(6.91156650726858e-10,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.382313301453716e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(6.91156650726858e-10,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(16000.0,17959.39277294997,14254.379490245428,16000.0,sos=Sos(a1=doubleArrayOf(0.7511127884288892,0.9474349259843516,0.6077435414237824,1.1492032880591414,0.5590818870997089,1.3287692023317226), a2=doubleArrayOf(0.6029568566338351,0.6285948136865629,0.6836472041890249,0.7374497853902116,0.8705313801288671,0.9013601600372116), b0=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00017740280306388184,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(2519.8420997897465,2828.42712474619,2244.9240966187463,2500.0,3,Sos(a1=doubleArrayOf(-1.8165097119283033,-1.8327278364989417,-1.8201108586209922,-1.8620520708389903,-1.8453324823283772,-1.8968763455852142), a2=doubleArrayOf(0.9268280488124225,0.9308168891662456,0.9433931969114127,0.9514711240169829,0.9783691390918853,0.9825187465670534), b0=doubleArrayOf(2.684574372673378e-09,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(5.369148745346756e-09,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.684574372673378e-09,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(20158.73679831797,22627.416997969518,17959.392772949966,20000.0,sos=Sos(a1=doubleArrayOf(1.2413737222383092,1.1545812920689111,1.5913330473935272,1.2692295742308106,1.7813978059062259,1.9088059626392415), a2=doubleArrayOf(0.4389237157990629,0.5162530772033582,0.6535489122217201,0.7880153266268755,0.8189707787110215,0.9408658628045891), b0=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.0005903384891348293,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(3174.8021039363994,3563.594872561358,2828.42712474619,3150.0,2,Sos(a1=doubleArrayOf(-1.7361720647072763,-1.7599595298520214,-1.7362282609156778,-1.7987101471164402,-1.7636941439008764,-1.8429155834449047), a2=doubleArrayOf(0.9087125903565318,0.9135375076733302,0.9293227322337309,0.9391361195874267,0.9728955015176468,0.97797732690065), b0=doubleArrayOf(1.0351518141960393e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.0703036283920787e-08,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.0351518141960393e-08,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,sos=Sos(a1=doubleArrayOf(0.08931454551996315,0.26142519691480276,-0.06094975521093459,0.4225931350065996,-0.15897989987756816,0.5440792188366831), a2=doubleArrayOf(0.6824341186617071,0.6852999529629943,0.7588006314601975,0.7648681667234911,0.9053884151557717,0.9088313442453133), b0=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-5.177306444963838e-05,2.0,-2.0,2.0,-2.0,2.0), b2=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(4000.0,4489.8481932374925,3563.594872561357,4000.0,2,Sos(a1=doubleArrayOf(-1.6180238146105717,-1.6528894834371832,-1.6111845676577818,-1.7041154148090003,-1.638653915827305,-1.7599015767615853), a2=doubleArrayOf(0.8864034769752915,0.8921284562827796,0.9120050759349341,0.9237096620549838,0.9661151955423802,0.9722364024371097), b0=doubleArrayOf(3.955684226935954e-08,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(7.911368453871908e-08,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(3.955684226935954e-08,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(16000.0,17959.39277294997,14254.379490245428,16000.0,sos=Sos(a1=doubleArrayOf(0.7511127884288892,0.9474349259843516,0.6077435414237824,1.1492032880591414,0.5590818870997089,1.3287692023317226), a2=doubleArrayOf(0.6029568566338351,0.6285948136865629,0.6836472041890249,0.7374497853902116,0.8705313801288671,0.9013601600372116), b0=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00017740280306388184,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(5039.684199579493,5656.85424949238,4489.8481932374925,5000.0,2,Sos(a1=doubleArrayOf(-1.444929044061489,-1.4957170043735097,-1.4258973038465004,-1.563146402998311,-1.4488230224705272,-1.6327258020473276), a2=doubleArrayOf(0.8590998950465403,0.8656805160974435,0.8908697742725937,0.9044065217137992,0.95778643697265,0.9649510549793255), b0=doubleArrayOf(1.4950003260908323e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(2.9900006521816645e-07,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(1.4950003260908323e-07,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(20158.73679831797,22627.416997969518,17959.392772949966,20000.0,sos=Sos(a1=doubleArrayOf(1.2413737222383092,1.1545812920689111,1.5913330473935272,1.2692295742308106,1.7813978059062259,1.9088059626392415), a2=doubleArrayOf(0.4389237157990629,0.5162530772033582,0.6535489122217201,0.7880153266268755,0.8189707787110215,0.9408658628045891), b0=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.0005903384891348293,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(6349.604207872798,7127.189745122714,5656.8542494923795,6300.0,1,Sos(a1=doubleArrayOf(-1.1941149006127025,-1.2670743205284096,-1.1550415228967335,-1.3547778680343292,-1.1652346750852467,-1.4396698084572528), a2=doubleArrayOf(0.8259548961648948,0.8330985104737876,0.8653960229572148,0.8801955148623766,0.9476951530180628,0.9556413101528509), b0=doubleArrayOf(5.574640991696916e-07,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.1149281983393831e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(5.574640991696916e-07,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,sos=Sos(a1=doubleArrayOf(0.08931454551996315,0.26142519691480276,-0.06094975521093459,0.4225931350065996,-0.15897989987756816,0.5440792188366831), a2=doubleArrayOf(0.6824341186617071,0.6852999529629943,0.7588006314601975,0.7648681667234911,0.9053884151557717,0.9088313442453133), b0=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-5.177306444963838e-05,2.0,-2.0,2.0,-2.0,2.0), b2=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(8000.0,8979.696386474985,7127.189745122714,8000.0,1,Sos(a1=doubleArrayOf(-0.8385656218768355,-0.9407668149562102,-0.7689289266746798,-1.0520645541182183,-0.7531924297464213,-1.1514952746822522), a2=doubleArrayOf(0.786163458984544,0.7930340162566856,0.8352970884716027,0.8496435477623626,0.935759288200993,0.9435931378701163), b0=doubleArrayOf(2.0452147968403255e-06,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(4.090429593680651e-06,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(2.0452147968403255e-06,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(16000.0,17959.39277294997,14254.379490245428,16000.0,sos=Sos(a1=doubleArrayOf(0.7511127884288892,0.9474349259843516,0.6077435414237824,1.1492032880591414,0.5590818870997089,1.3287692023317226), a2=doubleArrayOf(0.6029568566338351,0.6285948136865629,0.6836472041890249,0.7374497853902116,0.8705313801288671,0.9013601600372116), b0=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00017740280306388184,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(10079.368399158986,11313.70849898476,8979.696386474985,10000.0,1,Sos(a1=doubleArrayOf(-0.35405154763185304,-0.49128251707257314,-0.24223050428288867,-0.6261645675731202,-0.18240420331818913,-0.7338155809134382), a2=doubleArrayOf(0.7391734396217206,0.7437188888138072,0.8009751006663193,0.8105429251807174,0.9222982330633457,0.927623171295239), b0=doubleArrayOf(7.359484224824578e-06,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.4718968449649156e-05,2.0,2.0,-2.0,-2.0,-2.0), b2=doubleArrayOf(7.359484224824578e-06,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(20158.73679831797,22627.416997969518,17959.392772949966,20000.0,sos=Sos(a1=doubleArrayOf(1.2413737222383092,1.1545812920689111,1.5913330473935272,1.2692295742308106,1.7813978059062259,1.9088059626392415), a2=doubleArrayOf(0.4389237157990629,0.5162530772033582,0.6535489122217201,0.7880153266268755,0.8189707787110215,0.9408658628045891), b0=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.0005903384891348293,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(0.0002951692445674146,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,0,Sos(a1=doubleArrayOf(0.08931454551996315,0.26142519691480276,-0.06094975521093459,0.4225931350065996,-0.15897989987756816,0.5440792188366831), a2=doubleArrayOf(0.6824341186617071,0.6852999529629943,0.7588006314601975,0.7648681667234911,0.9053884151557717,0.9088313442453133), b0=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-5.177306444963838e-05,2.0,-2.0,2.0,-2.0,2.0), b2=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(12699.208415745596,14254.379490245428,11313.708498984759,12500.0,sos=Sos(a1=doubleArrayOf(0.08931454551996315,0.26142519691480276,-0.06094975521093459,0.4225931350065996,-0.15897989987756816,0.5440792188366831), a2=doubleArrayOf(0.6824341186617071,0.6852999529629943,0.7588006314601975,0.7648681667234911,0.9053884151557717,0.9088313442453133), b0=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-5.177306444963838e-05,2.0,-2.0,2.0,-2.0,2.0), b2=doubleArrayOf(2.588653222481919e-05,1.0,1.0,1.0,1.0,1.0)))), - Bandpass(16000.0,17959.39277294997,14254.379490245428,16000.0,0,Sos(a1=doubleArrayOf(0.7511127884288892,0.9474349259843516,0.6077435414237824,1.1492032880591414,0.5590818870997089,1.3287692023317226), a2=doubleArrayOf(0.6029568566338351,0.6285948136865629,0.6836472041890249,0.7374497853902116,0.8705313801288671,0.9013601600372116), b0=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00017740280306388184,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0)),subsamplingFilter=SubsamplingFilter(16000.0,17959.39277294997,14254.379490245428,16000.0,sos=Sos(a1=doubleArrayOf(0.7511127884288892,0.9474349259843516,0.6077435414237824,1.1492032880591414,0.5590818870997089,1.3287692023317226), a2=doubleArrayOf(0.6029568566338351,0.6285948136865629,0.6836472041890249,0.7374497853902116,0.8705313801288671,0.9013601600372116), b0=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(-0.00017740280306388184,-2.0,-2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.870140153194092e-05,1.0,1.0,1.0,1.0,1.0))))), - antiAliasing=AntiAliasing(a1=doubleArrayOf(-1.2446640940133593e-16,-1.630640067418199e-16,-1.387778780781446e-16,-2.4980018054066027e-16,-1.9428902930940244e-16,-1.6653345369377353e-16,-2.775557561562892e-16,-1.1102230246251565e-16,-2.220446049250313e-16,-2.2204460492503136e-16), a2=doubleArrayOf(0.001543712508674131,0.014008568735804144,0.03956612989658006,0.07954045177320418,0.13610158059098823,0.21252682194185937,0.31363013821503233,0.4464626921716894,0.6214743340943358,0.8544977810681018), b0=doubleArrayOf(8.555155699386506e-06,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0), b1=doubleArrayOf(1.7110311398773013e-05,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0), b2=doubleArrayOf(8.555155699386506e-06,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0),sampleRatio=2), - aWeighting=DigitalFilterConfiguration(filterDenominator=doubleArrayOf(1.0,-4.11305010732996,6.553157702958688,-4.990918046664834,1.7857953826173913,-0.24621080268676002,0.011225878476736176), filterNumerator=doubleArrayOf(0.23430394285372055,-0.4686078857074411,-0.23430394285372066,0.937215771414882,-0.2343039428537206,-0.4686078857074411,0.23430394285372055)), - cWeighting=DigitalFilterConfiguration(filterDenominator=doubleArrayOf(1.0,-2.219190223295007,1.4551725795223749,-0.24851742382307854,0.012540782192183809), filterNumerator=doubleArrayOf(0.1978854554445731,0.0,-0.3957709108891462,0.0,0.1978854554445731)), - configuration=GeneralConfiguration(sampleRate=48000)) -} diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/AudioSource.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/AudioSource.kt similarity index 56% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/AudioSource.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/AudioSource.kt index f18f7c4..dad4faa 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/AudioSource.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/AudioSource.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import kotlinx.coroutines.flow.Flow @@ -7,17 +7,28 @@ import kotlinx.coroutines.flow.Flow * As each device */ interface AudioSource { - enum class MicrophoneLocation { LOCATION_UNKNOWN, LOCATION_MAIN_BODY, - LOCATION_MAIN_BODY_MOVABLE, LOCATION_PERIPHERAL } - enum class InitializeErrorCode { INITIALIZE_OK, INITIALIZE_WRONG_BUFFER_SIZE, - INITIALIZE_SAMPLE_RATE_NOT_SUPPORTED, INITIALIZE_ALREADY_INITIALIZED, INITIALIZE_NO_MICROPHONE} + enum class MicrophoneLocation { + LOCATION_UNKNOWN, + LOCATION_MAIN_BODY, + LOCATION_MAIN_BODY_MOVABLE, + LOCATION_PERIPHERAL + } + + enum class InitializeErrorCode { + INITIALIZE_OK, + INITIALIZE_WRONG_BUFFER_SIZE, + INITIALIZE_SAMPLE_RATE_NOT_SUPPORTED, + INITIALIZE_ALREADY_INITIALIZED, + INITIALIZE_NO_MICROPHONE + } + /** * @param sampleRate Sample rate in Hz * @param bufferSize Buffer size in bytes * @return InitializeErrorCode instance */ - suspend fun setup() : Flow + suspend fun setup(): Flow /** * Release device and will require to setup again before getting new samples @@ -25,12 +36,22 @@ interface AudioSource { */ fun release() - fun getMicrophoneLocation() : MicrophoneLocation + fun getMicrophoneLocation(): MicrophoneLocation } -data class AudioSamples(val epoch : Long, val samples : FloatArray, val errorCode: ErrorCode, val sampleRate : Int) { - enum class ErrorCode { OK, ABORTED, DEVICE_ERROR} +data class AudioSamples( + val epoch: Long, + val samples: FloatArray, + val errorCode: ErrorCode, + val sampleRate: Int, +) { + + enum class ErrorCode { + OK, + ABORTED, + DEVICE_ERROR + } override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/DatabaseDriverFactory.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/DatabaseDriverFactory.kt similarity index 79% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/DatabaseDriverFactory.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/DatabaseDriverFactory.kt index 9299a48..fc95c42 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/DatabaseDriverFactory.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/DatabaseDriverFactory.kt @@ -1,9 +1,10 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import app.cash.sqldelight.db.SqlDriver const val DATABASE_VERSION = 1 interface DatabaseDriverFactory { + suspend fun provideDbDriver(): SqlDriver -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.common.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.common.kt similarity index 72% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.common.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.common.kt index 4d6a9e1..51ff2d8 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.common.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.common.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import androidx.compose.ui.graphics.ImageBitmap diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/AcousticIndicatorsProcessing.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/AcousticIndicatorsProcessing.kt similarity index 82% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/AcousticIndicatorsProcessing.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/AcousticIndicatorsProcessing.kt index 96b5e9b..bbfd756 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/AcousticIndicatorsProcessing.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/AcousticIndicatorsProcessing.kt @@ -1,15 +1,16 @@ -package org.noise_planet.noisecapture.shared +package org.noiseplanet.noisecapture.shared -import org.noise_planet.noisecapture.AudioSamples -import org.noise_planet.noisecapture.shared.signal.SpectrumChannel -import org.noise_planet.noisecapture.shared.signal.get44100HZ -import org.noise_planet.noisecapture.shared.signal.get48000HZ +import org.noiseplanet.noisecapture.AudioSamples +import org.noiseplanet.noisecapture.shared.signal.SpectrumChannel +import org.noiseplanet.noisecapture.shared.signal.get44100HZ +import org.noiseplanet.noisecapture.shared.signal.get48000HZ import kotlin.math.log10 import kotlin.math.min import kotlin.math.pow import kotlin.math.sqrt const val WINDOW_TIME = 0.125 + // https://source.android.com/docs/compatibility/12/android-12-cdd.pdf // Android 12 // Last updated: October 4, 2021 @@ -17,13 +18,13 @@ const val WINDOW_TIME = 0.125 // 90 dB Sound Pressure Level (SPL) yields a response with RMS of 2500 for 16 bit-samples // (or -22.35 dB Full Scale for floating point/double precision samples) for each and every // microphone used to record the voice recognition audio source. -const val ANDROID_GAIN = -(-22.35-90) +const val ANDROID_GAIN = -(-22.35 - 90) -class AcousticIndicatorsProcessing(val sampleRate: Int, val dbGain : Double = ANDROID_GAIN) { +class AcousticIndicatorsProcessing(val sampleRate: Int, val dbGain: Double = ANDROID_GAIN) { private var windowLength = (sampleRate * WINDOW_TIME).toInt() private var windowData = FloatArray(windowLength) private var windowDataCursor = 0 - private val nominalFrequencies : List + private val nominalFrequencies: List private val spectrumChannel: SpectrumChannel = SpectrumChannel().apply { this.loadConfiguration( @@ -82,7 +83,9 @@ class AcousticIndicatorsProcessing(val sampleRate: Int, val dbGain : Double = AN } } -data class AcousticIndicatorsData(val epoch : Long, val leq: Double, val laeq: Double, - val rms : Double, val thirdOctave : DoubleArray, - val nominalFrequencies : List) +data class AcousticIndicatorsData( + val epoch: Long, val leq: Double, val laeq: Double, + val rms: Double, val thirdOctave: DoubleArray, + val nominalFrequencies: List +) diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/Koin.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/Koin.kt similarity index 90% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/Koin.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/Koin.kt index c3cc422..27d254e 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/Koin.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/Koin.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture.shared +package org.noiseplanet.noisecapture.shared import com.adrianwitaszak.kmmpermissions.permissions.permissionsModule import org.koin.core.KoinApplication @@ -15,7 +15,7 @@ fun initKoin( listOf( module { includes(additionalModules) - }, + }, permissionsModule, ) ) diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/MeasurementService.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/MeasurementService.kt similarity index 50% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/MeasurementService.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/MeasurementService.kt index 01bfba0..bffac7a 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/MeasurementService.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/MeasurementService.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture.shared +package org.noiseplanet.noisecapture.shared import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope @@ -7,10 +7,9 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.launch -import org.koin.core.logger.Logger -import org.noise_planet.noisecapture.AudioSource -import org.noise_planet.noisecapture.shared.signal.SpectrumData -import org.noise_planet.noisecapture.shared.signal.WindowAnalysis +import org.noiseplanet.noisecapture.AudioSource +import org.noiseplanet.noisecapture.shared.signal.SpectrumData +import org.noiseplanet.noisecapture.shared.signal.WindowAnalysis import kotlin.properties.Delegates import kotlin.reflect.KProperty @@ -20,48 +19,54 @@ typealias SpectrumDataCallback = (spectrumData: SpectrumData) -> Unit const val FFT_SIZE = 4096 const val FFT_HOP = 2048 -class MeasurementService(private val audioSource: AudioSource, private val logger: Logger) { - var storageObservers = mutableListOf<(property: KProperty<*>, oldValue: Boolean, newValue: Boolean) -> Unit>() - private var storageActivated : Boolean by Delegates.observable(false) { property, oldValue, newValue -> +class MeasurementService(private val audioSource: AudioSource) { + + var storageObservers = + mutableListOf<(property: KProperty<*>, oldValue: Boolean, newValue: Boolean) -> Unit>() + + private var storageActivated: Boolean by Delegates.observable(false) { property, oldValue, newValue -> storageObservers.forEach { it(property, oldValue, newValue) } } - private var acousticIndicatorsProcessing : AcousticIndicatorsProcessing? = null - private var fftTool : WindowAnalysis? = null + private var acousticIndicatorsProcessing: AcousticIndicatorsProcessing? = null + private var fftTool: WindowAnalysis? = null private var onAcousticIndicatorsData: AcousticIndicatorsCallback? = null private var onSpectrumData: SpectrumDataCallback? = null - private var audioJob : Job? = null + private var audioJob: Job? = null @OptIn(DelicateCoroutinesApi::class) private fun startAudioRecord() { - if(audioJob?.isActive == true) { + if (audioJob?.isActive == true) { return } audioJob = GlobalScope.launch { - audioSource.setup().collect {audioSamples -> - if(onSpectrumData != null) { - if(fftTool == null) { + audioSource.setup().collect { audioSamples -> + if (onSpectrumData != null) { + if (fftTool == null) { fftTool = WindowAnalysis(audioSamples.sampleRate, FFT_SIZE, FFT_HOP) } - fftTool?.pushSamples(audioSamples.epoch, audioSamples.samples)?.forEach { spectrumData -> - onSpectrumData?.let { callback -> callback(spectrumData) } - } + fftTool?.pushSamples(audioSamples.epoch, audioSamples.samples) + ?.forEach { spectrumData -> + onSpectrumData?.let { callback -> callback(spectrumData) } + } } - if(onAcousticIndicatorsData != null || storageActivated) { - if(acousticIndicatorsProcessing == null) { + if (onAcousticIndicatorsData != null || storageActivated) { + if (acousticIndicatorsProcessing == null) { acousticIndicatorsProcessing = AcousticIndicatorsProcessing( - audioSamples.sampleRate) + audioSamples.sampleRate + ) } - acousticIndicatorsProcessing!!.processSamples(audioSamples).forEach { - acousticIndicators -> - if(onAcousticIndicatorsData != null) { - onAcousticIndicatorsData?.let { callback -> callback(acousticIndicators) } + acousticIndicatorsProcessing!!.processSamples(audioSamples) + .forEach { acousticIndicators -> + if (onAcousticIndicatorsData != null) { + onAcousticIndicatorsData?.let { callback -> + callback( + acousticIndicators + ) + } + } } - if(storageActivated) { - - } - } } } } @@ -75,59 +80,47 @@ class MeasurementService(private val audioSource: AudioSource, private val logge /** * Start collecting measurements to be forwarded to observers */ - fun collectAudioIndicators() : Flow = callbackFlow { + fun collectAudioIndicators(): Flow = callbackFlow { setAudioIndicatorsObserver { trySend(it) } awaitClose { resetAudioIndicatorsObserver() } } - fun collectSpectrumData() : Flow = callbackFlow { + fun collectSpectrumData(): Flow = callbackFlow { setSpectrumDataObserver { trySend(it) } awaitClose { resetSpectrumDataObserver() } } - fun setSpectrumDataObserver(onSpectrumData : SpectrumDataCallback) { + private fun setSpectrumDataObserver(onSpectrumData: SpectrumDataCallback) { this.onSpectrumData = onSpectrumData startAudioRecord() } - fun canReleaseAudio() : Boolean { + private fun canReleaseAudio(): Boolean { return !storageActivated && onAcousticIndicatorsData == null && onAcousticIndicatorsData == null } - fun resetSpectrumDataObserver() { + private fun resetSpectrumDataObserver() { onSpectrumData = null - if(canReleaseAudio()) { + if (canReleaseAudio()) { stopAudioRecord() } } - fun setAudioIndicatorsObserver(onAcousticIndicatorsData: AcousticIndicatorsCallback) { + private fun setAudioIndicatorsObserver(onAcousticIndicatorsData: AcousticIndicatorsCallback) { startAudioRecord() this.onAcousticIndicatorsData = onAcousticIndicatorsData } - fun resetAudioIndicatorsObserver() { + private fun resetAudioIndicatorsObserver() { onAcousticIndicatorsData = null - if(canReleaseAudio()) { + if (canReleaseAudio()) { stopAudioRecord() } } - - /** - * Store measurements in database - */ - fun startStorage() { - storageActivated = true - } - - fun stopStorage() { - storageActivated = false - } - } diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/ScreenData.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/ScreenData.kt similarity index 75% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/ScreenData.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/ScreenData.kt index d0a56a5..2b3b97e 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/ScreenData.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/ScreenData.kt @@ -1,9 +1,9 @@ -package org.noise_planet.noisecapture.shared +package org.noiseplanet.noisecapture.shared import com.bumble.appyx.utils.multiplatform.Parcelable import com.bumble.appyx.utils.multiplatform.Parcelize -sealed class ScreenData(val title : String) : Parcelable { +sealed class ScreenData(val title: String) : Parcelable { @Parcelize data object HomeTarget : ScreenData("Home") @@ -13,4 +13,4 @@ sealed class ScreenData(val title : String) : Parcelable { @Parcelize data object MeasurementTarget : ScreenData("Measurement") -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/HomeScreen.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/HomeScreen.kt new file mode 100644 index 0000000..e8e3a48 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/HomeScreen.kt @@ -0,0 +1,122 @@ +package org.noiseplanet.noisecapture.shared.child + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.material.Button +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import com.bumble.appyx.components.backstack.BackStack +import com.bumble.appyx.components.backstack.operation.push +import com.bumble.appyx.navigation.modality.NodeContext +import com.bumble.appyx.navigation.node.LeafNode +import org.noiseplanet.noisecapture.shared.ScreenData +import org.noiseplanet.noisecapture.shared.ui.theme.CenterFocusWeak +import org.noiseplanet.noisecapture.shared.ui.theme.Help +import org.noiseplanet.noisecapture.shared.ui.theme.Info +import org.noiseplanet.noisecapture.shared.ui.theme.Map +import org.noiseplanet.noisecapture.shared.ui.theme.Mic +import org.noiseplanet.noisecapture.shared.ui.theme.Settings +import org.noiseplanet.noisecapture.shared.ui.theme.ShowChart +import org.noiseplanet.noisecapture.shared.ui.theme.clinicalNotes +import org.noiseplanet.noisecapture.shared.ui.theme.overview + +class HomeScreen( + nodeContext: NodeContext, + private val backStack: BackStack, +) : LeafNode(nodeContext) { + + @Composable + private fun MenuItems(): Array { + return arrayOf( + MenuItem( + "Test label", + Mic, + onClick = { backStack.push(ScreenData.PermissionTarget) } + ), + MenuItem( + "Measurement History", + overview(), + onClick = {} + ), + MenuItem( + "Measurement feedback", + clinicalNotes(), + onClick = {} + ), + MenuItem( + "Measurement statistics", + ShowChart, + onClick = {} + ), + MenuItem( + "Last measurement map", + Map, + onClick = {} + ), + MenuItem( + "Help", + Help, + onClick = {} + ), + MenuItem( + "About", + Info, + onClick = {} + ), + MenuItem( + "Calibration", + CenterFocusWeak, + onClick = {} + ), + MenuItem( + "Settings", + Settings, + onClick = {} + ) + ) + } + + @Composable + override fun Content(modifier: Modifier) { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background + ) { + val menuItems = MenuItems() + LazyVerticalGrid( + columns = GridCells.Adaptive(minSize = 96.dp), + contentPadding = PaddingValues( + start = 24.dp, + top = 24.dp, + end = 24.dp, + bottom = 24.dp + ), + content = { + items(menuItems.size) { index -> + Button( + onClick = menuItems[index].onClick, + modifier = Modifier.aspectRatio(1f).padding(12.dp) + ) { + Icon( + imageVector = menuItems[index].imageVector, + menuItems[index].label, + modifier.fillMaxSize() + ) + } + } + } + ) + } + } +} + +data class MenuItem(val label: String, val imageVector: ImageVector, val onClick: () -> Unit) diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/MeasurementScreen.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/MeasurementScreen.kt similarity index 66% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/MeasurementScreen.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/MeasurementScreen.kt index 1694360..50056e2 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/MeasurementScreen.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/MeasurementScreen.kt @@ -1,4 +1,7 @@ -package org.noise_planet.noisecapture.shared.child +// TODO: Remove detekt suppression after Appyx refactor +@file:Suppress("detekt:all") + +package org.noiseplanet.noisecapture.shared.child import androidx.compose.foundation.Canvas import androidx.compose.foundation.ExperimentalFoundationApi @@ -59,18 +62,18 @@ import com.bumble.appyx.navigation.node.LeafNode import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.koin.core.logger.Logger -import org.noise_planet.noisecapture.shared.ANDROID_GAIN -import org.noise_planet.noisecapture.shared.FFT_HOP -import org.noise_planet.noisecapture.shared.MeasurementService -import org.noise_planet.noisecapture.shared.ScreenData -import org.noise_planet.noisecapture.shared.WINDOW_TIME -import org.noise_planet.noisecapture.shared.signal.FAST_DECAY_RATE -import org.noise_planet.noisecapture.shared.signal.LevelDisplayWeightedDecay -import org.noise_planet.noisecapture.shared.signal.SpectrumData -import org.noise_planet.noisecapture.shared.ui.SpectrogramBitmap -import org.noise_planet.noisecapture.shared.ui.SpectrogramBitmap.Companion.toComposeColor -import org.noise_planet.noisecapture.shared.ui.asEventFlow -import org.noise_planet.noisecapture.toImageBitmap +import org.noiseplanet.noisecapture.shared.ANDROID_GAIN +import org.noiseplanet.noisecapture.shared.FFT_HOP +import org.noiseplanet.noisecapture.shared.MeasurementService +import org.noiseplanet.noisecapture.shared.ScreenData +import org.noiseplanet.noisecapture.shared.WINDOW_TIME +import org.noiseplanet.noisecapture.shared.signal.FAST_DECAY_RATE +import org.noiseplanet.noisecapture.shared.signal.LevelDisplayWeightedDecay +import org.noiseplanet.noisecapture.shared.signal.SpectrumData +import org.noiseplanet.noisecapture.shared.ui.SpectrogramBitmap +import org.noiseplanet.noisecapture.shared.ui.SpectrogramBitmap.Companion.toComposeColor +import org.noiseplanet.noisecapture.shared.ui.asEventFlow +import org.noiseplanet.noisecapture.toImageBitmap import kotlin.math.log10 import kotlin.math.max import kotlin.math.min @@ -94,19 +97,20 @@ class MeasurementScreen( nodeContext: NodeContext, val backStack: BackStack, private val measurementService: MeasurementService, - private val logger: Logger + private val logger: Logger, ) : LeafNode(nodeContext), DefaultPlatformLifecycleObserver { private var rangedB = 40.0 private var mindB = 0.0 private var dbGain = ANDROID_GAIN - val noiseColorRampSpl : List> = listOf( + val noiseColorRampSpl: List> = listOf( Pair(75F, "#FF0000".toComposeColor()), // >= 75 dB Pair(65F, "#FF8000".toComposeColor()), // >= 65 dB Pair(55F, "#FFFF00".toComposeColor()), // >= 55 dB Pair(45F, "#99FF00".toComposeColor()), // >= 45 dB - Pair(Float.NEGATIVE_INFINITY, "#00FF00".toComposeColor())) // < 45 dB + Pair(Float.NEGATIVE_INFINITY, "#00FF00".toComposeColor()) + ) // < 45 dB fun getColorIndex(noiseLevel: Double) = when { noiseLevel > 75.0 -> 0 @@ -117,27 +121,44 @@ class MeasurementScreen( } - private val scaleMode = SpectrogramBitmap.Companion.SCALE_MODE.SCALE_LOG - val spectrumCanvasState = SpectrogramViewModel(SpectrogramBitmap.SpectrogramDataModel(IntSize(1, 1), - ByteArray(Int.SIZE_BYTES),0 ,SpectrogramBitmap.Companion.SCALE_MODE.SCALE_LOG, 1.0), ArrayList(), Size.Zero) + private val scaleMode = SpectrogramBitmap.Companion.ScaleMode.SCALE_LOG + val spectrumCanvasState = SpectrogramViewModel( + SpectrogramBitmap.SpectrogramDataModel( + IntSize(1, 1), + ByteArray(Int.SIZE_BYTES), 0, SpectrogramBitmap.Companion.ScaleMode.SCALE_LOG, 1.0 + ), ArrayList(), Size.Zero + ) - var preparedLegendBitmap = LegendBitmap( ImageBitmap(1, 1), Size(0F, 0F), Size(0F, 0F), Size(0F, 0F) ) + var preparedLegendBitmap = + LegendBitmap(ImageBitmap(1, 1), Size(0F, 0F), Size(0F, 0F), Size(0F, 0F)) @Composable - fun spectrogram(spectrumCanvasState : SpectrogramViewModel, bitmapOffset : Int) { - Canvas(modifier = Modifier.fillMaxSize() ) { - val canvasSize = IntSize(SPECTROGRAM_STRIP_WIDTH, (size.height - preparedLegendBitmap.bottomLegendSize.height).toInt()) - drawRect(color = SpectrogramBitmap.colorRamp[0], size=Size(size.width - preparedLegendBitmap.rightLegendSize.width, canvasSize.height.toFloat())) - spectrumCanvasState.spectrogramCanvasSize = Size(size.width - preparedLegendBitmap.rightLegendSize.width, size.height - - preparedLegendBitmap.bottomLegendSize.height) - if(spectrumCanvasState.currentStripData.size.height != canvasSize.height) { + fun spectrogram(spectrumCanvasState: SpectrogramViewModel, bitmapOffset: Int) { + Canvas(modifier = Modifier.fillMaxSize()) { + val canvasSize = IntSize( + SPECTROGRAM_STRIP_WIDTH, + (size.height - preparedLegendBitmap.bottomLegendSize.height).toInt() + ) + drawRect( + color = SpectrogramBitmap.colorRamp[0], + size = Size( + size.width - preparedLegendBitmap.rightLegendSize.width, + canvasSize.height.toFloat() + ) + ) + spectrumCanvasState.spectrogramCanvasSize = Size( + size.width - preparedLegendBitmap.rightLegendSize.width, size.height + - preparedLegendBitmap.bottomLegendSize.height + ) + if (spectrumCanvasState.currentStripData.size.height != canvasSize.height) { // reset buffer on resize or first draw println("Clear ${spectrumCanvasState.cachedStrips.size} strips ${spectrumCanvasState.currentStripData.size.height} != ${canvasSize.height}") spectrumCanvasState.currentStripData = SpectrogramBitmap.createSpectrogram( - canvasSize, scaleMode, spectrumCanvasState.currentStripData.sampleRate) + canvasSize, scaleMode, spectrumCanvasState.currentStripData.sampleRate + ) spectrumCanvasState.cachedStrips.clear() } else { - if(spectrumCanvasState.currentStripData.sampleRate > 1) { + if (spectrumCanvasState.currentStripData.sampleRate > 1) { drawImage( spectrumCanvasState.currentStripData.byteArray.toImageBitmap(), topLeft = Offset( @@ -149,7 +170,7 @@ class MeasurementScreen( .forEachIndexed { index, imageBitmap -> val bitmapX = size.width - preparedLegendBitmap.rightLegendSize.width - ((index + 1) * SPECTROGRAM_STRIP_WIDTH - + bitmapOffset).toFloat() + + bitmapOffset).toFloat() drawImage( imageBitmap, topLeft = Offset(bitmapX, 0F) @@ -160,8 +181,10 @@ class MeasurementScreen( } } - private fun makeXLegend(textMeasurer: TextMeasurer, timeValue : Double, legendWidth : Float, - timePerPixel : Double, depth: Int) : LegendElement { + private fun makeXLegend( + textMeasurer: TextMeasurer, timeValue: Double, legendWidth: Float, + timePerPixel: Double, depth: Int, + ): LegendElement { val xPos = (legendWidth - timeValue / timePerPixel).toFloat() val legendText = buildAnnotatedString { withStyle(style = SpanStyle()) { @@ -169,39 +192,76 @@ class MeasurementScreen( } } val textLayout = textMeasurer.measure(legendText) - val textPos = min(legendWidth-textLayout.size.width, - max(0F, xPos - textLayout.size.width / 2)) + val textPos = min( + legendWidth - textLayout.size.width, + max(0F, xPos - textLayout.size.width / 2) + ) return LegendElement(legendText, textLayout.size, xPos, textPos, depth) } - private fun recursiveLegendBuild(textMeasurer: TextMeasurer, timeValue : Double, legendWidth : Float, - timePerPixel : Double, minX : Float, maxX : Float, timeValueLeft : Double, - timeValueRight : Double, feedElements: ArrayList, depth: Int) { + @Suppress("LongParameterList") + private fun recursiveLegendBuild( + textMeasurer: TextMeasurer, + timeValue: Double, + legendWidth: Float, + timePerPixel: Double, + minX: Float, + maxX: Float, + timeValueLeft: Double, + timeValueRight: Double, + feedElements: ArrayList, + depth: Int, + ) { val legendElement = makeXLegend(textMeasurer, timeValue, legendWidth, timePerPixel, depth) - if(legendElement.textPos > minX && legendElement.xPos + legendElement.textSize.width / 2 < maxX) { + if (legendElement.textPos > minX && legendElement.xPos + legendElement.textSize.width / 2 < maxX) { feedElements.add(legendElement) // left legend, + x seconds - recursiveLegendBuild(textMeasurer, timeValue + (timeValueLeft - timeValue) / 2, - legendWidth, timePerPixel, minX, legendElement.textPos, timeValueLeft, timeValue, - feedElements, depth + 1) + recursiveLegendBuild( + textMeasurer, + timeValue + (timeValueLeft - timeValue) / 2, + legendWidth, + timePerPixel, + minX, + legendElement.textPos, + timeValueLeft, + timeValue, + feedElements, + depth + 1 + ) // right legend, - x seconds - recursiveLegendBuild(textMeasurer, timeValue - (timeValue - timeValueRight) / 2, - legendWidth, timePerPixel, legendElement.textPos + legendElement.textSize.width, - maxX, timeValue, timeValueRight, feedElements, depth + 1) + recursiveLegendBuild( + textMeasurer, + timeValue - (timeValue - timeValueRight) / 2, + legendWidth, + timePerPixel, + legendElement.textPos + legendElement.textSize.width, + maxX, + timeValue, + timeValueRight, + feedElements, + depth + 1 + ) } } - fun buildLegendBitmap(size: Size, density: Density, scaleMode: SpectrogramBitmap.Companion.SCALE_MODE, - sampleRate: Double, textMeasurer: TextMeasurer, colors: Colors): LegendBitmap { + fun buildLegendBitmap( + size: Size, + density: Density, + scaleMode: SpectrogramBitmap.Companion.ScaleMode, + sampleRate: Double, + textMeasurer: TextMeasurer, + colors: Colors, + ): LegendBitmap { val drawScope = CanvasDrawScope() val bitmap = ImageBitmap(size.width.toInt(), size.height.toInt()) val canvas = androidx.compose.ui.graphics.Canvas(bitmap) var frequencyLegendPosition = when (scaleMode) { - SpectrogramBitmap.Companion.SCALE_MODE.SCALE_LOG -> SpectrogramBitmap.frequencyLegendPositionLog + SpectrogramBitmap.Companion.ScaleMode.SCALE_LOG -> SpectrogramBitmap.frequencyLegendPositionLog else -> SpectrogramBitmap.frequencyLegendPositionLinear } - frequencyLegendPosition = frequencyLegendPosition.filter { f -> f < sampleRate / 2 }.toIntArray() + frequencyLegendPosition = + frequencyLegendPosition.filter { f -> f < sampleRate / 2 }.toIntArray() val timeXLabelMeasure = textMeasurer.measure(REFERENCE_LEGEND_TEXT) val timeXLabelHeight = timeXLabelMeasure.size.height val maxYLabelWidth = @@ -220,11 +280,11 @@ class MeasurementScreen( val tickLength = 4.dp.toPx() val tickStroke = 2.dp - val legendHeight = timeXLabelHeight+tickLength - val legendWidth = maxYLabelWidth+tickLength - bottomLegendSize = Size(size.width-legendWidth, legendHeight) + val legendHeight = timeXLabelHeight + tickLength + val legendWidth = maxYLabelWidth + tickLength + bottomLegendSize = Size(size.width - legendWidth, legendHeight) rightLegendSize = Size(legendWidth, size.height - legendHeight) - if(sampleRate > 1) { + if (sampleRate > 1) { // draw Y axe labels val fMax = sampleRate / 2 val fMin = frequencyLegendPosition[0].toDouble() @@ -237,25 +297,32 @@ class MeasurementScreen( } val textSize = textMeasurer.measure(text) val tickHeightPos = when (scaleMode) { - SpectrogramBitmap.Companion.SCALE_MODE.SCALE_LOG -> { + SpectrogramBitmap.Companion.ScaleMode.SCALE_LOG -> { sheight - (log10(frequency / fMin) / ((log10(fMax / fMin) / sheight))).toInt() } + else -> (sheight - frequency / fMax * sheight).toInt() } drawLine( color = colors.onPrimary, start = Offset( size.width - legendWidth, - tickHeightPos.toFloat() - tickStroke.toPx()/2 + tickHeightPos.toFloat() - tickStroke.toPx() / 2 ), end = Offset( size.width - legendWidth + tickLength, - tickHeightPos.toFloat() - tickStroke.toPx()/2 + tickHeightPos.toFloat() - tickStroke.toPx() / 2 ), strokeWidth = tickStroke.toPx() ) - val textPos = min((size.height - textSize.size.height).toInt(), - max(0, tickHeightPos - textSize.size.height / 2)) - drawText(textMeasurer, text, topLeft = Offset(size.width - legendWidth + tickLength, textPos.toFloat())) + val textPos = min( + (size.height - textSize.size.height).toInt(), + max(0, tickHeightPos - textSize.size.height / 2) + ) + drawText( + textMeasurer, + text, + topLeft = Offset(size.width - legendWidth + tickLength, textPos.toFloat()) + ) } // draw X axe labels val xLegendWidth = (size.width - legendWidth) @@ -264,24 +331,32 @@ class MeasurementScreen( val lastTime = xLegendWidth * timePerPixel val legendElements = ArrayList() val rightLegend = makeXLegend(textMeasurer, 0.0, xLegendWidth, timePerPixel, -1) - val leftLegend = makeXLegend(textMeasurer, lastTime, xLegendWidth, timePerPixel, -1) + val leftLegend = makeXLegend(textMeasurer, lastTime, xLegendWidth, timePerPixel, -1) legendElements.add(leftLegend) legendElements.add(rightLegend) - recursiveLegendBuild(textMeasurer, lastTime / 2, xLegendWidth, timePerPixel, + recursiveLegendBuild( + textMeasurer, lastTime / 2, xLegendWidth, timePerPixel, leftLegend.textSize.width.toFloat(), - rightLegend.xPos-rightLegend.textSize.width, lastTime, 0.0, legendElements, 0) + rightLegend.xPos - rightLegend.textSize.width, lastTime, 0.0, legendElements, 0 + ) // find depth index with maximum number of elements (to generate same intervals on legend) - val legendDepthCount = IntArray(legendElements.maxOf { it.depth }+1) { 0 } + val legendDepthCount = IntArray(legendElements.maxOf { it.depth } + 1) { 0 } legendElements.forEach { - if(it.depth >= 0) { + if (it.depth >= 0) { legendDepthCount[it.depth] += 1 } } legendElements.removeAll { it.depth > 0 && legendDepthCount[it.depth] != (2.0.pow(it.depth)).toInt() } - legendElements.forEach {legendElement -> - val tickPos = max(tickStroke.toPx() / 2F, min(xLegendWidth-tickStroke.toPx(), legendElement.xPos - tickStroke.toPx() / 2F)) + legendElements.forEach { legendElement -> + val tickPos = max( + tickStroke.toPx() / 2F, + min( + xLegendWidth - tickStroke.toPx(), + legendElement.xPos - tickStroke.toPx() / 2F + ) + ) drawLine( color = colors.onPrimary, start = Offset( tickPos, @@ -293,7 +368,11 @@ class MeasurementScreen( ), strokeWidth = tickStroke.toPx() ) - drawText(textMeasurer,legendElement.text, topLeft = Offset(legendElement.textPos, sheight.toFloat() + tickLength)) + drawText( + textMeasurer, + legendElement.text, + topLeft = Offset(legendElement.textPos, sheight.toFloat() + tickLength) + ) } } } @@ -302,7 +381,7 @@ class MeasurementScreen( @Composable - fun spectrogramLegend(scaleMode: SpectrogramBitmap.Companion.SCALE_MODE, sampleRate: Double) { + fun spectrogramLegend(scaleMode: SpectrogramBitmap.Companion.ScaleMode, sampleRate: Double) { val colors = MaterialTheme.colors val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { @@ -319,18 +398,18 @@ class MeasurementScreen( fun formatFrequency(frequency: Int): String { return if (frequency >= 1000) { - if(frequency%1000 > 0) { - val subKilo = (frequency%1000).toString().trimEnd('0') - "${frequency/1000}.$subKilo kHz" + if (frequency % 1000 > 0) { + val subKilo = (frequency % 1000).toString().trimEnd('0') + "${frequency / 1000}.$subKilo kHz" } else { - "${frequency/1000} kHz" + "${frequency / 1000} kHz" } } else { "$frequency Hz" } } - fun processSpectrum(spectrumCanvasState: SpectrogramViewModel, it : SpectrumData) : Int { + fun processSpectrum(spectrumCanvasState: SpectrogramViewModel, it: SpectrumData): Int { spectrumCanvasState.currentStripData.pushSpectrumToSpectrogramData( it, mindB, rangedB, dbGain @@ -358,14 +437,16 @@ class MeasurementScreen( } @Composable - fun buildNoiseLevelText(noiseLevel : Double) : AnnotatedString = buildAnnotatedString { + fun buildNoiseLevelText(noiseLevel: Double): AnnotatedString = buildAnnotatedString { val inRangeNoise = noiseLevel > MIN_SHOWN_DBA_VALUE && noiseLevel < MAX_SHOWN_DBA_VALUE val colorIndex = noiseColorRampSpl.indexOfFirst { pair -> pair.first < noiseLevel } - withStyle(style = SpanStyle( - color = if(inRangeNoise) noiseColorRampSpl[colorIndex].second else MaterialTheme.colors.onPrimary, - fontSize = NOISE_LEVEL_FONT_SIZE, - baselineShift = BaselineShift.None - )) { + withStyle( + style = SpanStyle( + color = if (inRangeNoise) noiseColorRampSpl[colorIndex].second else MaterialTheme.colors.onPrimary, + fontSize = NOISE_LEVEL_FONT_SIZE, + baselineShift = BaselineShift.None + ) + ) { when { inRangeNoise -> append("${round(noiseLevel * 10) / 10}") else -> append("-") @@ -374,38 +455,54 @@ class MeasurementScreen( } @Composable - fun spectrumPlot(modifier: Modifier, settings: SpectrumSettings, values : SpectrumPlotData) { + fun spectrumPlot(modifier: Modifier, settings: SpectrumSettings, values: SpectrumPlotData) { val surfaceColor = MaterialTheme.colors.onSurface // color ramp 0F left side of spectrum // 1F right side of spectrum - val spectrumColorRamp = List(noiseColorRampSpl.size) { - index -> + val spectrumColorRamp = List(noiseColorRampSpl.size) { index -> val pair = noiseColorRampSpl[noiseColorRampSpl.size - 1 - index] - val linearIndex = max(0.0,((pair.first - settings.minimumX) / - (settings.maximumX-settings.minimumX))) + val linearIndex = max( + 0.0, ((pair.first - settings.minimumX) / + (settings.maximumX - settings.minimumX)) + ) Pair(linearIndex.toFloat(), pair.second) }.toTypedArray() Canvas(modifier) { val barHeight = size.height / values.spl.size - SPECTRUM_PLOT_SQUARE_OFFSET.toPx() - val pathEffect = PathEffect.dashPathEffect(floatArrayOf(SPECTRUM_PLOT_SQUARE_WIDTH.toPx(), SPECTRUM_PLOT_SQUARE_OFFSET.toPx())) + val pathEffect = PathEffect.dashPathEffect( + floatArrayOf( + SPECTRUM_PLOT_SQUARE_WIDTH.toPx(), + SPECTRUM_PLOT_SQUARE_OFFSET.toPx() + ) + ) val weightedBarWidth = 10.dp.toPx() values.spl.forEachIndexed { index, spl -> - val barYOffset = (barHeight + SPECTRUM_PLOT_SQUARE_OFFSET.toPx())*index - val splRatio = (spl-settings.minimumX)/(settings.maximumX-settings.minimumX) + val barYOffset = (barHeight + SPECTRUM_PLOT_SQUARE_OFFSET.toPx()) * index + val splRatio = (spl - settings.minimumX) / (settings.maximumX - settings.minimumX) val splWeighted = max(spl, values.splWeighted[index]) - val splWeightedRatio = (splWeighted-settings.minimumX)/(settings.maximumX-settings.minimumX) - val splGradient = Brush.horizontalGradient(*spectrumColorRamp, startX = 0F, endX = size.width) - drawLine(brush = splGradient, start = Offset(0F, barYOffset + barHeight / 2), - end = Offset((size.width*splRatio).toFloat(), barYOffset + barHeight / 2), - strokeWidth = barHeight, pathEffect = pathEffect) - drawRect(color = surfaceColor, topLeft = Offset((size.width*splWeightedRatio).toFloat()-weightedBarWidth, barYOffset), - size=Size(weightedBarWidth, barHeight)) + val splWeightedRatio = + (splWeighted - settings.minimumX) / (settings.maximumX - settings.minimumX) + val splGradient = + Brush.horizontalGradient(*spectrumColorRamp, startX = 0F, endX = size.width) + drawLine( + brush = splGradient, start = Offset(0F, barYOffset + barHeight / 2), + end = Offset((size.width * splRatio).toFloat(), barYOffset + barHeight / 2), + strokeWidth = barHeight, pathEffect = pathEffect + ) + drawRect( + color = surfaceColor, + topLeft = Offset( + (size.width * splWeightedRatio).toFloat() - weightedBarWidth, + barYOffset + ), + size = Size(weightedBarWidth, barHeight) + ) } } } @Composable - fun vueMeter(modifier: Modifier, settings: VueMeterSettings, value : Double) { + fun vueMeter(modifier: Modifier, settings: VueMeterSettings, value: Double) { val color = MaterialTheme.colors val textMeasurer = rememberTextMeasurer() Canvas(modifier = modifier) { @@ -442,7 +539,7 @@ class MeasurementScreen( cornerRadius = CornerRadius(barHeight / 2, barHeight / 2), size = Size(size.width, barHeight) ) - val valueRatio = (value-settings.minimum)/(settings.maximum-settings.minimum) + val valueRatio = (value - settings.minimum) / (settings.maximum - settings.minimum) val colorIndex = noiseColorRampSpl.indexOfFirst { pair -> pair.first < value } drawRoundRect( color = noiseColorRampSpl[colorIndex].second, @@ -461,8 +558,8 @@ class MeasurementScreen( bottomStart = 0.dp, bottomEnd = 40.dp ) - val vueMeterSettings = VueMeterSettings(20.0,120.0, - IntArray(6){v->((v+ 1)*20.0).toInt()}) + val vueMeterSettings = VueMeterSettings(20.0, 120.0, + IntArray(6) { v -> ((v + 1) * 20.0).toInt() }) Column() { Row( horizontalArrangement = Arrangement.SpaceBetween @@ -473,8 +570,10 @@ class MeasurementScreen( shape = rightRoundedSquareShape, elevation = 10.dp ) { - Row(modifier = Modifier.padding(10.dp), - horizontalArrangement = Arrangement.SpaceBetween) { + Row( + modifier = Modifier.padding(10.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { Text( buildAnnotatedString { withStyle( @@ -484,8 +583,7 @@ class MeasurementScreen( TextUnitType.Sp ), ) - ) - { append("dB(A)") } + ) { append("dB(A)") } }, modifier = Modifier.align(Alignment.CenterVertically) ) @@ -529,7 +627,8 @@ class MeasurementScreen( val animationScope = rememberCoroutineScope() val pagerState = rememberPagerState(pageCount = { MeasurementTabState.entries.size }) - val spectrumSettings = SpectrumSettings(MIN_SHOWN_DBA_VALUE_SPECTRUM, MAX_SHOWN_DBA_VALUE_SPECTRUM) + val spectrumSettings = + SpectrumSettings(MIN_SHOWN_DBA_VALUE_SPECTRUM, MAX_SHOWN_DBA_VALUE_SPECTRUM) Column(horizontalAlignment = Alignment.CenterHorizontally) { TabRow(selectedTabIndex = pagerState.currentPage) { @@ -537,22 +636,32 @@ class MeasurementScreen( Tab( text = { Text(MEASUREMENT_TAB_LABEL[entry.ordinal]) }, selected = pagerState.currentPage == entry.ordinal, - onClick = { animationScope.launch {pagerState.animateScrollToPage(entry.ordinal)} } + onClick = { animationScope.launch { pagerState.animateScrollToPage(entry.ordinal) } } ) } } - HorizontalPager(state = pagerState) {page-> + HorizontalPager(state = pagerState) { page -> when (MeasurementTabState.entries[page]) { MeasurementTabState.SPECTROGRAM -> Box(Modifier.fillMaxSize()) { spectrogram(spectrumCanvasState, bitmapOffset) spectrogramLegend(scaleMode, sampleRate) } - MeasurementTabState.SPECTRUM -> spectrumPlot(Modifier.fillMaxSize(), spectrumSettings, spectrumData) - else -> Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {Text( - text = "Text tab ${MEASUREMENT_TAB_LABEL[page]} selected", - style = MaterialTheme.typography.body1 - )} + MeasurementTabState.SPECTRUM -> spectrumPlot( + Modifier.fillMaxSize(), + spectrumSettings, + spectrumData + ) + + else -> Surface( + Modifier.fillMaxSize(), + color = MaterialTheme.colors.background + ) { + Text( + text = "Text tab ${MEASUREMENT_TAB_LABEL[page]} selected", + style = MaterialTheme.typography.body1 + ) + } } } } @@ -564,20 +673,34 @@ class MeasurementScreen( override fun Content(modifier: Modifier) { var bitmapOffset by remember { mutableStateOf(0) } var noiseLevel by remember { mutableStateOf(0.0) } - var sampleRate by remember { mutableStateOf( DEFAULT_SAMPLE_RATE ) } - var spectrumDataState by remember { mutableStateOf( SpectrumPlotData(DoubleArray(0), DoubleArray(0))) } - var indicatorCollectJob : Job? = null - var spectrumCollectJob : Job? = null - val launchMeasurementJob = fun () { + var sampleRate by remember { mutableStateOf(DEFAULT_SAMPLE_RATE) } + var spectrumDataState by remember { + mutableStateOf( + SpectrumPlotData( + DoubleArray(0), + DoubleArray(0) + ) + ) + } + var indicatorCollectJob: Job? = null + var spectrumCollectJob: Job? = null + val launchMeasurementJob = fun() { indicatorCollectJob = lifecycleScope.launch { val levelDisplay = LevelDisplayWeightedDecay(FAST_DECAY_RATE, WINDOW_TIME) - var levelDisplayBands : Array? = null + var levelDisplayBands: Array? = null measurementService.collectAudioIndicators().collect { - if(levelDisplayBands == null) { - levelDisplayBands = Array(it.nominalFrequencies.size) {LevelDisplayWeightedDecay(FAST_DECAY_RATE, WINDOW_TIME)} + if (levelDisplayBands == null) { + levelDisplayBands = Array(it.nominalFrequencies.size) { + LevelDisplayWeightedDecay( + FAST_DECAY_RATE, + WINDOW_TIME + ) + } } noiseLevel = levelDisplay.getWeightedValue(it.laeq) - val splWeightedArray = DoubleArray(it.nominalFrequencies.size) {index -> levelDisplayBands!![index].getWeightedValue(it.thirdOctave[index])} + val splWeightedArray = DoubleArray(it.nominalFrequencies.size) { index -> + levelDisplayBands!![index].getWeightedValue(it.thirdOctave[index]) + } spectrumDataState = SpectrumPlotData(it.thirdOctave, splWeightedArray) } } @@ -594,11 +717,12 @@ class MeasurementScreen( launchMeasurementJob() lifecycleScope.launch { lifecycle.asEventFlow().collect { event -> - if(event == Lifecycle.Event.ON_PAUSE) { + if (event == Lifecycle.Event.ON_PAUSE) { indicatorCollectJob?.cancel() spectrumCollectJob?.cancel() - } else if(event == Lifecycle.Event.ON_RESUME && - (indicatorCollectJob == null || indicatorCollectJob?.isActive == false)) { + } else if (event == Lifecycle.Event.ON_RESUME && + (indicatorCollectJob == null || indicatorCollectJob?.isActive == false) + ) { launchMeasurementJob() } } @@ -609,7 +733,7 @@ class MeasurementScreen( color = MaterialTheme.colors.primary ) { BoxWithConstraints { - if(maxWidth > maxHeight) { + if (maxWidth > maxHeight) { Row(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.fillMaxWidth(.5F)) { measurementHeader(noiseLevel) @@ -629,24 +753,39 @@ class MeasurementScreen( } } -data class SpectrogramViewModel(var currentStripData : SpectrogramBitmap.SpectrogramDataModel, - val cachedStrips : ArrayList, - var spectrogramCanvasSize : Size) +data class SpectrogramViewModel( + var currentStripData: SpectrogramBitmap.SpectrogramDataModel, + val cachedStrips: ArrayList, + var spectrogramCanvasSize: Size, +) + +data class LegendElement( + val text: AnnotatedString, val textSize: IntSize, val xPos: Float, + val textPos: Float, val depth: Int, +) -data class LegendElement(val text : AnnotatedString, val textSize : IntSize, val xPos : Float, - val textPos : Float, val depth : Int) +enum class MeasurementTabState { SPECTRUM, + SPECTROGRAM, + MAP +} -enum class MeasurementTabState { SPECTRUM, SPECTROGRAM, MAP} val MEASUREMENT_TAB_LABEL = listOf("Spectrum", "Spectrogram", "Map") -data class LegendBitmap(val imageBitmap: ImageBitmap, val imageSize: Size, val bottomLegendSize: Size, val rightLegendSize: Size) -data class MeasurementStatistics(val label : String, val value : String) +data class LegendBitmap( + val imageBitmap: ImageBitmap, + val imageSize: Size, + val bottomLegendSize: Size, + val rightLegendSize: Size, +) + +data class MeasurementStatistics(val label: String, val value: String) + +data class SpectrumSettings(val minimumX: Double, val maximumX: Double) -data class SpectrumSettings(val minimumX : Double, val maximumX : Double) +data class SpectrumPlotData(val spl: DoubleArray, val splWeighted: DoubleArray) -data class SpectrumPlotData(val spl : DoubleArray, val splWeighted : DoubleArray) +data class VueMeterSettings(val minimum: Double, val maximum: Double, val xLabels: IntArray) { -data class VueMeterSettings(val minimum : Double, val maximum : Double, val xLabels : IntArray) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/NavigationScreen.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/NavigationScreen.kt similarity index 83% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/NavigationScreen.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/NavigationScreen.kt index e62e135..70ce2d4 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/NavigationScreen.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/NavigationScreen.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture.shared.child +package org.noiseplanet.noisecapture.shared.child import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material.Icon @@ -13,21 +13,22 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import com.bumble.appyx.components.backstack.BackStack import com.bumble.appyx.components.backstack.operation.pop -import org.noise_planet.noisecapture.shared.ScreenData +import org.noiseplanet.noisecapture.shared.ScreenData @Composable fun NavigationScreen( backStack: BackStack, - body: @Composable (PaddingValues) -> Unit + body: @Composable (PaddingValues) -> Unit, ) { val canBackPress by backStack.canHandleBackPress().collectAsState(false) val currentTitle by backStack.model.output.collectAsState() + Scaffold( topBar = { TopAppBar( title = { Text(text = currentTitle.currentTargetState.active.interactionTarget.title) }, navigationIcon = { - IconButton(onClick = {backStack.pop()}, enabled = canBackPress) { + IconButton(onClick = { backStack.pop() }, enabled = canBackPress) { Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null) } } diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/PermissionScreen.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/PermissionScreen.kt similarity index 56% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/PermissionScreen.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/PermissionScreen.kt index 68b80f1..bd41805 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/child/PermissionScreen.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/child/PermissionScreen.kt @@ -1,4 +1,7 @@ -package org.noise_planet.noisecapture.shared.child +// TODO: Remove detekt suppression after Appyx refactor +@file:Suppress("detekt:all") + +package org.noiseplanet.noisecapture.shared.child import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState @@ -33,81 +36,92 @@ import com.bumble.appyx.navigation.modality.NodeContext import com.bumble.appyx.navigation.node.LeafNode import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch -import org.noise_planet.noisecapture.shared.ui.theme.Check -import org.noise_planet.noisecapture.shared.ui.theme.Close -import org.noise_planet.noisecapture.shared.ui.theme.QuestionMark +import org.noiseplanet.noisecapture.shared.ui.theme.Check +import org.noiseplanet.noisecapture.shared.ui.theme.Close +import org.noiseplanet.noisecapture.shared.ui.theme.QuestionMark class PermissionScreen( nodeContext: NodeContext, private val permissionsService: PermissionsService, - private val onNextClick: () -> Unit + private val onNextClick: () -> Unit, ) : LeafNode(nodeContext) { @Composable override fun Content(modifier: Modifier) { val scope = rememberCoroutineScope() - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colors.background + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background + ) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(4.dp), + contentPadding = PaddingValues( + top = 16.dp, + bottom = 64.dp, + start = 16.dp, + end = 16.dp + ), + modifier = Modifier.fillMaxSize() ) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(4.dp), - contentPadding = PaddingValues( - top = 16.dp, - bottom = 64.dp, - start = 16.dp, - end = 16.dp - ), - modifier = Modifier.fillMaxSize() - ) { - item { - Column { - Text( - text = "Permissions", - style = MaterialTheme.typography.h4, - color = MaterialTheme.colors.onSurface, - ) - Divider() - Text( - text = "In order to measure the sound level and place it on a map the application need to have access to sensitive sensors. Please tap on Request buttons.", - style = MaterialTheme.typography.body1, - color = MaterialTheme.colors.onSurface, - ) - } + item { + Column { + Text( + text = "Permissions", + style = MaterialTheme.typography.h4, + color = MaterialTheme.colors.onSurface, + ) + Divider() + Text( + text = "In order to measure the sound level and place it on a map the application need to have access to sensitive sensors. Please tap on Request buttons.", + style = MaterialTheme.typography.body1, + color = MaterialTheme.colors.onSurface, + ) } - val requiredPermissions = arrayOf(Permission.RECORD_AUDIO, Permission.LOCATION_SERVICE_ON, Permission.LOCATION_FOREGROUND, Permission.LOCATION_BACKGROUND) - item { - nextPanelAfterGranted(requiredPermissions, onNextClick = onNextClick, permissionsService) - } - items(requiredPermissions) { permission -> - val permissionState by permissionsService.checkPermissionFlow(permission) - .collectAsState(PermissionState.NOT_DETERMINED) - if(permissionState != PermissionState.NO_PERMISSION_DELEGATE) { - permissionItem( - permissionName = permission.name, - permissionState = permissionState, - onRequestClick = { - scope.launch { - permissionsService.providePermission(permission) - } - }, - onOpenSettingsClick = { - permissionsService.openSettingPage(permission) - }, - ) - } + } + val requiredPermissions = arrayOf( + Permission.RECORD_AUDIO, + Permission.LOCATION_SERVICE_ON, + Permission.LOCATION_FOREGROUND, + Permission.LOCATION_BACKGROUND + ) + item { + nextPanelAfterGranted( + requiredPermissions, + onNextClick = onNextClick, + permissionsService + ) + } + items(requiredPermissions) { permission -> + val permissionState by permissionsService.checkPermissionFlow(permission) + .collectAsState(PermissionState.NOT_DETERMINED) + if (permissionState != PermissionState.NO_PERMISSION_DELEGATE) { + permissionItem( + permissionName = permission.name, + permissionState = permissionState, + onRequestClick = { + scope.launch { + permissionsService.providePermission(permission) + } + }, + onOpenSettingsClick = { + permissionsService.openSettingPage(permission) + }, + ) } } } } + } } @Composable -internal fun nextPanelAfterGranted(permissions: Array, onNextClick: () -> Unit, - permissionsService: PermissionsService) { - val permissionsState by combine(permissions.map { p-> permissionsService.checkPermissionFlow(p)}, - transform = {it.all { p -> p != PermissionState.DENIED }}) +internal fun nextPanelAfterGranted( + permissions: Array, onNextClick: () -> Unit, + permissionsService: PermissionsService, +) { + val permissionsState by combine(permissions.map { p -> permissionsService.checkPermissionFlow(p) }, + transform = { it.all { p -> p != PermissionState.DENIED } }) .collectAsState(false) Column(modifier = Modifier.fillMaxSize()) { Spacer(modifier = Modifier.fillMaxSize()) diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/root/RootNode.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/root/RootNode.kt similarity index 82% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/root/RootNode.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/root/RootNode.kt index 93c385d..32760d7 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/root/RootNode.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/root/RootNode.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture.shared.root +package org.noiseplanet.noisecapture.shared.root import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -16,13 +16,13 @@ import com.bumble.appyx.navigation.node.Node import com.bumble.appyx.navigation.node.LeafNode import org.koin.core.Koin import org.koin.core.annotation.KoinInternalApi -import org.noise_planet.noisecapture.shared.ScreenData -import org.noise_planet.noisecapture.shared.ScreenData.HomeTarget -import org.noise_planet.noisecapture.shared.ScreenData.PermissionTarget -import org.noise_planet.noisecapture.shared.child.HomeScreen -import org.noise_planet.noisecapture.shared.child.MeasurementScreen -import org.noise_planet.noisecapture.shared.child.NavigationScreen -import org.noise_planet.noisecapture.shared.child.PermissionScreen +import org.noiseplanet.noisecapture.shared.ScreenData +import org.noiseplanet.noisecapture.shared.ScreenData.HomeTarget +import org.noiseplanet.noisecapture.shared.ScreenData.PermissionTarget +import org.noiseplanet.noisecapture.shared.child.HomeScreen +import org.noiseplanet.noisecapture.shared.child.MeasurementScreen +import org.noiseplanet.noisecapture.shared.child.NavigationScreen +import org.noiseplanet.noisecapture.shared.child.PermissionScreen class RootNode( nodeContext: NodeContext, @@ -41,16 +41,18 @@ class RootNode( @OptIn(KoinInternalApi::class) override fun buildChildNode(navTarget: ScreenData, nodeContext: NodeContext): LeafNode = - when(navTarget) { + when (navTarget) { is PermissionTarget -> PermissionScreen( nodeContext, koin.get(), ::onPermissionGrantedBeforeMeasurement ) + is HomeTarget -> HomeScreen( nodeContext, backStack ) + is ScreenData.MeasurementTarget -> MeasurementScreen( nodeContext, backStack, diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/BiquadFilter.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/BiquadFilter.kt similarity index 75% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/BiquadFilter.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/BiquadFilter.kt index 703c381..d1369f3 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/BiquadFilter.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/BiquadFilter.kt @@ -24,7 +24,7 @@ * 14-20 Boulevard Newton Cite Descartes, Champs sur Marne F-77447 Marne la Vallee Cedex 2 FRANCE * or write to scientific.computing@ifsttar.fr */ -package org.noise_planet.noisecapture.shared.signal +package org.noiseplanet.noisecapture.shared.signal import kotlin.math.log10 @@ -38,23 +38,25 @@ import kotlin.math.log10 * @author Valentin Le Bescond, Université Gustave Eiffel */ class BiquadFilter( - var b0: DoubleArray, - var b1: DoubleArray, - var b2: DoubleArray, + private var b0: DoubleArray, + private var b1: DoubleArray, + private var b2: DoubleArray, var a1: DoubleArray, - var a2: DoubleArray + var a2: DoubleArray, ) { - var delay1: DoubleArray - var delay2: DoubleArray + + private var delay1: DoubleArray + private var delay2: DoubleArray init { delay1 = DoubleArray(b0.size) delay2 = DoubleArray(b0.size) - if(!(b0.size == b1.size && b1.size == b2.size && b2.size == a1.size - && a1.size == a2.size)) { - throw IllegalArgumentException("Issues with filter size b0.size == ${b0.size}" + + if (b0.size != b1.size || b1.size != b2.size || b2.size != a1.size || a1.size != a2.size) { + throw IllegalArgumentException( + "Issues with filter size b0.size == ${b0.size}" + " b1.size == ${b1.size} b2.size == ${b2.size} a1.size == ${a1.size}" + - " a2.size = ${a2.size}") + " a2.size = ${a2.size}" + ) } } @@ -83,25 +85,25 @@ class BiquadFilter( return 10 * log10(squareSum / samples.size) } - fun filterSlice(samplesIn: FloatArray, samplesOut: FloatArray, subsampling_factor: Int) { - var samples_out_index = 0 - var output_acc: Double + fun filterSlice(samplesIn: FloatArray, samplesOut: FloatArray, subsamplingFactor: Int) { + var samplesOutIndex = 0 + var outputAcc: Double for (i in samplesIn.indices) { - var input_acc = samplesIn[i].toDouble() + var inputAcc = samplesIn[i].toDouble() for (j in b0.indices) { - input_acc -= delay1[j] * a1[j] - input_acc -= delay2[j] * a2[j] - output_acc = input_acc * b0[j] - output_acc += delay1[j] * b1[j] - output_acc += delay2[j] * b2[j] + inputAcc -= delay1[j] * a1[j] + inputAcc -= delay2[j] * a2[j] + outputAcc = inputAcc * b0[j] + outputAcc += delay1[j] * b1[j] + outputAcc += delay2[j] * b2[j] delay2[j] = delay1[j] - delay1[j] = input_acc - input_acc = output_acc + delay1[j] = inputAcc + inputAcc = outputAcc } - if (i % subsampling_factor == 0) { - samplesOut[samples_out_index] = input_acc.toFloat() - samples_out_index++ + if (i % subsamplingFactor == 0) { + samplesOut[samplesOutIndex] = inputAcc.toFloat() + samplesOutIndex++ } } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/Bluestein.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/Bluestein.kt new file mode 100644 index 0000000..3dd2277 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/Bluestein.kt @@ -0,0 +1,147 @@ +package org.noiseplanet.noisecapture.shared.signal + +import kotlin.math.PI +import kotlin.math.atan +import kotlin.math.cos +import kotlin.math.exp +import kotlin.math.ln +import kotlin.math.max +import kotlin.math.pow +import kotlin.math.sin +import kotlin.math.sqrt + +/** + * Chirp transform for non power of two fft + * + * [Details](https://gist.github.com/fnielsen/99b981b9da34ae3d5035) + */ +class Bluestein(private val windowLength: Int) { + + companion object { + + operator fun Double.plus(other: Complex) = Complex(this + other.real, other.imag) + operator fun Double.minus(other: Complex) = Complex(this - other.real, -other.imag) + operator fun Double.times(other: Complex) = Complex( + this * other.real - this * other.imag, + this * other.imag + this * other.real + ) + + operator fun Double.div(other: Complex) = Complex( + (this * other.real) / + (other.real * other.real + other.imag * other.imag), + (-this * other.imag) / (other.real * other.real + other.imag * other.imag) + ) + + @Suppress("TooManyFunctions") + data class Complex(val real: Double, val imag: Double) { + + operator fun plus(other: Complex) = Complex(real + other.real, imag + other.imag) + operator fun minus(other: Complex) = Complex(real - other.real, imag - other.imag) + operator fun times(other: Complex) = Complex( + real * other.real - imag * other.imag, + real * other.imag + imag * other.real + ) + + operator fun times(d: Double): Complex = Complex(real * d, imag * d) + + operator fun div(c: Double): Complex = Complex(real / c, imag / c) + operator fun div(other: Complex) = Complex( + (real * other.real + imag * other.imag) / (other.real * other.real + other.imag * other.imag), + (imag * other.real - real * other.imag) / (other.real * other.real + other.imag * other.imag) + ) + + fun module(): Double = sqrt(real.pow(2) + imag.pow(2)) + fun arg(): Double { + return when { + this.real == 0.0 && this.imag > 0.0 -> PI / 2 + this.real == 0.0 && this.imag < 0.0 -> -PI / 2 + this.real > 0.0 -> atan(this.imag / this.real) + this.real < 0.0 && this.imag >= 0.0 -> atan(this.imag / this.real) + PI + this.real < 0.0 && this.imag < 0.0 -> atan(this.imag / this.real) - PI + else -> 0.0 + } + } + + fun ln(): Complex { + return Complex(ln(this.module()), arg()) + } + + fun exp(): Complex { + val r: Double = exp(this.real) + return Complex(r * cos(this.imag), r * sin(this.imag)) + } + + fun pow(c: Complex): Complex = (c * this.ln()).exp() + fun pow(c: Double): Complex = (this.ln() * c).exp() + + operator fun unaryMinus() = Complex(-real, -imag) + + override fun toString() = "$real ${if (imag >= 0) '+' else ' '} ${imag}i" + } + + val Double.im get() = Complex(0.0, this) + } + + val n = windowLength + val m = n + val w = (Complex(0.0, -2.0) * PI / m.toDouble()).exp() + val a = 1.0 + val chirp = ((1 - n).. + val c = w.pow(i.toDouble().pow(2) / 2.0) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + realImagArray + } + val n2 = nextPowerOfTwo(m + n - 1) + val ichirp = (0.. + if (i < m + n - 1) { + val c = 1.0 / Complex(chirp[index * 2], chirp[index * 2 + 1]) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + } + realImagArray + } + + init { + fft(ichirp.size / 2, ichirp) + } + + fun fft(x: DoubleArray): DoubleArray { + require(x.size == windowLength * 2) + val xp = (0.. + if (i < n) { + val realIndex = index * 2 + val imIndex = index * 2 + 1 + val chirpOffset = (n - 1) * 2 + val c = Complex(x[realIndex], x[imIndex]) * Complex( + chirp[chirpOffset + realIndex], + chirp[chirpOffset + imIndex] + ) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + } + realImagArray + } + fft(xp.size / 2, xp) + val r = (0.. + val realIndex = index * 2 + val imIndex = index * 2 + 1 + val c = + Complex(xp[realIndex], xp[imIndex]) * Complex(ichirp[realIndex], ichirp[imIndex]) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + realImagArray + } + iFFT(r.size / 2, r) + return (n - 1.. + val realIndex = i * 2 + val imIndex = i * 2 + 1 + val c = Complex(r[realIndex], r[imIndex]) * Complex(chirp[realIndex], chirp[imIndex]) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + realImagArray + } + } +} diff --git a/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/BluesteinFloat.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/BluesteinFloat.kt new file mode 100644 index 0000000..296a377 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/BluesteinFloat.kt @@ -0,0 +1,147 @@ +package org.noiseplanet.noisecapture.shared.signal + +import kotlin.math.PI +import kotlin.math.atan +import kotlin.math.cos +import kotlin.math.exp +import kotlin.math.ln +import kotlin.math.max +import kotlin.math.pow +import kotlin.math.sin +import kotlin.math.sqrt + +/** + * Chirp transform for non power of two fft + * + * [Details](https://gist.github.com/fnielsen/99b981b9da34ae3d5035) + */ +class BluesteinFloat(private val windowLength: Int) { + + companion object { + + val PIF = PI.toFloat() + operator fun Float.plus(other: Complex) = Complex(this + other.real, other.imag) + operator fun Float.minus(other: Complex) = Complex(this - other.real, -other.imag) + operator fun Float.times(other: Complex) = Complex( + this * other.real - this * other.imag, + this * other.imag + this * other.real + ) + + operator fun Float.div(other: Complex) = Complex(this, 0.0F) / other + + @Suppress("TooManyFunctions") + data class Complex(val real: Float, val imag: Float) { + + operator fun plus(other: Complex) = Complex(real + other.real, imag + other.imag) + operator fun minus(other: Complex) = Complex(real - other.real, imag - other.imag) + operator fun times(other: Complex) = Complex( + real * other.real - imag * other.imag, + real * other.imag + imag * other.real + ) + + operator fun times(d: Float): Complex = Complex(real * d, imag * d) + + operator fun div(c: Float): Complex = Complex(real / c, imag / c) + operator fun div(other: Complex) = Complex( + (real * other.real + imag * other.imag) / (other.real * other.real + other.imag * other.imag), + (imag * other.real - real * other.imag) / (other.real * other.real + other.imag * other.imag) + ) + + fun module(): Float = sqrt(real.pow(2) + imag.pow(2)) + fun arg(): Float { + return when { + this.real == 0.0F && this.imag > 0.0 -> PIF / 2 + this.real == 0.0F && this.imag < 0.0 -> -PIF / 2 + this.real > 0.0F -> atan(this.imag / this.real) + this.real < 0.0F && this.imag >= 0.0 -> atan(this.imag / this.real) + PIF + this.real < 0.0F && this.imag < 0.0 -> atan(this.imag / this.real) - PIF + else -> 0.0F + } + } + + fun ln(): Complex { + return Complex(ln(this.module()), arg()) + } + + fun exp(): Complex { + val r: Float = exp(this.real) + return Complex(r * cos(this.imag), r * sin(this.imag)) + } + + fun pow(c: Complex): Complex = (c * this.ln()).exp() + fun pow(c: Float): Complex = (this.ln() * c).exp() + + operator fun unaryMinus() = Complex(-real, -imag) + + override fun toString() = "$real ${if (imag >= 0) '+' else ' '} ${imag}i" + } + + val Float.im get() = Complex(0.0F, this) + } + + val n = windowLength + val m = n + val w = (Complex(0.0F, -2.0F) * PIF / m.toFloat()).exp() + val a = 1.0F + private val chirp = ((1 - n).. + val c = w.pow(i.toFloat().pow(2) / 2.0F) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + realImagArray + } + private val n2 = nextPowerOfTwo(m + n - 1) + private val ichirp = (0.. + if (i < m + n - 1) { + val c = 1.0F / Complex(chirp[index * 2], chirp[index * 2 + 1]) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + } + realImagArray + } + + init { + fftFloat(ichirp.size / 2, ichirp) + } + + fun fft(x: FloatArray): FloatArray { + require(x.size == windowLength * 2) + val xp = (0.. + if (i < n) { + val realIndex = index * 2 + val imIndex = index * 2 + 1 + val chirpOffset = (n - 1) * 2 + val c = Complex( + x[realIndex], + x[imIndex] + ) * a.pow(-i) * Complex( + chirp[chirpOffset + realIndex], + chirp[chirpOffset + imIndex] + ) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + } + realImagArray + } + fftFloat(xp.size / 2, xp) + val r = (0.. + val realIndex = index * 2 + val imIndex = index * 2 + 1 + val c = + Complex(xp[realIndex], xp[imIndex]) * Complex(ichirp[realIndex], ichirp[imIndex]) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + realImagArray + } + iFFTFloat(r.size / 2, r) + return (n - 1.. + val realIndex = i * 2 + val imIndex = i * 2 + 1 + val c = Complex(r[realIndex], r[imIndex]) * Complex(chirp[realIndex], chirp[imIndex]) + realImagArray[index * 2] = c.real + realImagArray[index * 2 + 1] = c.imag + realImagArray + } + } +} diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/DigitalFilter.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/DigitalFilter.kt similarity index 98% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/DigitalFilter.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/DigitalFilter.kt index f429ae3..0b95852 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/DigitalFilter.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/DigitalFilter.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture.shared.signal +package org.noiseplanet.noisecapture.shared.signal import kotlin.math.log10 diff --git a/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt new file mode 100644 index 0000000..bdb589f --- /dev/null +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/LevelDisplayWeightedDecay.kt @@ -0,0 +1,24 @@ +package org.noiseplanet.noisecapture.shared.signal + +import kotlin.math.log10 +import kotlin.math.pow + + +const val FAST_DECAY_RATE = -34.7 +const val SLOW_DECAY_RATE = -4.3 + +/** + * IEC 61672-1 standard for displayed sound level decay + */ +class LevelDisplayWeightedDecay(decibelDecayPerSecond: Double, newValueTimeInterval: Double) { + + val timeWeight = 10.0.pow(decibelDecayPerSecond * newValueTimeInterval / 10.0) + var timeIntegration = 0.0 + + fun getWeightedValue(newValue: Double): Double { + timeIntegration = + timeIntegration * timeWeight + 10.0.pow(newValue / 10.0) * (1 - timeWeight) + return 10 * log10(timeIntegration) + } + +} diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/SpectrumChannel.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/SpectrumChannel.kt similarity index 85% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/SpectrumChannel.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/SpectrumChannel.kt index 2167c5c..2700bfe 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/SpectrumChannel.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/SpectrumChannel.kt @@ -1,9 +1,7 @@ -package org.noise_planet.noisecapture.shared.signal +package org.noiseplanet.noisecapture.shared.signal -import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch -import kotlin.math.max import kotlin.math.pow /** @@ -31,20 +29,11 @@ class SpectrumChannel { * @param useCascade Reduce computation time by subsampling the audio according to filter frequency range */ fun loadConfiguration(configuration: SpectrumChannelConfiguration, useCascade: Boolean = true) { - subSamplers.clear() - bandFilterSize = 0 - aWeightingFilter = null - cWeightingFilter = null - iirFilters.clear() - subsamplingRatio = 0 - nominalFrequency.clear() + clearState() if (configuration.bandpass.isNotEmpty()) { var maxSubsampling = 0 if (useCascade) { - for (biquad in configuration.bandpass) { - maxSubsampling = - max(maxSubsampling, biquad.subsamplingDepth) - } + maxSubsampling = configuration.bandpass.maxOfOrNull { it.subsamplingDepth } ?: 0 } iirFilters = mutableListOf() subsamplingRatio = configuration.antiAliasing.sampleRatio @@ -89,12 +78,14 @@ class SpectrumChannel { if (configuration.aWeighting != null) { aWeightingFilter = DigitalFilter( configuration.aWeighting.filterNumerator, - configuration.aWeighting.filterDenominator) + configuration.aWeighting.filterDenominator + ) } if (configuration.cWeighting != null) { cWeightingFilter = DigitalFilter( configuration.cWeighting.filterNumerator, - configuration.cWeighting.filterDenominator) + configuration.cWeighting.filterDenominator + ) } } } @@ -107,33 +98,35 @@ class SpectrumChannel { } fun processSamplesWeightA(samples: FloatArray): Double { - return aWeightingFilter?.filterLeq(samples) - ?: throw IllegalStateException("A weighting filter not configured") + return checkNotNull(aWeightingFilter?.filterLeq(samples)) { + "A weighting filter not configured" + } } fun processSamplesWeightC(samples: FloatArray): Double { - return cWeightingFilter?.filterLeq(samples) - ?: throw IllegalStateException("C weighting filter not configured") + return checkNotNull(cWeightingFilter?.filterLeq(samples)) { + "C weighting filter not configured" + } } - suspend fun processSamples(samples: FloatArray, parallel : Boolean = true): DoubleArray { + suspend fun processSamples(samples: FloatArray, parallel: Boolean = true): DoubleArray { if (samples.size % minimumSamplesLength != 0) { throw IllegalArgumentException( "Provided samples len should be a" + - " factor of $minimumSamplesLength samples" + " factor of $minimumSamplesLength samples" ) } if (iirFilters.isEmpty()) { throw IllegalStateException( "Loaded configuration does not contain bandpass" + - " filters" + " filters" ) } var lastFilterSamples = samples val leqs = DoubleArray(bandFilterSize) for (cascadeIndex in iirFilters.indices) { val cascadeFilters: HashMap = iirFilters[cascadeIndex] - if(parallel) { + if (parallel) { coroutineScope { for ((key, value) in cascadeFilters.entries) { launch { @@ -158,4 +151,14 @@ class SpectrumChannel { } return leqs } -} \ No newline at end of file + + private fun clearState() { + subSamplers.clear() + bandFilterSize = 0 + aWeightingFilter = null + cWeightingFilter = null + iirFilters.clear() + subsamplingRatio = 0 + nominalFrequency.clear() + } +} diff --git a/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt new file mode 100644 index 0000000..b22a3ff --- /dev/null +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/SpectrumChannelConfiguration.kt @@ -0,0 +1,3066 @@ +@file:Suppress("LongMethod") + +package org.noiseplanet.noisecapture.shared.signal + + +data class SpectrumChannelConfiguration( + val bandpass: List, + val antiAliasing: AntiAliasing, + val aWeighting: DigitalFilterConfiguration?, + val cWeighting: DigitalFilterConfiguration?, + val configuration: GeneralConfiguration, +) + +data class DigitalFilterConfiguration( + val filterDenominator: DoubleArray, + val filterNumerator: DoubleArray, +) + +data class AntiAliasing( + val a1: DoubleArray, + val a2: DoubleArray, + val b0: DoubleArray, + val b1: DoubleArray, + val b2: DoubleArray, + val sampleRatio: Int, +) + +data class Bandpass( + val centerFrequency: Double, + val maxFrequency: Double, + val minFrequency: Double, + val nominalFrequency: Double, + val subsamplingDepth: Int, + val sos: Sos, + val subsamplingFilter: SubsamplingFilter, +) + +data class GeneralConfiguration( + val sampleRate: Int, +) + +data class Sos( + val a1: DoubleArray, + val a2: DoubleArray, + val b0: DoubleArray, + val b1: DoubleArray, + val b2: DoubleArray, +) + +data class SubsamplingFilter( + val centerFrequency: Double, + val maxFrequency: Double, + val minFrequency: Double, + val nominalFrequency: Double, + val sos: Sos, +) + + +fun get44100HZ(): SpectrumChannelConfiguration { + return SpectrumChannelConfiguration( + bandpass = listOf( + Bandpass( + 49.60628287400625, + 55.681169883771226, + 44.19417382415923, + 50.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9983198086546723, + -1.9984209770619399, + -1.99868995344204, + -1.9988956714388693, + -1.9994669879419251, + -1.9995836340086606 + ), + a2 = doubleArrayOf( + 0.9983728225106281, + 0.9984679700150535, + 0.9987487770098534, + 0.9989380410501625, + 0.9995294236302933, + 0.999623581666138 + ), + b0 = doubleArrayOf(2.993188043570381e-19, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.986376087140762e-19, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.993188043570381e-19, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 49.60628287400625, + 55.681169883771226, + 44.19417382415923, + 50.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9983198086546723, + -1.9984209770619399, + -1.99868995344204, + -1.9988956714388693, + -1.9994669879419251, + -1.9995836340086606 + ), + a2 = doubleArrayOf( + 0.9983728225106281, + 0.9984679700150535, + 0.9987487770098534, + 0.9989380410501625, + 0.9995294236302933, + 0.999623581666138 + ), + b0 = doubleArrayOf(2.993188043570381e-19, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.986376087140762e-19, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.993188043570381e-19, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 62.5, + 70.15387801933582, + 55.681169883771204, + 63.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.997866182386905, + -1.9979955649826082, + -1.9983304542326876, + -1.9985949509811112, + -1.9993080437878314, + -1.9994623556213569 + ), + a2 = doubleArrayOf( + 0.9979503186377896, + 0.9980701466331582, + 0.9984238153827981, + 0.9986621991296034, + 0.9994071479024906, + 0.9995257653492752 + ), + b0 = doubleArrayOf(1.196292648558012e-18, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.392585297116024e-18, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.196292648558012e-18, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 62.5, + 70.15387801933582, + 55.681169883771204, + 63.0, + sos = Sos( + a1 = doubleArrayOf( + -1.997866182386905, + -1.9979955649826082, + -1.9983304542326876, + -1.9985949509811112, + -1.9993080437878314, + -1.9994623556213569 + ), + a2 = doubleArrayOf( + 0.9979503186377896, + 0.9980701466331582, + 0.9984238153827981, + 0.9986621991296034, + 0.9994071479024906, + 0.9995257653492752 + ), + b0 = doubleArrayOf(1.196292648558012e-18, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.392585297116024e-18, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.196292648558012e-18, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 78.74506561842958, + 88.38834764831844, + 70.15387801933582, + 80.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9972847298727139, + -1.9974507843363472, + -1.9978663704766202, + -1.9982080364995807, + -1.9990958070523397, + -1.9993018875143298 + ), + a2 = doubleArrayOf( + 0.997418251755548, + 0.9975691452302478, + 0.9980145410531994, + 0.9983147673823352, + 0.9992531121476155, + 0.999402537668409 + ), + b0 = doubleArrayOf(4.780224578976738e-18, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.560449157953475e-18, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.780224578976738e-18, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 78.74506561842958, + 88.38834764831844, + 70.15387801933582, + 80.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9972847298727139, + -1.9974507843363472, + -1.9978663704766202, + -1.9982080364995807, + -1.9990958070523397, + -1.9993018875143298 + ), + a2 = doubleArrayOf( + 0.997418251755548, + 0.9975691452302478, + 0.9980145410531994, + 0.9983147673823352, + 0.9992531121476155, + 0.999402537668409 + ), + b0 = doubleArrayOf(4.780224578976738e-18, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.560449157953475e-18, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.780224578976738e-18, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 99.21256574801247, + 111.36233976754241, + 88.38834764831843, + 100.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9965364131606704, + -1.996750453198385, + -1.9972639850393383, + -1.9977078138459268, + -1.9988093943603864, + -1.9990875422352576 + ), + a2 = doubleArrayOf( + 0.996748293529012, + 0.9969382790329787, + 0.9974991288319082, + 0.9978772006004107, + 0.999059074482199, + 0.9992473012061489 + ), + b0 = doubleArrayOf(1.9096008867615595e-17, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.819201773523119e-17, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.9096008867615595e-17, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 99.21256574801247, + 111.36233976754241, + 88.38834764831843, + 100.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9965364131606704, + -1.996750453198385, + -1.9972639850393383, + -1.9977078138459268, + -1.9988093943603864, + -1.9990875422352576 + ), + a2 = doubleArrayOf( + 0.996748293529012, + 0.9969382790329787, + 0.9974991288319082, + 0.9978772006004107, + 0.999059074482199, + 0.9992473012061489 + ), + b0 = doubleArrayOf(1.9096008867615595e-17, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.819201773523119e-17, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.9096008867615595e-17, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 125.0, + 140.30775603867164, + 111.36233976754241, + 125.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9955686463431954, + -1.9958459676577474, + -1.9964769915778326, + -1.9970573610880828, + -1.9984183696347848, + -1.9987981731888196 + ), + a2 = doubleArrayOf( + 0.9959048400703698, + 0.9961440013871193, + 0.9968501336181875, + 0.9973261694669386, + 0.9988146588348366, + 0.999051747964678 + ), + b0 = doubleArrayOf(7.625883439127173e-17, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.5251766878254346e-16, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.625883439127173e-17, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 125.0, + 140.30775603867164, + 111.36233976754241, + 125.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9955686463431954, + -1.9958459676577474, + -1.9964769915778326, + -1.9970573610880828, + -1.9984183696347848, + -1.9987981731888196 + ), + a2 = doubleArrayOf( + 0.9959048400703698, + 0.9961440013871193, + 0.9968501336181875, + 0.9973261694669386, + 0.9988146588348366, + 0.999051747964678 + ), + b0 = doubleArrayOf(7.625883439127173e-17, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.5251766878254346e-16, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.625883439127173e-17, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 157.49013123685916, + 176.7766952966369, + 140.30775603867164, + 160.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9943097900886524, + -1.9946713116001737, + -1.9954409879611368, + -1.9962057847910375, + -1.997877844403779, + -1.9984029467524744 + ), + a2 = doubleArrayOf( + 0.9948431721342493, + 0.9951441671210961, + 0.9960330611133081, + 0.9966323377918527, + 0.9985068052423528, + 0.9988054170367806 + ), + b0 = doubleArrayOf(3.044058450203638e-16, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(6.088116900407276e-16, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.044058450203638e-16, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 157.49013123685916, + 176.7766952966369, + 140.30775603867164, + 160.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9943097900886524, + -1.9946713116001737, + -1.9954409879611368, + -1.9962057847910375, + -1.997877844403779, + -1.9984029467524744 + ), + a2 = doubleArrayOf( + 0.9948431721342493, + 0.9951441671210961, + 0.9960330611133081, + 0.9966323377918527, + 0.9985068052423528, + 0.9988054170367806 + ), + b0 = doubleArrayOf(3.044058450203638e-16, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(6.088116900407276e-16, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.044058450203638e-16, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 198.42513149602493, + 222.72467953508482, + 176.77669529663686, + 200.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9926610639212046, + -1.993135746076362, + -1.9940652391539457, + -1.995082032908412, + -1.997120891752752, + -1.997856368425131 + ), + a2 = doubleArrayOf( + 0.9935071670438599, + 0.9938858674931755, + 0.995004585865703, + 0.995758833577239, + 0.9981190804995169, + 0.9984951384537887 + ), + b0 = doubleArrayOf(1.214460313047029e-15, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.428920626094058e-15, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.214460313047029e-15, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 198.42513149602493, + 222.72467953508482, + 176.77669529663686, + 200.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9926610639212046, + -1.993135746076362, + -1.9940652391539457, + -1.995082032908412, + -1.997120891752752, + -1.997856368425131 + ), + a2 = doubleArrayOf( + 0.9935071670438599, + 0.9938858674931755, + 0.995004585865703, + 0.995758833577239, + 0.9981190804995169, + 0.9984951384537887 + ), + b0 = doubleArrayOf(1.214460313047029e-15, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.428920626094058e-15, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.214460313047029e-15, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 250.0, + 280.6155120773433, + 222.72467953508482, + 250.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.990484543589923, + -1.9911129858369792, + -1.9922202506858138, + -1.9935856131585172, + -1.9960467535953117, + -1.9970905797877736 + ), + a2 = doubleArrayOf( + 0.9918264632393188, + 0.9923027422852423, + 0.9937103364002111, + 0.9946593406583514, + 0.9976308150496469, + 0.9981043341042959 + ), + b0 = doubleArrayOf(4.841959696459329e-15, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.683919392918658e-15, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.841959696459329e-15, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 250.0, + 280.6155120773433, + 222.72467953508482, + 250.0, + sos = Sos( + a1 = doubleArrayOf( + -1.990484543589923, + -1.9911129858369792, + -1.9922202506858138, + -1.9935856131585172, + -1.9960467535953117, + -1.9970905797877736 + ), + a2 = doubleArrayOf( + 0.9918264632393188, + 0.9923027422852423, + 0.9937103364002111, + 0.9946593406583514, + 0.9976308150496469, + 0.9981043341042959 + ), + b0 = doubleArrayOf(4.841959696459329e-15, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.683919392918658e-15, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.841959696459329e-15, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 314.9802624737183, + 353.5533905932738, + 280.6155120773433, + 315.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9875851916090912, + -1.9884250270785415, + -1.9897188898480878, + -1.9915725553870065, + -1.9945024522551325, + -1.9960033805175177 + ), + a2 = doubleArrayOf( + 0.9897129626266903, + 0.9903116554314061, + 0.9920821533812527, + 0.9932757218314784, + 0.9970160247033005, + 0.997612139139008 + ), + b0 = doubleArrayOf(1.9288175446367492e-14, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.8576350892734984e-14, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.9288175446367492e-14, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 314.9802624737183, + 353.5533905932738, + 280.6155120773433, + 315.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9875851916090912, + -1.9884250270785415, + -1.9897188898480878, + -1.9915725553870065, + -1.9945024522551325, + -1.9960033805175177 + ), + a2 = doubleArrayOf( + 0.9897129626266903, + 0.9903116554314061, + 0.9920821533812527, + 0.9932757218314784, + 0.9970160247033005, + 0.997612139139008 + ), + b0 = doubleArrayOf(1.9288175446367492e-14, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.8576350892734984e-14, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.9288175446367492e-14, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 396.8502629920499, + 445.44935907016975, + 353.5533905932738, + 400.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.983683760843643, + -1.9848177973188836, + -1.9862875464023864, + -1.9888340386407348, + -1.9922540564683868, + -1.9944395456030453 + ), + a2 = doubleArrayOf( + 0.9870565418362137, + 0.9878086030156703, + 0.9900347138507755, + 0.9915350658113805, + 0.996242067775943, + 0.9969922977121467 + ), + b0 = doubleArrayOf(7.675358798811291e-14, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.5350717597622583e-13, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.675358798811291e-14, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 396.8502629920499, + 445.44935907016975, + 353.5533905932738, + 400.0, + sos = Sos( + a1 = doubleArrayOf( + -1.983683760843643, + -1.9848177973188836, + -1.9862875464023864, + -1.9888340386407348, + -1.9922540564683868, + -1.9944395456030453 + ), + a2 = doubleArrayOf( + 0.9870565418362137, + 0.9878086030156703, + 0.9900347138507755, + 0.9915350658113805, + 0.996242067775943, + 0.9969922977121467 + ), + b0 = doubleArrayOf(7.675358798811291e-14, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.5350717597622583e-13, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.675358798811291e-14, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 500.0, + 561.2310241546866, + 445.44935907016963, + 500.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.978375713144662, + -1.9799242729184654, + -1.9815219064760534, + -1.9850636820103815, + -1.9889417297107825, + -1.9921616221501939 + ), + a2 = doubleArrayOf( + 0.9837198346904802, + 0.9846637063121387, + 0.9874614057534, + 0.9893460286738955, + 0.9952679803456205, + 0.9962117744393044 + ), + b0 = doubleArrayOf(3.050178389757522e-13, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(6.100356779515044e-13, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.050178389757522e-13, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 500.0, + 561.2310241546866, + 445.44935907016963, + 500.0, + sos = Sos( + a1 = doubleArrayOf( + -1.978375713144662, + -1.9799242729184654, + -1.9815219064760534, + -1.9850636820103815, + -1.9889417297107825, + -1.9921616221501939 + ), + a2 = doubleArrayOf( + 0.9837198346904802, + 0.9846637063121387, + 0.9874614057534, + 0.9893460286738955, + 0.9952679803456205, + 0.9962117744393044 + ), + b0 = doubleArrayOf(3.050178389757522e-13, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(6.100356779515044e-13, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.050178389757522e-13, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 629.9605249474366, + 707.1067811865476, + 561.2310241546866, + 630.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9710687181060893, + -1.9732083856639946, + -1.97481898635759, + -1.9798073097638182, + -1.984009403826708, + -1.9888042278234839 + ), + a2 = doubleArrayOf( + 0.9795319509086966, + 0.9807151306083703, + 0.9842293382669933, + 0.9865943148708757, + 0.9940424264123358, + 0.9952290061875053 + ), + b0 = doubleArrayOf(1.2101032033538562e-12, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.4202064067077124e-12, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.2101032033538562e-12, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 629.9605249474366, + 707.1067811865476, + 561.2310241546866, + 630.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9710687181060893, + -1.9732083856639946, + -1.97481898635759, + -1.9798073097638182, + -1.984009403826708, + -1.9888042278234839 + ), + a2 = doubleArrayOf( + 0.9795319509086966, + 0.9807151306083703, + 0.9842293382669933, + 0.9865943148708757, + 0.9940424264123358, + 0.9952290061875053 + ), + b0 = doubleArrayOf(1.2101032033538562e-12, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.4202064067077124e-12, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.2101032033538562e-12, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 793.7005259840998, + 890.8987181403395, + 707.1067811865476, + 800.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9608874446844493, + -1.963879545064532, + -1.965272672649919, + -1.9723856664756494, + -1.97659489715021, + -1.9838025235597203 + ), + a2 = doubleArrayOf( + 0.9742810207967747, + 0.9757617669177364, + 0.9801733960432047, + 0.9831371116044616, + 0.9925011997680455, + 0.9939916952442285 + ), + b0 = doubleArrayOf(4.790763704092158e-12, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.581527408184316e-12, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.790763704092158e-12, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 793.7005259840998, + 890.8987181403395, + 707.1067811865476, + 800.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9608874446844493, + -1.963879545064532, + -1.965272672649919, + -1.9723856664756494, + -1.97659489715021, + -1.9838025235597203 + ), + a2 = doubleArrayOf( + 0.9742810207967747, + 0.9757617669177364, + 0.9801733960432047, + 0.9831371116044616, + 0.9925011997680455, + 0.9939916952442285 + ), + b0 = doubleArrayOf(4.790763704092158e-12, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.581527408184316e-12, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.790763704092158e-12, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 1000.0, + 1122.4620483093731, + 890.8987181403393, + 1000.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9465287395721516, + -1.9507624588529555, + -1.9515135594143518, + -1.9617755601288898, + -1.9653587645094899, + -1.9762804069485385 + ), + a2 = doubleArrayOf( + 0.9677055193767125, + 0.9695545234083117, + 0.9750893095099044, + 0.9787962486896891, + 0.9905642285037186, + 0.9924340097579212 + ), + b0 = doubleArrayOf(1.8916509736126712e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.7833019472253423e-11, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.8916509736126712e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 1000.0, + 1122.4620483093731, + 890.8987181403393, + 1000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9465287395721516, + -1.9507624588529555, + -1.9515135594143518, + -1.9617755601288898, + -1.9653587645094899, + -1.9762804069485385 + ), + a2 = doubleArrayOf( + 0.9677055193767125, + 0.9695545234083117, + 0.9750893095099044, + 0.9787962486896891, + 0.9905642285037186, + 0.9924340097579212 + ), + b0 = doubleArrayOf(1.8916509736126712e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.7833019472253423e-11, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.8916509736126712e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 1259.9210498948732, + 1414.213562373095, + 1122.4620483093731, + 1250.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.9260424262170373, + -1.9320996286683723, + -1.9314647877308484, + -1.9464276256666304, + -1.9482192536879337, + -1.9648762820351429 + ), + a2 = doubleArrayOf( + 0.959484453052192, + 0.9617861010981933, + 0.9687258551697253, + 0.9733497927864203, + 0.9881320776548521, + 0.9904730011558084 + ), + b0 = doubleArrayOf(7.444638326450626e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.4889276652901253e-10, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.444638326450626e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 1259.9210498948732, + 1414.213562373095, + 1122.4620483093731, + 1250.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9260424262170373, + -1.9320996286683723, + -1.9314647877308484, + -1.9464276256666304, + -1.9482192536879337, + -1.9648762820351429 + ), + a2 = doubleArrayOf( + 0.959484453052192, + 0.9617861010981933, + 0.9687258551697253, + 0.9733497927864203, + 0.9881320776548521, + 0.9904730011558084 + ), + b0 = doubleArrayOf(7.444638326450626e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.4889276652901253e-10, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.444638326450626e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 1587.4010519681995, + 1781.7974362806785, + 1414.2135623730949, + 1600.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.8965027818651838, + -1.9052541730305297, + -1.9019737635216158, + -1.9239887853351916, + -1.9219458962629674, + -1.9474731148985207 + ), + a2 = doubleArrayOf( + 0.9492267296230263, + 0.95207917414863, + 0.9607765846022719, + 0.9665216737012422, + 0.9850820532134703, + 0.9880039518087411 + ), + b0 = doubleArrayOf(2.917804238031064e-10, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.835608476062128e-10, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.917804238031064e-10, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 1587.4010519681995, + 1781.7974362806785, + 1414.2135623730949, + 1600.0, + sos = Sos( + a1 = doubleArrayOf( + -1.8965027818651838, + -1.9052541730305297, + -1.9019737635216158, + -1.9239887853351916, + -1.9219458962629674, + -1.9474731148985207 + ), + a2 = doubleArrayOf( + 0.9492267296230263, + 0.95207917414863, + 0.9607765846022719, + 0.9665216737012422, + 0.9850820532134703, + 0.9880039518087411 + ), + b0 = doubleArrayOf(2.917804238031064e-10, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.835608476062128e-10, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.917804238031064e-10, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 2000.0, + 2244.9240966187463, + 1781.7974362806785, + 2000.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.853524646230276, + -1.8662693215227235, + -1.8582666776011347, + -1.8908847111532185, + -1.881546589134322, + -1.9207843344407987 + ), + a2 = doubleArrayOf( + 0.9364604437606376, + 0.9399729439539108, + 0.9508720272624499, + 0.9579687210223301, + 0.9812642472067368, + 0.9848941893771724 + ), + b0 = doubleArrayOf(1.1377293181749668e-09, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.2754586363499337e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.1377293181749668e-09, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 2000.0, + 2244.9240966187463, + 1781.7974362806785, + 2000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.853524646230276, + -1.8662693215227235, + -1.8582666776011347, + -1.8908847111532185, + -1.881546589134322, + -1.9207843344407987 + ), + a2 = doubleArrayOf( + 0.9364604437606376, + 0.9399729439539108, + 0.9508720272624499, + 0.9579687210223301, + 0.9812642472067368, + 0.9848941893771724 + ), + b0 = doubleArrayOf(1.1377293181749668e-09, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.2754586363499337e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.1377293181749668e-09, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 2519.8420997897465, + 2828.42712474619, + 2244.9240966187463, + 2500.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.7905716270112106, + -1.809232149768823, + -1.7931653070496998, + -1.8417048438803578, + -1.8193689745652162, + -1.8797292043052978 + ), + a2 = doubleArrayOf( + 0.9206234986422084, + 0.9249080336194182, + 0.9385743411553938, + 0.9472640245029827, + 0.9764983624230299, + 0.9809745604874052 + ), + b0 = doubleArrayOf(4.408111664350121e-09, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(8.816223328700242e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.408111664350121e-09, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 2519.8420997897465, + 2828.42712474619, + 2244.9240966187463, + 2500.0, + sos = Sos( + a1 = doubleArrayOf( + -1.7905716270112106, + -1.809232149768823, + -1.7931653070496998, + -1.8417048438803578, + -1.8193689745652162, + -1.8797292043052978 + ), + a2 = doubleArrayOf( + 0.9206234986422084, + 0.9249080336194182, + 0.9385743411553938, + 0.9472640245029827, + 0.9764983624230299, + 0.9809745604874052 + ), + b0 = doubleArrayOf(4.408111664350121e-09, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(8.816223328700242e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.408111664350121e-09, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 3174.8021039363994, + 3563.594872561358, + 2828.42712474619, + 3150.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.698018049211772, + -1.7253955989854663, + -1.6960230821791877, + -1.768329526959257, + -1.7238475768878447, + -1.8165175010628238 + ), + a2 = doubleArrayOf( + 0.9010581254789203, + 0.9062094836371017, + 0.9233783287144209, + 0.9338745051748574, + 0.9705731974939211, + 0.9760270510168769 + ), + b0 = doubleArrayOf(1.6944944571208185e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.388988914241637e-08, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.6944944571208185e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 3174.8021039363994, + 3563.594872561358, + 2828.42712474619, + 3150.0, + sos = Sos( + a1 = doubleArrayOf( + -1.698018049211772, + -1.7253955989854663, + -1.6960230821791877, + -1.768329526959257, + -1.7238475768878447, + -1.8165175010628238 + ), + a2 = doubleArrayOf( + 0.9010581254789203, + 0.9062094836371017, + 0.9233783287144209, + 0.9338745051748574, + 0.9705731974939211, + 0.9760270510168769 + ), + b0 = doubleArrayOf(1.6944944571208185e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.388988914241637e-08, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.6944944571208185e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 4000.0, + 4489.8481932374925, + 3563.594872561357, + 4000.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.5620018562313807, + -1.6020680617209626, + -1.5514295488390417, + -1.6587733605130148, + -1.5779211014645125, + -1.7193763872935146 + ), + a2 = doubleArrayOf( + 0.8770137966311882, + 0.8830668098889431, + 0.9047265072461494, + 0.9171282371849951, + 0.9632528802297299, + 0.9697655086311445 + ), + b0 = doubleArrayOf(6.450735040335462e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.2901470080670925e-07, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(6.450735040335462e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 4000.0, + 4489.8481932374925, + 3563.594872561357, + 4000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.5620018562313807, + -1.6020680617209626, + -1.5514295488390417, + -1.6587733605130148, + -1.5779211014645125, + -1.7193763872935146 + ), + a2 = doubleArrayOf( + 0.8770137966311882, + 0.8830668098889431, + 0.9047265072461494, + 0.9171282371849951, + 0.9632528802297299, + 0.9697655086311445 + ), + b0 = doubleArrayOf(6.450735040335462e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.2901470080670925e-07, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(6.450735040335462e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 5039.684199579493, + 5656.85424949238, + 4489.8481932374925, + 5000.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.3633369519581289, + -1.4214643083634073, + -1.3380146172088572, + -1.495861751008069, + -1.3574610576475574, + -1.570954880985224 + ), + a2 = doubleArrayOf( + 0.8476665728706247, + 0.8545076730564747, + 0.8820537169725038, + 0.896161432848994, + 0.954298805785257, + 0.9618028595678236 + ), + b0 = doubleArrayOf(2.426697477611551e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(4.853394955223102e-07, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.426697477611551e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 5039.684199579493, + 5656.85424949238, + 4489.8481932374925, + 5000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.3633369519581289, + -1.4214643083634073, + -1.3380146172088572, + -1.495861751008069, + -1.3574610576475574, + -1.570954880985224 + ), + a2 = doubleArrayOf( + 0.8476665728706247, + 0.8545076730564747, + 0.8820537169725038, + 0.896161432848994, + 0.954298805785257, + 0.9618028595678236 + ), + b0 = doubleArrayOf(2.426697477611551e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(4.853394955223102e-07, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.426697477611551e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 6349.604207872798, + 7127.189745122714, + 5656.8542494923795, + 6300.0, + 0, + Sos( + a1 = doubleArrayOf( + -1.0773395486328345, + -1.1602031096288796, + -1.0283985417327675, + -1.256274714648876, + -1.0309072558826313, + -1.3467894695867646 + ), + a2 = doubleArrayOf( + 0.8121705498843197, + 0.8193547936396985, + 0.8548947631581487, + 0.869820439177322, + 0.9435278418933211, + 0.9515891151377003 + ), + b0 = doubleArrayOf(8.998148499223296e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.7996296998446593e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(8.998148499223296e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 6349.604207872798, + 7127.189745122714, + 5656.8542494923795, + 6300.0, + sos = Sos( + a1 = doubleArrayOf( + -1.0773395486328345, + -1.1602031096288796, + -1.0283985417327675, + -1.256274714648876, + -1.0309072558826313, + -1.3467894695867646 + ), + a2 = doubleArrayOf( + 0.8121705498843197, + 0.8193547936396985, + 0.8548947631581487, + 0.869820439177322, + 0.9435278418933211, + 0.9515891151377003 + ), + b0 = doubleArrayOf(8.998148499223296e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.7996296998446593e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(8.998148499223296e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 8000.0, + 8979.696386474985, + 7127.189745122714, + 8000.0, + 0, + Sos( + a1 = doubleArrayOf( + -0.6767266245218572, + -0.7912873195095531, + -0.5929092846277738, + -0.9115385133184579, + -0.563294468815372, + -1.0151634969766976 + ), + a2 = doubleArrayOf( + 0.7697779369631321, + 0.7761347143350338, + 0.8231381617779963, + 0.8364515353139432, + 0.9309588850819872, + 0.9382780377204392 + ), + b0 = doubleArrayOf(3.279097356293735e-06, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(6.55819471258747e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.279097356293735e-06, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 8000.0, + 8979.696386474985, + 7127.189745122714, + 8000.0, + sos = Sos( + a1 = doubleArrayOf( + -0.6767266245218572, + -0.7912873195095531, + -0.5929092846277738, + -0.9115385133184579, + -0.563294468815372, + -1.0151634969766976 + ), + a2 = doubleArrayOf( + 0.7697779369631321, + 0.7761347143350338, + 0.8231381617779963, + 0.8364515353139432, + 0.9309588850819872, + 0.9382780377204392 + ), + b0 = doubleArrayOf(3.279097356293735e-06, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(6.55819471258747e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.279097356293735e-06, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 10079.368399158986, + 11313.70849898476, + 8979.696386474985, + 10000.0, + 0, + Sos( + a1 = doubleArrayOf( + -0.14222192561841182, + -0.29277856105906586, + -0.012595844464301289, + -0.4348904602476619, + 0.06824248192606285, + -0.5419579256858204 + ), + a2 = doubleArrayOf( + 0.7201408449235422, + 0.7228312366826449, + 0.7876833682787011, + 0.7933613741944567, + 0.9172048817957851, + 0.9203880227993824 + ), + b0 = doubleArrayOf(1.170610624091675e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.34122124818335e-05, 2.0, -2.0, -2.0, 2.0, -2.0), + b2 = doubleArrayOf(1.170610624091675e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 10079.368399158986, + 11313.70849898476, + 8979.696386474985, + 10000.0, + sos = Sos( + a1 = doubleArrayOf( + -0.14222192561841182, + -0.29277856105906586, + -0.012595844464301289, + -0.4348904602476619, + 0.06824248192606285, + -0.5419579256858204 + ), + a2 = doubleArrayOf( + 0.7201408449235422, + 0.7228312366826449, + 0.7876833682787011, + 0.7933613741944567, + 0.9172048817957851, + 0.9203880227993824 + ), + b0 = doubleArrayOf(1.170610624091675e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.34122124818335e-05, 2.0, -2.0, -2.0, 2.0, -2.0), + b2 = doubleArrayOf(1.170610624091675e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + 0, + Sos( + a1 = doubleArrayOf( + 0.32848784493062516, + 0.5111660791529813, + 0.17733074339349109, + 0.6895165716585276, + 0.09114717463945445, + 0.8350589885757965 + ), + a2 = doubleArrayOf( + 0.656000255318543, + 0.6643131072250139, + 0.7350225079427642, + 0.7526237525351431, + 0.8946980509545702, + 0.9047450752107424 + ), + b0 = doubleArrayOf(4.079796937465318e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-8.159593874930636e-05, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(4.079796937465318e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + sos = Sos( + a1 = doubleArrayOf( + 0.32848784493062516, + 0.5111660791529813, + 0.17733074339349109, + 0.6895165716585276, + 0.09114717463945445, + 0.8350589885757965 + ), + a2 = doubleArrayOf( + 0.656000255318543, + 0.6643131072250139, + 0.7350225079427642, + 0.7526237525351431, + 0.8946980509545702, + 0.9047450752107424 + ), + b0 = doubleArrayOf(4.079796937465318e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-8.159593874930636e-05, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(4.079796937465318e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + 0, + Sos( + a1 = doubleArrayOf( + 0.982782646682872, + 1.1871163009075574, + 0.8498066536564547, + 1.3967580458961404, + 0.837324208994016, + 1.5838701207898134 + ), + a2 = doubleArrayOf( + 0.5651700023975551, + 0.6111614520796955, + 0.6434040840366426, + 0.7378609025916056, + 0.8507018518753098, + 0.9043557997025693 + ), + b0 = doubleArrayOf(0.00013834805505964022, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00027669611011928044, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(0.00013834805505964022, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + sos = Sos( + a1 = doubleArrayOf( + 0.982782646682872, + 1.1871163009075574, + 0.8498066536564547, + 1.3967580458961404, + 0.837324208994016, + 1.5838701207898134 + ), + a2 = doubleArrayOf( + 0.5651700023975551, + 0.6111614520796955, + 0.6434040840366426, + 0.7378609025916056, + 0.8507018518753098, + 0.9043557997025693 + ), + b0 = doubleArrayOf(0.00013834805505964022, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00027669611011928044, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(0.00013834805505964022, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ) + ), + antiAliasing = AntiAliasing( + a1 = doubleArrayOf( + -1.2446640940133593e-16, + -1.630640067418199e-16, + -1.387778780781446e-16, + -2.4980018054066027e-16, + -1.9428902930940244e-16, + -1.6653345369377353e-16, + -2.775557561562892e-16, + -1.1102230246251565e-16, + -2.220446049250313e-16, + -2.2204460492503136e-16 + ), + a2 = doubleArrayOf( + 0.001543712508674131, + 0.014008568735804144, + 0.03956612989658006, + 0.07954045177320418, + 0.13610158059098823, + 0.21252682194185937, + 0.31363013821503233, + 0.4464626921716894, + 0.6214743340943358, + 0.8544977810681018 + ), + b0 = doubleArrayOf(8.555155699386506e-06, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.7110311398773013e-05, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.555155699386506e-06, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0), + sampleRatio = 2 + ), + aWeighting = DigitalFilterConfiguration( + filterDenominator = doubleArrayOf( + 1.0, + -4.01958210258393, + 6.18944019937876, + -4.453264993081893, + 1.420898591796445, + -0.14184390392723248, + 0.004352219710019002 + ), + filterNumerator = doubleArrayOf( + 0.25574354103517516, + -0.5114870820703503, + -0.2557435410351751, + 1.0229741641407009, + -0.25574354103517505, + -0.5114870820703503, + 0.25574354103517516 + ) + ), + cWeighting = DigitalFilterConfiguration( + filterDenominator = doubleArrayOf( + 1.0, + -2.134692386318322, + 1.279369764820266, + -0.14957989013271009, + 0.004909935845510762 + ), + filterNumerator = doubleArrayOf( + 0.21700689624574065, + 0.0, + -0.4340137924914813, + 0.0, + 0.21700689624574065 + ) + ), + configuration = GeneralConfiguration(sampleRate = 44100) + ) +} + + +fun get48000HZ(): SpectrumChannelConfiguration { + return SpectrumChannelConfiguration( + bandpass = listOf( + Bandpass( + 49.60628287400625, + 55.681169883771226, + 44.19417382415923, + 50.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9984601797146522, + -1.998552690675498, + -1.998800724570471, + -1.9989885175100113, + -1.999514946443491, + -1.9996204400445106 + ), + a2 = doubleArrayOf( + 0.9985049317808966, + 0.9985923599926457, + 0.9988503801969798, + 0.9990242833273026, + 0.9995676495557392, + 0.9996541604577023 + ), + b0 = doubleArrayOf(1.8006511759597513e-19, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.6013023519195027e-19, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.8006511759597513e-19, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 793.7005259840998, + 890.8987181403395, + 707.1067811865476, + 800.0, + sos = Sos( + a1 = doubleArrayOf( + -1.965026429970155, + -1.967668597254471, + -1.9691783807266252, + -1.9754146534405037, + -1.979674406907778, + -1.9858749551591182 + ), + a2 = doubleArrayOf( + 0.9763456895906536, + 0.9777097806866464, + 0.9817686375136939, + 0.9844974374007367, + 0.9931078103210836, + 0.9944788918207288 + ), + b0 = doubleArrayOf(2.893016243936537e-12, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.786032487873074e-12, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.893016243936537e-12, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 62.5, + 70.15387801933582, + 55.681169883771204, + 63.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9980456727590987, + -1.9981638490205884, + -1.998472975728957, + -1.9987140616601067, + -1.9993716476778767, + -1.9995107633757623 + ), + a2 = doubleArrayOf( + 0.9981166982879324, + 0.998226808502417, + 0.9985517871612583, + 0.9987708290608652, + 0.9994553037380881, + 0.9995642887013393 + ), + b0 = doubleArrayOf(7.1971733989403265e-19, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.4394346797880653e-18, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.1971733989403265e-19, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 1000.0, + 1122.4620483093731, + 890.8987181403393, + 1000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9523863069067073, + -1.956108973076721, + -1.9571616552873032, + -1.9661212030625006, + -1.9700353114553955, + -1.979405001055496 + ), + a2 = doubleArrayOf( + 0.9702899031224652, + 0.9719947859444387, + 0.977088153176812, + 0.9805039267142269, + 0.9913264267319474, + 0.9930473480576114 + ), + b0 = doubleArrayOf(1.1435038769653862e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.2870077539307723e-11, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.1435038769653862e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 78.74506561842958, + 88.38834764831844, + 70.15387801933582, + 80.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9975150516721296, + -1.997666513582322, + -1.9980506301304577, + -1.9983614893563468, + -1.999180989630783, + -1.9993661073959705 + ), + a2 = doubleArrayOf( + 0.9976277697102935, + 0.9977664322112939, + 0.9981757114692286, + 0.998451587351376, + 0.9993137754070069, + 0.9994510685212221 + ), + b0 = doubleArrayOf(2.876135110287808e-18, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.752270220575616e-18, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.876135110287808e-18, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 1259.9210498948732, + 1414.213562373095, + 1122.4620483093731, + 1250.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9344269032418544, + -1.9397319953310073, + -1.9397192012263036, + -1.9527343232383667, + -1.955364536130128, + -1.969623329484771 + ), + a2 = doubleArrayOf( + 0.962713847798208, + 0.9648387911194759, + 0.9712264142580659, + 0.9754919104957473, + 0.9890888216359307, + 0.9912451584685964 + ), + b0 = doubleArrayOf(4.506127850247666e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.012255700495332e-11, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.506127850247666e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 99.21256574801247, + 111.36233976754241, + 88.38834764831843, + 100.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9968332267646434, + -1.9970281309710414, + -1.9975035831956809, + -1.997906519235211, + -1.9989247264019108, + -1.9991735803604036 + ), + a2 = doubleArrayOf( + 0.9970120994122506, + 0.9971866951423459, + 0.9977020891971378, + 0.9980495114976516, + 0.9991354905208201, + 0.9993084375601545 + ), + b0 = doubleArrayOf(1.1490779436069693e-17, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.2981558872139386e-17, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.1490779436069693e-17, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 1587.4010519681995, + 1781.7974362806785, + 1414.2135623730949, + 1600.0, + sos = Sos( + a1 = doubleArrayOf( + -1.9086271549871285, + -1.916265810798378, + -1.9141456550592422, + -1.9332356686147505, + -1.9329115625897635, + -1.954729191654851 + ), + a2 = doubleArrayOf( + 0.9532534031282983, + 0.9558916290464894, + 0.9638981867815446, + 0.9692065253993708, + 0.9862812992117321, + 0.9889762139111492 + ), + b0 = doubleArrayOf(1.7689636295331422e-10, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.5379272590662843e-10, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.7689636295331422e-10, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 125.0, + 140.30775603867164, + 111.36233976754241, + 125.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9959531145933485, + -1.996205137556247, + -1.99679067441179, + -1.997316227634658, + -1.9985763869742814, + -1.9989147095139637 + ), + a2 = doubleArrayOf( + 0.9962369446432664, + 0.9964567486353729, + 0.9971056859894822, + 0.9975431546301207, + 0.9989109131331084, + 0.9991287613275411 + ), + b0 = doubleArrayOf(4.5893885449978465e-17, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.178777089995693e-17, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.5893885449978465e-17, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 2000.0, + 2244.9240966187463, + 1781.7974362806785, + 2000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.871205384376715, + -1.8823000944590687, + -1.8763388947791837, + -1.904558436948833, + -1.8984178521059683, + -1.9319247221181628 + ), + a2 = doubleArrayOf( + 0.941467504228004, + 0.944724780930314, + 0.954757924553905, + 0.9613309174454501, + 0.9827643692222471, + 0.9861189380456511 + ), + b0 = doubleArrayOf(6.91156650726858e-10, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.382313301453716e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(6.91156650726858e-10, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 157.49013123685916, + 176.7766952966369, + 140.30775603867164, + 160.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9948108434480472, + -1.995138608941705, + -1.9958549254625118, + -1.99654543832831, + -1.9980970962795688, + -1.998562682476333 + ), + a2 = doubleArrayOf( + 0.9952611693790621, + 0.9955378281154749, + 0.9963547796390351, + 0.9969055435967796, + 0.9986280396391475, + 0.9989024265317586 + ), + b0 = doubleArrayOf(1.832273856726884e-16, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.664547713453768e-16, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.832273856726884e-16, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 2519.8420997897465, + 2828.42712474619, + 2244.9240966187463, + 2500.0, + sos = Sos( + a1 = doubleArrayOf( + -1.8165097119283033, + -1.8327278364989417, + -1.8201108586209922, + -1.8620520708389903, + -1.8453324823283772, + -1.8968763455852142 + ), + a2 = doubleArrayOf( + 0.9268280488124225, + 0.9308168891662456, + 0.9433931969114127, + 0.9514711240169829, + 0.9783691390918853, + 0.9825187465670534 + ), + b0 = doubleArrayOf(2.684574372673378e-09, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.369148745346756e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.684574372673378e-09, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 198.42513149602493, + 222.72467953508482, + 176.77669529663686, + 200.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9933187384467437, + -1.9937479129040492, + -1.9946164416668248, + -1.9955313782127055, + -1.9974291162822255, + -1.998078105503138 + ), + a2 = doubleArrayOf( + 0.9940331313329048, + 0.9943812547620593, + 0.9954095160792754, + 0.996102770070116, + 0.9982717642479774, + 0.9986173297121667 + ), + b0 = doubleArrayOf(7.31159711822096e-16, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.462319423644192e-15, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.31159711822096e-16, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 3174.8021039363994, + 3563.594872561358, + 2828.42712474619, + 3150.0, + sos = Sos( + a1 = doubleArrayOf( + -1.7361720647072763, + -1.7599595298520214, + -1.7362282609156778, + -1.7987101471164402, + -1.7636941439008764, + -1.8429155834449047 + ), + a2 = doubleArrayOf( + 0.9087125903565318, + 0.9135375076733302, + 0.9293227322337309, + 0.9391361195874267, + 0.9728955015176468, + 0.97797732690065 + ), + b0 = doubleArrayOf(1.0351518141960393e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.0703036283920787e-08, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.0351518141960393e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 250.0, + 280.6155120773433, + 222.72467953508482, + 250.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9913549482178523, + -1.9919213473960666, + -1.992961734561673, + -1.9941856866943861, + -1.9964858191009422, + -1.9974024456375816 + ), + a2 = doubleArrayOf( + 0.9924880597760736, + 0.9929259516314002, + 0.9942198634821799, + 0.9950922308001783, + 0.997823084005514, + 0.998258234393528 + ), + b0 = doubleArrayOf(2.915850405403221e-15, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.831700810806442e-15, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.915850405403221e-15, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 4000.0, + 4489.8481932374925, + 3563.594872561357, + 4000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.6180238146105717, + -1.6528894834371832, + -1.6111845676577818, + -1.7041154148090003, + -1.638653915827305, + -1.7599015767615853 + ), + a2 = doubleArrayOf( + 0.8864034769752915, + 0.8921284562827796, + 0.9120050759349341, + 0.9237096620549838, + 0.9661151955423802, + 0.9722364024371097 + ), + b0 = doubleArrayOf(3.955684226935954e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(7.911368453871908e-08, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.955684226935954e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 314.9802624737183, + 353.5533905932738, + 280.6155120773433, + 315.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9887479618777701, + -1.9895021967303281, + -1.9907275393254193, + -1.992382364609799, + -1.99513606793578, + -1.996447847380435 + ), + a2 = doubleArrayOf( + 0.9905448150503697, + 0.9910953670304777, + 0.9927230754790283, + 0.9938204340344118, + 0.997258104736648, + 0.9978059631311219 + ), + b0 = doubleArrayOf(1.1619301238020158e-14, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.3238602476040316e-14, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.1619301238020158e-14, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 5039.684199579493, + 5656.85424949238, + 4489.8481932374925, + 5000.0, + sos = Sos( + a1 = doubleArrayOf( + -1.444929044061489, + -1.4957170043735097, + -1.4258973038465004, + -1.563146402998311, + -1.4488230224705272, + -1.6327258020473276 + ), + a2 = doubleArrayOf( + 0.8590998950465403, + 0.8656805160974435, + 0.8908697742725937, + 0.9044065217137992, + 0.95778643697265, + 0.9649510549793255 + ), + b0 = doubleArrayOf(1.4950003260908323e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.9900006521816645e-07, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.4950003260908323e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 396.8502629920499, + 445.44935907016975, + 353.5533905932738, + 400.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.985253318813393, + -1.9862677916946907, + -1.9876761395004403, + -1.9899394676176885, + -1.993179826793365, + -1.9950812653258392 + ), + a2 = doubleArrayOf( + 0.9881019056669826, + 0.9887936803667451, + 0.9908405610202708, + 0.9922202715949308, + 0.9965468014759469, + 0.9972363820904895 + ), + b0 = doubleArrayOf(4.625615616925849e-14, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.251231233851698e-14, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.625615616925849e-14, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 6349.604207872798, + 7127.189745122714, + 5656.8542494923795, + 6300.0, + sos = Sos( + a1 = doubleArrayOf( + -1.1941149006127025, + -1.2670743205284096, + -1.1550415228967335, + -1.3547778680343292, + -1.1652346750852467, + -1.4396698084572528 + ), + a2 = doubleArrayOf( + 0.8259548961648948, + 0.8330985104737876, + 0.8653960229572148, + 0.8801955148623766, + 0.9476951530180628, + 0.9556413101528509 + ), + b0 = doubleArrayOf(5.574640991696916e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.1149281983393831e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(5.574640991696916e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 500.0, + 561.2310241546866, + 445.44935907016963, + 500.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9805183605881083, + -1.9818978551492608, + -1.9834575729707877, + -1.9865911374749832, + -1.9903100156946432, + -1.9930996785658013 + ), + a2 = doubleArrayOf( + 0.9850326275872681, + 0.985901148759386, + 0.9884740475498774, + 0.9902076356199037, + 0.9956514788832681, + 0.9965191245238226 + ), + b0 = doubleArrayOf(1.8391819354137517e-13, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.6783638708275035e-13, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.8391819354137517e-13, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 8000.0, + 8979.696386474985, + 7127.189745122714, + 8000.0, + sos = Sos( + a1 = doubleArrayOf( + -0.8385656218768355, + -0.9407668149562102, + -0.7689289266746798, + -1.0520645541182183, + -0.7531924297464213, + -1.1514952746822522 + ), + a2 = doubleArrayOf( + 0.786163458984544, + 0.7930340162566856, + 0.8352970884716027, + 0.8496435477623626, + 0.935759288200993, + 0.9435931378701163 + ), + b0 = doubleArrayOf(2.0452147968403255e-06, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(4.090429593680651e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.0452147968403255e-06, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 629.9605249474366, + 707.1067811865476, + 561.2310241546866, + 630.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9740286599079484, + -1.9759264207270635, + -1.9775515830152486, + -1.981944694593259, + -1.9860528263876591, + -1.9901913146712302 + ), + a2 = doubleArrayOf( + 0.981179187823968, + 0.9822684388078915, + 0.9855009207967405, + 0.9876772349651166, + 0.9945248701796774, + 0.9956159845310506 + ), + b0 = doubleArrayOf(7.301444354678124e-13, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.4602888709356248e-12, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.301444354678124e-13, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 10079.368399158986, + 11313.70849898476, + 8979.696386474985, + 10000.0, + sos = Sos( + a1 = doubleArrayOf( + -0.35405154763185304, + -0.49128251707257314, + -0.24223050428288867, + -0.6261645675731202, + -0.18240420331818913, + -0.7338155809134382 + ), + a2 = doubleArrayOf( + 0.7391734396217206, + 0.7437188888138072, + 0.8009751006663193, + 0.8105429251807174, + 0.9222982330633457, + 0.927623171295239 + ), + b0 = doubleArrayOf(7.359484224824578e-06, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.4718968449649156e-05, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.359484224824578e-06, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 793.7005259840998, + 890.8987181403395, + 707.1067811865476, + 800.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.965026429970155, + -1.967668597254471, + -1.9691783807266252, + -1.9754146534405037, + -1.979674406907778, + -1.9858749551591182 + ), + a2 = doubleArrayOf( + 0.9763456895906536, + 0.9777097806866464, + 0.9817686375136939, + 0.9844974374007367, + 0.9931078103210836, + 0.9944788918207288 + ), + b0 = doubleArrayOf(2.893016243936537e-12, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.786032487873074e-12, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.893016243936537e-12, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + sos = Sos( + a1 = doubleArrayOf( + 0.08931454551996315, + 0.26142519691480276, + -0.06094975521093459, + 0.4225931350065996, + -0.15897989987756816, + 0.5440792188366831 + ), + a2 = doubleArrayOf( + 0.6824341186617071, + 0.6852999529629943, + 0.7588006314601975, + 0.7648681667234911, + 0.9053884151557717, + 0.9088313442453133 + ), + b0 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-5.177306444963838e-05, 2.0, -2.0, 2.0, -2.0, 2.0), + b2 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 1000.0, + 1122.4620483093731, + 890.8987181403393, + 1000.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9523863069067073, + -1.956108973076721, + -1.9571616552873032, + -1.9661212030625006, + -1.9700353114553955, + -1.979405001055496 + ), + a2 = doubleArrayOf( + 0.9702899031224652, + 0.9719947859444387, + 0.977088153176812, + 0.9805039267142269, + 0.9913264267319474, + 0.9930473480576114 + ), + b0 = doubleArrayOf(1.1435038769653862e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.2870077539307723e-11, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.1435038769653862e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + sos = Sos( + a1 = doubleArrayOf( + 0.7511127884288892, + 0.9474349259843516, + 0.6077435414237824, + 1.1492032880591414, + 0.5590818870997089, + 1.3287692023317226 + ), + a2 = doubleArrayOf( + 0.6029568566338351, + 0.6285948136865629, + 0.6836472041890249, + 0.7374497853902116, + 0.8705313801288671, + 0.9013601600372116 + ), + b0 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00017740280306388184, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 1259.9210498948732, + 1414.213562373095, + 1122.4620483093731, + 1250.0, + 4, + Sos( + a1 = doubleArrayOf( + -1.9344269032418544, + -1.9397319953310073, + -1.9397192012263036, + -1.9527343232383667, + -1.955364536130128, + -1.969623329484771 + ), + a2 = doubleArrayOf( + 0.962713847798208, + 0.9648387911194759, + 0.9712264142580659, + 0.9754919104957473, + 0.9890888216359307, + 0.9912451584685964 + ), + b0 = doubleArrayOf(4.506127850247666e-11, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(9.012255700495332e-11, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(4.506127850247666e-11, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 20158.73679831797, + 22627.416997969518, + 17959.392772949966, + 20000.0, + sos = Sos( + a1 = doubleArrayOf( + 1.2413737222383092, + 1.1545812920689111, + 1.5913330473935272, + 1.2692295742308106, + 1.7813978059062259, + 1.9088059626392415 + ), + a2 = doubleArrayOf( + 0.4389237157990629, + 0.5162530772033582, + 0.6535489122217201, + 0.7880153266268755, + 0.8189707787110215, + 0.9408658628045891 + ), + b0 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.0005903384891348293, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 1587.4010519681995, + 1781.7974362806785, + 1414.2135623730949, + 1600.0, + 3, + Sos( + a1 = doubleArrayOf( + -1.9086271549871285, + -1.916265810798378, + -1.9141456550592422, + -1.9332356686147505, + -1.9329115625897635, + -1.954729191654851 + ), + a2 = doubleArrayOf( + 0.9532534031282983, + 0.9558916290464894, + 0.9638981867815446, + 0.9692065253993708, + 0.9862812992117321, + 0.9889762139111492 + ), + b0 = doubleArrayOf(1.7689636295331422e-10, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(3.5379272590662843e-10, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.7689636295331422e-10, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + sos = Sos( + a1 = doubleArrayOf( + 0.08931454551996315, + 0.26142519691480276, + -0.06094975521093459, + 0.4225931350065996, + -0.15897989987756816, + 0.5440792188366831 + ), + a2 = doubleArrayOf( + 0.6824341186617071, + 0.6852999529629943, + 0.7588006314601975, + 0.7648681667234911, + 0.9053884151557717, + 0.9088313442453133 + ), + b0 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-5.177306444963838e-05, 2.0, -2.0, 2.0, -2.0, 2.0), + b2 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 2000.0, + 2244.9240966187463, + 1781.7974362806785, + 2000.0, + 3, + Sos( + a1 = doubleArrayOf( + -1.871205384376715, + -1.8823000944590687, + -1.8763388947791837, + -1.904558436948833, + -1.8984178521059683, + -1.9319247221181628 + ), + a2 = doubleArrayOf( + 0.941467504228004, + 0.944724780930314, + 0.954757924553905, + 0.9613309174454501, + 0.9827643692222471, + 0.9861189380456511 + ), + b0 = doubleArrayOf(6.91156650726858e-10, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.382313301453716e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(6.91156650726858e-10, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + sos = Sos( + a1 = doubleArrayOf( + 0.7511127884288892, + 0.9474349259843516, + 0.6077435414237824, + 1.1492032880591414, + 0.5590818870997089, + 1.3287692023317226 + ), + a2 = doubleArrayOf( + 0.6029568566338351, + 0.6285948136865629, + 0.6836472041890249, + 0.7374497853902116, + 0.8705313801288671, + 0.9013601600372116 + ), + b0 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00017740280306388184, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 2519.8420997897465, + 2828.42712474619, + 2244.9240966187463, + 2500.0, + 3, + Sos( + a1 = doubleArrayOf( + -1.8165097119283033, + -1.8327278364989417, + -1.8201108586209922, + -1.8620520708389903, + -1.8453324823283772, + -1.8968763455852142 + ), + a2 = doubleArrayOf( + 0.9268280488124225, + 0.9308168891662456, + 0.9433931969114127, + 0.9514711240169829, + 0.9783691390918853, + 0.9825187465670534 + ), + b0 = doubleArrayOf(2.684574372673378e-09, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(5.369148745346756e-09, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.684574372673378e-09, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 20158.73679831797, + 22627.416997969518, + 17959.392772949966, + 20000.0, + sos = Sos( + a1 = doubleArrayOf( + 1.2413737222383092, + 1.1545812920689111, + 1.5913330473935272, + 1.2692295742308106, + 1.7813978059062259, + 1.9088059626392415 + ), + a2 = doubleArrayOf( + 0.4389237157990629, + 0.5162530772033582, + 0.6535489122217201, + 0.7880153266268755, + 0.8189707787110215, + 0.9408658628045891 + ), + b0 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.0005903384891348293, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 3174.8021039363994, + 3563.594872561358, + 2828.42712474619, + 3150.0, + 2, + Sos( + a1 = doubleArrayOf( + -1.7361720647072763, + -1.7599595298520214, + -1.7362282609156778, + -1.7987101471164402, + -1.7636941439008764, + -1.8429155834449047 + ), + a2 = doubleArrayOf( + 0.9087125903565318, + 0.9135375076733302, + 0.9293227322337309, + 0.9391361195874267, + 0.9728955015176468, + 0.97797732690065 + ), + b0 = doubleArrayOf(1.0351518141960393e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.0703036283920787e-08, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.0351518141960393e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + sos = Sos( + a1 = doubleArrayOf( + 0.08931454551996315, + 0.26142519691480276, + -0.06094975521093459, + 0.4225931350065996, + -0.15897989987756816, + 0.5440792188366831 + ), + a2 = doubleArrayOf( + 0.6824341186617071, + 0.6852999529629943, + 0.7588006314601975, + 0.7648681667234911, + 0.9053884151557717, + 0.9088313442453133 + ), + b0 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-5.177306444963838e-05, 2.0, -2.0, 2.0, -2.0, 2.0), + b2 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 4000.0, + 4489.8481932374925, + 3563.594872561357, + 4000.0, + 2, + Sos( + a1 = doubleArrayOf( + -1.6180238146105717, + -1.6528894834371832, + -1.6111845676577818, + -1.7041154148090003, + -1.638653915827305, + -1.7599015767615853 + ), + a2 = doubleArrayOf( + 0.8864034769752915, + 0.8921284562827796, + 0.9120050759349341, + 0.9237096620549838, + 0.9661151955423802, + 0.9722364024371097 + ), + b0 = doubleArrayOf(3.955684226935954e-08, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(7.911368453871908e-08, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(3.955684226935954e-08, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + sos = Sos( + a1 = doubleArrayOf( + 0.7511127884288892, + 0.9474349259843516, + 0.6077435414237824, + 1.1492032880591414, + 0.5590818870997089, + 1.3287692023317226 + ), + a2 = doubleArrayOf( + 0.6029568566338351, + 0.6285948136865629, + 0.6836472041890249, + 0.7374497853902116, + 0.8705313801288671, + 0.9013601600372116 + ), + b0 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00017740280306388184, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 5039.684199579493, + 5656.85424949238, + 4489.8481932374925, + 5000.0, + 2, + Sos( + a1 = doubleArrayOf( + -1.444929044061489, + -1.4957170043735097, + -1.4258973038465004, + -1.563146402998311, + -1.4488230224705272, + -1.6327258020473276 + ), + a2 = doubleArrayOf( + 0.8590998950465403, + 0.8656805160974435, + 0.8908697742725937, + 0.9044065217137992, + 0.95778643697265, + 0.9649510549793255 + ), + b0 = doubleArrayOf(1.4950003260908323e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(2.9900006521816645e-07, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(1.4950003260908323e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 20158.73679831797, + 22627.416997969518, + 17959.392772949966, + 20000.0, + sos = Sos( + a1 = doubleArrayOf( + 1.2413737222383092, + 1.1545812920689111, + 1.5913330473935272, + 1.2692295742308106, + 1.7813978059062259, + 1.9088059626392415 + ), + a2 = doubleArrayOf( + 0.4389237157990629, + 0.5162530772033582, + 0.6535489122217201, + 0.7880153266268755, + 0.8189707787110215, + 0.9408658628045891 + ), + b0 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.0005903384891348293, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 6349.604207872798, + 7127.189745122714, + 5656.8542494923795, + 6300.0, + 1, + Sos( + a1 = doubleArrayOf( + -1.1941149006127025, + -1.2670743205284096, + -1.1550415228967335, + -1.3547778680343292, + -1.1652346750852467, + -1.4396698084572528 + ), + a2 = doubleArrayOf( + 0.8259548961648948, + 0.8330985104737876, + 0.8653960229572148, + 0.8801955148623766, + 0.9476951530180628, + 0.9556413101528509 + ), + b0 = doubleArrayOf(5.574640991696916e-07, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.1149281983393831e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(5.574640991696916e-07, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + sos = Sos( + a1 = doubleArrayOf( + 0.08931454551996315, + 0.26142519691480276, + -0.06094975521093459, + 0.4225931350065996, + -0.15897989987756816, + 0.5440792188366831 + ), + a2 = doubleArrayOf( + 0.6824341186617071, + 0.6852999529629943, + 0.7588006314601975, + 0.7648681667234911, + 0.9053884151557717, + 0.9088313442453133 + ), + b0 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-5.177306444963838e-05, 2.0, -2.0, 2.0, -2.0, 2.0), + b2 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 8000.0, + 8979.696386474985, + 7127.189745122714, + 8000.0, + 1, + Sos( + a1 = doubleArrayOf( + -0.8385656218768355, + -0.9407668149562102, + -0.7689289266746798, + -1.0520645541182183, + -0.7531924297464213, + -1.1514952746822522 + ), + a2 = doubleArrayOf( + 0.786163458984544, + 0.7930340162566856, + 0.8352970884716027, + 0.8496435477623626, + 0.935759288200993, + 0.9435931378701163 + ), + b0 = doubleArrayOf(2.0452147968403255e-06, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(4.090429593680651e-06, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(2.0452147968403255e-06, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + sos = Sos( + a1 = doubleArrayOf( + 0.7511127884288892, + 0.9474349259843516, + 0.6077435414237824, + 1.1492032880591414, + 0.5590818870997089, + 1.3287692023317226 + ), + a2 = doubleArrayOf( + 0.6029568566338351, + 0.6285948136865629, + 0.6836472041890249, + 0.7374497853902116, + 0.8705313801288671, + 0.9013601600372116 + ), + b0 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00017740280306388184, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 10079.368399158986, + 11313.70849898476, + 8979.696386474985, + 10000.0, + 1, + Sos( + a1 = doubleArrayOf( + -0.35405154763185304, + -0.49128251707257314, + -0.24223050428288867, + -0.6261645675731202, + -0.18240420331818913, + -0.7338155809134382 + ), + a2 = doubleArrayOf( + 0.7391734396217206, + 0.7437188888138072, + 0.8009751006663193, + 0.8105429251807174, + 0.9222982330633457, + 0.927623171295239 + ), + b0 = doubleArrayOf(7.359484224824578e-06, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.4718968449649156e-05, 2.0, 2.0, -2.0, -2.0, -2.0), + b2 = doubleArrayOf(7.359484224824578e-06, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 20158.73679831797, + 22627.416997969518, + 17959.392772949966, + 20000.0, + sos = Sos( + a1 = doubleArrayOf( + 1.2413737222383092, + 1.1545812920689111, + 1.5913330473935272, + 1.2692295742308106, + 1.7813978059062259, + 1.9088059626392415 + ), + a2 = doubleArrayOf( + 0.4389237157990629, + 0.5162530772033582, + 0.6535489122217201, + 0.7880153266268755, + 0.8189707787110215, + 0.9408658628045891 + ), + b0 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.0005903384891348293, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(0.0002951692445674146, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + 0, + Sos( + a1 = doubleArrayOf( + 0.08931454551996315, + 0.26142519691480276, + -0.06094975521093459, + 0.4225931350065996, + -0.15897989987756816, + 0.5440792188366831 + ), + a2 = doubleArrayOf( + 0.6824341186617071, + 0.6852999529629943, + 0.7588006314601975, + 0.7648681667234911, + 0.9053884151557717, + 0.9088313442453133 + ), + b0 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-5.177306444963838e-05, 2.0, -2.0, 2.0, -2.0, 2.0), + b2 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 12699.208415745596, + 14254.379490245428, + 11313.708498984759, + 12500.0, + sos = Sos( + a1 = doubleArrayOf( + 0.08931454551996315, + 0.26142519691480276, + -0.06094975521093459, + 0.4225931350065996, + -0.15897989987756816, + 0.5440792188366831 + ), + a2 = doubleArrayOf( + 0.6824341186617071, + 0.6852999529629943, + 0.7588006314601975, + 0.7648681667234911, + 0.9053884151557717, + 0.9088313442453133 + ), + b0 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-5.177306444963838e-05, 2.0, -2.0, 2.0, -2.0, 2.0), + b2 = doubleArrayOf(2.588653222481919e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ), + Bandpass( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + 0, + Sos( + a1 = doubleArrayOf( + 0.7511127884288892, + 0.9474349259843516, + 0.6077435414237824, + 1.1492032880591414, + 0.5590818870997089, + 1.3287692023317226 + ), + a2 = doubleArrayOf( + 0.6029568566338351, + 0.6285948136865629, + 0.6836472041890249, + 0.7374497853902116, + 0.8705313801288671, + 0.9013601600372116 + ), + b0 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00017740280306388184, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ), + subsamplingFilter = SubsamplingFilter( + 16000.0, + 17959.39277294997, + 14254.379490245428, + 16000.0, + sos = Sos( + a1 = doubleArrayOf( + 0.7511127884288892, + 0.9474349259843516, + 0.6077435414237824, + 1.1492032880591414, + 0.5590818870997089, + 1.3287692023317226 + ), + a2 = doubleArrayOf( + 0.6029568566338351, + 0.6285948136865629, + 0.6836472041890249, + 0.7374497853902116, + 0.8705313801288671, + 0.9013601600372116 + ), + b0 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(-0.00017740280306388184, -2.0, -2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.870140153194092e-05, 1.0, 1.0, 1.0, 1.0, 1.0) + ) + ) + ) + ), + antiAliasing = AntiAliasing( + a1 = doubleArrayOf( + -1.2446640940133593e-16, + -1.630640067418199e-16, + -1.387778780781446e-16, + -2.4980018054066027e-16, + -1.9428902930940244e-16, + -1.6653345369377353e-16, + -2.775557561562892e-16, + -1.1102230246251565e-16, + -2.220446049250313e-16, + -2.2204460492503136e-16 + ), + a2 = doubleArrayOf( + 0.001543712508674131, + 0.014008568735804144, + 0.03956612989658006, + 0.07954045177320418, + 0.13610158059098823, + 0.21252682194185937, + 0.31363013821503233, + 0.4464626921716894, + 0.6214743340943358, + 0.8544977810681018 + ), + b0 = doubleArrayOf(8.555155699386506e-06, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0), + b1 = doubleArrayOf(1.7110311398773013e-05, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0), + b2 = doubleArrayOf(8.555155699386506e-06, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0), + sampleRatio = 2 + ), + aWeighting = DigitalFilterConfiguration( + filterDenominator = doubleArrayOf( + 1.0, + -4.11305010732996, + 6.553157702958688, + -4.990918046664834, + 1.7857953826173913, + -0.24621080268676002, + 0.011225878476736176 + ), + filterNumerator = doubleArrayOf( + 0.23430394285372055, + -0.4686078857074411, + -0.23430394285372066, + 0.937215771414882, + -0.2343039428537206, + -0.4686078857074411, + 0.23430394285372055 + ) + ), + cWeighting = DigitalFilterConfiguration( + filterDenominator = doubleArrayOf( + 1.0, + -2.219190223295007, + 1.4551725795223749, + -0.24851742382307854, + 0.012540782192183809 + ), + filterNumerator = doubleArrayOf( + 0.1978854554445731, + 0.0, + -0.3957709108891462, + 0.0, + 0.1978854554445731 + ) + ), + configuration = GeneralConfiguration(sampleRate = 48000) + ) +} diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/WindowAnalysis.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/WindowAnalysis.kt similarity index 64% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/WindowAnalysis.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/WindowAnalysis.kt index e893621..5e4da38 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/signal/WindowAnalysis.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/signal/WindowAnalysis.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture.shared.signal +package org.noiseplanet.noisecapture.shared.signal import kotlin.math.PI import kotlin.math.ceil @@ -16,52 +16,67 @@ import kotlin.math.sqrt * @windowSize Size of the window * @windowHop Run a new analysis each windowHop samples */ -class WindowAnalysis(val sampleRate : Int, val windowSize : Int, val windowHop : Int, private val applyHannWindow : Boolean = true) { +class WindowAnalysis( + val sampleRate: Int, + val windowSize: Int, + val windowHop: Int, + private val applyHannWindow: Boolean = true, +) { + val circularSamplesBuffer = FloatArray(windowSize) var circularBufferCursor = 0 var samplesUntilWindow = windowSize val hannWindow: FloatArray? = when (applyHannWindow) { - true -> - FloatArray(windowSize) { - (0.5 * (1 - cos(2 * PI * it / (windowSize - 1)))).toFloat() - } + true -> FloatArray(windowSize) { + (0.5 * (1 - cos(2 * PI * it / (windowSize - 1)))).toFloat() + } + else -> null } - //Windowing correction factors + + // Windowing correction factors // [1] F. J. Harris, “On the use of windows for harmonic analysis with the discrete fourier // transform,”Proceedings of the IEEE, vol. 66, no. 1, pp. 51–83, Jan. 1978. val windowCorrectionFactor = when (applyHannWindow) { true -> 0.375 else -> 1.0 } + init { - if(windowHop <= 0) { - throw IllegalArgumentException("Window hop must be superior than 0") + require(windowHop > 0) { + "Window hop must be superior than 0" } } /** * Process the provided samples and run a STFFT analysis when a window is complete */ - fun pushSamples(epoch: Long, samples: FloatArray, processedWindows: MutableList? = null) - : Sequence = sequence { + fun pushSamples( + epoch: Long, + samples: FloatArray, + processedWindows: MutableList? = null, + ): Sequence = sequence { var processed = 0 - while(processed < samples.size) { + while (processed < samples.size) { var toFetch = min(samples.size - processed, samplesUntilWindow) // fill the circular buffer - while(toFetch > 0) { + while (toFetch > 0) { val copySize = min(circularSamplesBuffer.size - circularBufferCursor, toFetch) - samples.copyInto(circularSamplesBuffer, circularBufferCursor, processed, - processed + copySize) + samples.copyInto( + circularSamplesBuffer, + circularBufferCursor, + processed, + processed + copySize + ) circularBufferCursor += copySize processed += copySize toFetch -= copySize samplesUntilWindow -= copySize - if(circularBufferCursor == circularSamplesBuffer.size) { + if (circularBufferCursor == circularSamplesBuffer.size) { circularBufferCursor = 0 } } - if(samplesUntilWindow == 0) { + if (samplesUntilWindow == 0) { // window complete push it val windowSamples = FloatArray(windowSize) circularSamplesBuffer.copyInto( @@ -77,7 +92,7 @@ class WindowAnalysis(val sampleRate : Int, val windowSize : Int, val windowHop : circularSamplesBuffer.size ) // apply window function - if(hannWindow != null) { + if (hannWindow != null) { for (i in windowSamples.indices) { windowSamples[i] *= hannWindow[i] } @@ -93,12 +108,12 @@ class WindowAnalysis(val sampleRate : Int, val windowSize : Int, val windowHop : } } - fun reconstructOriginalSignal(processedWindows : List) : FloatArray{ + fun reconstructOriginalSignal(processedWindows: List): FloatArray { val sum = FloatArray(processedWindows.size + processedWindows.size * windowHop) - for(i in processedWindows.indices) { - for(j in 0..< windowSize) { + for (i in processedWindows.indices) { + for (j in 0..Filling the FFT Input Buffer */ - private fun processWindow(window: Window) : SpectrumData{ + private fun processWindow(window: Window): SpectrumData { return SpectrumData(window.epoch, processWindowFloat(window), sampleRate) } - fun processWindowFloat(window: Window) : FloatArray { + fun processWindowFloat(window: Window): FloatArray { // resize array to power of two if window.samples.size is not a power of two val fftWindow: FloatArray = when (val fftWindowSize = nextPowerOfTwo(windowSize)) { windowSize -> window.samples @@ -122,33 +137,36 @@ class WindowAnalysis(val sampleRate : Int, val windowSize : Int, val windowHop : // place 0 padding at the center of the window window.samples.copyInto(paddedWindow, 0, windowSize / 2, windowSize) window.samples.copyInto( - paddedWindow, fftWindowSize - (windowSize / 2), - 0, windowSize / 2 + paddedWindow, + fftWindowSize - (windowSize / 2), + 0, + windowSize / 2 ) paddedWindow } } val fr = realFFTFloat(fftWindow) - val vRef = (((windowSize*windowSize)/2.0)*windowCorrectionFactor).toFloat() - return FloatArray(fr.size / 2) { i: Int -> 10 * log10((fr[(i*2)+1]*fr[(i*2)+1]) /vRef) } + val vRef = (((windowSize * windowSize) / 2.0) * windowCorrectionFactor).toFloat() + return FloatArray(fr.size / 2) { i: Int -> 10 * log10((fr[(i * 2) + 1] * fr[(i * 2) + 1]) / vRef) } } - fun processWindowDouble(window: Window) : DoubleArray { + fun processWindowDouble(window: Window): DoubleArray { val fftWindowSize = nextPowerOfTwo(windowSize) val fftWindow = DoubleArray(fftWindowSize) - val startIndex = windowSize/2 - for(i in startIndex..< windowSize) { - fftWindow[i-startIndex] = window.samples[i].toDouble() + val startIndex = windowSize / 2 + for (i in startIndex.. { + private fun getBands( + bandIndex: Int, + g: Double, + bandDivision: Double, + ): Triple { val fMid = g.pow(bandIndex / bandDivision) * 1000.0 val fMax = g.pow(1.0 / (2.0 * bandDivision)) * fMid val fMin = g.pow(-1.0 / (2.0 * bandDivision)) * fMid @@ -202,7 +232,7 @@ data class SpectrumData(val epoch : Long, val spectrum : FloatArray, val sampleR private fun getBandIndexByFrequency( targetFrequency: Double, g: Double, - bandDivision: Double + bandDivision: Double, ): Int { var frequencyBandIndex = 0 var (fMin, fMid, fMax) = getBands(frequencyBandIndex, g, bandDivision) @@ -222,16 +252,19 @@ data class SpectrumData(val epoch : Long, val spectrum : FloatArray, val sampleR /** * Create (third-)octave array from the specified parameters (without spl values) */ - fun emptyFrequencyBands(firstFrequencyBand : Double, - lastFrequencyBand : Double, base : BASE_METHOD = BASE_METHOD.B10, - bandDivision : Double = 3.0) : Array { + fun emptyFrequencyBands( + firstFrequencyBand: Double, + lastFrequencyBand: Double, + base: BaseMethod = BaseMethod.B10, + bandDivision: Double = 3.0, + ): Array { val g = when (base) { - BASE_METHOD.B10 -> 10.0.pow(3.0 / 10.0) - BASE_METHOD.B2 -> 2.0 + BaseMethod.B10 -> 10.0.pow(3.0 / 10.0) + BaseMethod.B2 -> 2.0 } val firstBandIndex = getBandIndexByFrequency(firstFrequencyBand, g, bandDivision) val lastBandIndex = getBandIndexByFrequency(lastFrequencyBand, g, bandDivision) - return Array(lastBandIndex - firstBandIndex) {bandIndex -> + return Array(lastBandIndex - firstBandIndex) { bandIndex -> val (fMin, fMid, fMax) = getBands(bandIndex + firstBandIndex, g, bandDivision) FrequencyBand(fMin, fMid, fMax, 0.0) } @@ -247,37 +280,42 @@ data class SpectrumData(val epoch : Long, val spectrum : FloatArray, val sampleR * @base Octave base 10 or base 2 * @octaveWindow Rectangular association of frequency band or fractional close to done by a filter */ - fun thirdOctaveProcessing(firstFrequencyBand : Double, - lastFrequencyBand : Double, base : BASE_METHOD = BASE_METHOD.B10, - bandDivision : Double = 3.0, - octaveWindow: OCTAVE_WINDOW = OCTAVE_WINDOW.FRACTIONAL): Array { + @Suppress("NestedBlockDepth") + fun thirdOctaveProcessing( + firstFrequencyBand: Double, + lastFrequencyBand: Double, + base: BaseMethod = BaseMethod.B10, + bandDivision: Double = 3.0, + octaveWindow: OctaveWindow = OctaveWindow.FRACTIONAL, + ): Array { val freqByCell: Double = (spectrum.size.toDouble() * 2) / sampleRate - val thirdOctave = emptyFrequencyBands(firstFrequencyBand, lastFrequencyBand, base, bandDivision) - if(octaveWindow == OCTAVE_WINDOW.FRACTIONAL) { + val thirdOctave = + emptyFrequencyBands(firstFrequencyBand, lastFrequencyBand, base, bandDivision) + + if (octaveWindow == OctaveWindow.FRACTIONAL) { for (band in thirdOctave) { for (cellIndex in spectrum.indices) { val f = (cellIndex + 1) / freqByCell - val cellGain = sqrt( - 1.0 / (1.0 + ((f / band.midFrequency - band.midFrequency / f) - * 1.507 * bandDivision).pow(6)) - ) - val fg = 10.0.pow(spectrum[cellIndex]/10.0) * cellGain - if(fg.isFinite()) { + val division = + (f / band.midFrequency - band.midFrequency / f) * 1.507 * bandDivision + val cellGain = sqrt(1.0 / (1.0 + division.pow(6))) + val fg = 10.0.pow(spectrum[cellIndex] / 10.0) * cellGain + if (fg.isFinite()) { band.spl += fg } } } for (band in thirdOctave) { - band.spl = 10*log10(band.spl) + band.spl = 10 * log10(band.spl) } } else { for (band in thirdOctave) { - val minCell = max(0,floor(band.minFrequency * freqByCell).toInt()) + val minCell = max(0, floor(band.minFrequency * freqByCell).toInt()) val maxCell = min(spectrum.size, ceil(band.maxFrequency * freqByCell).toInt()) var rms = 0.0 - for(cellIndex in minCell.. = callbackFlow { - val observer = SinglePointPlatformLifeCycleObserver{event -> trySend(event) } + val observer = SinglePointPlatformLifeCycleObserver { event -> trySend(event) } addObserver(observer) awaitClose { removeObserver(observer) } } diff --git a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/ui/SpectrogramBitmap.kt b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/ui/SpectrogramBitmap.kt similarity index 50% rename from shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/ui/SpectrogramBitmap.kt rename to shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/ui/SpectrogramBitmap.kt index 9bc25fd..edfd0ca 100644 --- a/shared/src/commonMain/kotlin/org/noise_planet/noisecapture/shared/ui/SpectrogramBitmap.kt +++ b/shared/src/commonMain/kotlin/org/noiseplanet/noisecapture/shared/ui/SpectrogramBitmap.kt @@ -1,10 +1,10 @@ -package org.noise_planet.noisecapture.shared.ui +package org.noiseplanet.noisecapture.shared.ui import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.IntSize -import org.noise_planet.noisecapture.shared.FFT_SIZE -import org.noise_planet.noisecapture.shared.signal.SpectrumData +import org.noiseplanet.noisecapture.shared.FFT_SIZE +import org.noiseplanet.noisecapture.shared.signal.SpectrumData import kotlin.math.floor import kotlin.math.log10 import kotlin.math.max @@ -16,52 +16,163 @@ import kotlin.math.pow * Convert FFT result into spectrogram bitmap bytearray */ class SpectrogramBitmap { + companion object { + val bmpHeader = intArrayOf( // All values are little-endian - 0x42, 0x4D, // Signature 'BM' - 0xaa, 0x00, 0x00, 0x00, // Size: 170 bytes - 0x00, 0x00, // Unused - 0x00, 0x00, // Unused - 0x8a, 0x00, 0x00, 0x00, // Offset to image data - 0x7c, 0x00, 0x00, 0x00, // DIB header size (124 bytes) - 0x04, 0x00, 0x00, 0x00, // Width (4px) - 0x02, 0x00, 0x00, 0x00, // Height (2px) - 0x01, 0x00, // Planes (1) - 0x20, 0x00, // Bits per pixel (32) - 0x03, 0x00, 0x00, 0x00, // Format (bitfield = use bitfields | no compression) - 0x20, 0x00, 0x00, 0x00, // Image raw size (32 bytes) - 0x13, 0x0B, 0x00, 0x00, // Horizontal print resolution (2835 = 72dpi * 39.3701) - 0x13, 0x0B, 0x00, 0x00, // Vertical print resolution (2835 = 72dpi * 39.3701) - 0x00, 0x00, 0x00, 0x00, // Colors in palette (none) - 0x00, 0x00, 0x00, 0x00, // Important colors (0 = all) - 0x00, 0x00, 0xFF, 0x00, // R bitmask (00FF0000) - 0x00, 0xFF, 0x00, 0x00, // G bitmask (0000FF00) - 0xFF, 0x00, 0x00, 0x00, // B bitmask (000000FF) - 0x00, 0x00, 0x00, 0xFF, // A bitmask (FF000000) - 0x42, 0x47, 0x52, 0x73, // sRGB color space - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Unused R, G, B entries for color space - 0x00, 0x00, 0x00, 0x00, // Unused Gamma X entry for color space - 0x00, 0x00, 0x00, 0x00, // Unused Gamma Y entry for color space - 0x00, 0x00, 0x00, 0x00, // Unused Gamma Z entry for color space - 0x00, 0x00, 0x00, 0x00, // Unknown - 0x00, 0x00, 0x00, 0x00, // Unknown - 0x00, 0x00, 0x00, 0x00, // Unknown - 0x00, 0x00, 0x00, 0x00 // Unknown + 0x42, + 0x4D, // Signature 'BM' + 0xaa, + 0x00, + 0x00, + 0x00, // Size: 170 bytes + 0x00, + 0x00, // Unused + 0x00, + 0x00, // Unused + 0x8a, + 0x00, + 0x00, + 0x00, // Offset to image data + 0x7c, + 0x00, + 0x00, + 0x00, // DIB header size (124 bytes) + 0x04, + 0x00, + 0x00, + 0x00, // Width (4px) + 0x02, + 0x00, + 0x00, + 0x00, // Height (2px) + 0x01, + 0x00, // Planes (1) + 0x20, + 0x00, // Bits per pixel (32) + 0x03, + 0x00, + 0x00, + 0x00, // Format (bitfield = use bitfields | no compression) + 0x20, + 0x00, + 0x00, + 0x00, // Image raw size (32 bytes) + 0x13, + 0x0B, + 0x00, + 0x00, // Horizontal print resolution (2835 = 72dpi * 39.3701) + 0x13, + 0x0B, + 0x00, + 0x00, // Vertical print resolution (2835 = 72dpi * 39.3701) + 0x00, + 0x00, + 0x00, + 0x00, // Colors in palette (none) + 0x00, + 0x00, + 0x00, + 0x00, // Important colors (0 = all) + 0x00, + 0x00, + 0xFF, + 0x00, // R bitmask (00FF0000) + 0x00, + 0xFF, + 0x00, + 0x00, // G bitmask (0000FF00) + 0xFF, + 0x00, + 0x00, + 0x00, // B bitmask (000000FF) + 0x00, + 0x00, + 0x00, + 0xFF, // A bitmask (FF000000) + 0x42, + 0x47, + 0x52, + 0x73, // sRGB color space + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // Unused R, G, B entries for color space + 0x00, + 0x00, + 0x00, + 0x00, // Unused Gamma X entry for color space + 0x00, + 0x00, + 0x00, + 0x00, // Unused Gamma Y entry for color space + 0x00, + 0x00, + 0x00, + 0x00, // Unused Gamma Z entry for color space + 0x00, + 0x00, + 0x00, + 0x00, // Unknown + 0x00, + 0x00, + 0x00, + 0x00, // Unknown + 0x00, + 0x00, + 0x00, + 0x00, // Unknown + 0x00, + 0x00, + 0x00, + 0x00 // Unknown // Image data after this ).map { it.toByte() }.toByteArray() - val sizeIndex = 2 - val widthIndex = 18 - val heightIndex = 22 - val rawSizeIndex = 34 + const val sizeIndex = 2 + const val widthIndex = 18 + const val heightIndex = 22 + const val rawSizeIndex = 34 fun parseColor(colorString: String): Int { var color = colorString.substring(1).toLong(16) if (colorString.length == 7) { // Set the alpha value color = color or 0x00000000ff000000L - } else if (colorString.length != 9) { - throw IllegalArgumentException("Unknown color") + } else { + require(colorString.length != 9) { "Unknown color" } } return color.toInt() } @@ -70,7 +181,7 @@ class SpectrogramBitmap { return Color(parseColor(this)) } - enum class SCALE_MODE { + enum class ScaleMode { SCALE_LINEAR, SCALE_LOG } @@ -78,7 +189,7 @@ class SpectrogramBitmap { val frequencyLegendPositionLog = intArrayOf(63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000, 24000) - val frequencyLegendPositionLinear = IntArray(24) { it * 1000 + 1000} + val frequencyLegendPositionLinear = IntArray(24) { it * 1000 + 1000 } val colorRamp = arrayOf( "#303030".toComposeColor(), @@ -99,23 +210,35 @@ class SpectrogramBitmap { "#F75500".toComposeColor(), "#FB2A00".toComposeColor(), ) - fun createSpectrogram(size: IntSize, scaleMode: SCALE_MODE, sampleRate: Double) : SpectrogramDataModel { + + fun createSpectrogram( + size: IntSize, + scaleMode: ScaleMode, + sampleRate: Double, + ): SpectrogramDataModel { val byteArray = ByteArray(bmpHeader.size + Int.SIZE_BYTES * size.width * size.height) bmpHeader.copyInto(byteArray) // fill with changing header data - val rawPixelSize = size.width*size.height*Int.SIZE_BYTES + val rawPixelSize = size.width * size.height * Int.SIZE_BYTES rawPixelSize.toLittleEndianBytes().copyInto(byteArray, rawSizeIndex) - (rawPixelSize+bmpHeader.size).toLittleEndianBytes().copyInto(byteArray, sizeIndex) + (rawPixelSize + bmpHeader.size).toLittleEndianBytes().copyInto(byteArray, sizeIndex) size.width.toLittleEndianBytes().copyInto(byteArray, widthIndex) size.height.toLittleEndianBytes().copyInto(byteArray, heightIndex) - return SpectrogramDataModel(size, byteArray, scaleMode = scaleMode, sampleRate = sampleRate) + return SpectrogramDataModel( + size, + byteArray, + scaleMode = scaleMode, + sampleRate = sampleRate + ) } /** * Convert Int into little endian array of bytes */ - fun Int.toLittleEndianBytes() : ByteArray = byteArrayOf(this.toByte(), this.ushr(8).toByte(), - this.ushr(16).toByte(), this.ushr(24).toByte()) + fun Int.toLittleEndianBytes(): ByteArray = byteArrayOf( + this.toByte(), this.ushr(8).toByte(), + this.ushr(16).toByte(), this.ushr(24).toByte() + ) } @@ -123,9 +246,12 @@ class SpectrogramBitmap { * @constructor * @si */ - data class SpectrogramDataModel(val size: IntSize, val byteArray: ByteArray, - var offset : Int = 0, val scaleMode: SCALE_MODE, - val sampleRate: Double) { + data class SpectrogramDataModel( + val size: IntSize, val byteArray: ByteArray, + var offset: Int = 0, val scaleMode: ScaleMode, + val sampleRate: Double, + ) { + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false @@ -142,13 +268,15 @@ class SpectrogramBitmap { return result } - fun pushSpectrumToSpectrogramData(fftResult : SpectrumData, - mindB : Double, rangedB : Double, gain : Double) { + fun pushSpectrumToSpectrogramData( + fftResult: SpectrumData, + mindB: Double, rangedB: Double, gain: Double, + ) { // generate columns of pixels // merge power of each frequencies following the destination bitmap resolution val hertzBySpectrumCell = sampleRate / FFT_SIZE.toDouble() val frequencyLegendPosition = when (scaleMode) { - SCALE_MODE.SCALE_LOG -> frequencyLegendPositionLog + ScaleMode.SCALE_LOG -> frequencyLegendPositionLog else -> frequencyLegendPositionLinear } var lastProcessFrequencyIndex = 0 @@ -156,7 +284,7 @@ class SpectrogramBitmap { for (pixel in 0.. - signal = signal.zip(generateSinusoidalSignal(frequencyPeak, - sampleRate.toDouble(), 1.0, coefficient = { peak }), sum + signal = signal.zip( + generateSinusoidalSignal(frequencyPeak, + sampleRate.toDouble(), 1.0, coefficient = { peak }), sum ).toDoubleArray() } @@ -268,17 +269,54 @@ class TestFFT { 0.0, 0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 4.0, 0.0, 5.0, 0.0 ) val bluestein = Bluestein(input.size / 2) - val expected_chirp = doubleArrayOf(0.965925826289071,-0.2588190451025107,0.5000000000000081, - -0.8660254037844339,-0.7071067811865438,-0.7071067811865512,-0.5000000000000047, - 0.8660254037844359,0.9659258262890694,-0.2588190451025166,-1.0,-3.920107718544707E-15, - 0.9659258262890689,-0.2588190451025185,-0.5000000000000012,0.8660254037844379, - -0.7071067811865468,-0.7071067811865482,0.5000000000000003,-0.8660254037844385, - 0.9659258262890683,-0.2588190451025207,1.0,-0.0,0.9659258262890683,-0.2588190451025207, - 0.5000000000000003,-0.8660254037844385,-0.7071067811865468,-0.7071067811865482, - -0.5000000000000012,0.8660254037844379,0.9659258262890689,-0.2588190451025185,-1.0, - -3.920107718544707E-15,0.9659258262890694,-0.2588190451025166,-0.5000000000000047, - 0.8660254037844359,-0.7071067811865438,-0.7071067811865512,0.5000000000000081, - -0.8660254037844339,0.965925826289071,-0.2588190451025107) + val expected_chirp = doubleArrayOf( + 0.965925826289071, + -0.2588190451025107, + 0.5000000000000081, + -0.8660254037844339, + -0.7071067811865438, + -0.7071067811865512, + -0.5000000000000047, + 0.8660254037844359, + 0.9659258262890694, + -0.2588190451025166, + -1.0, + -3.920107718544707E-15, + 0.9659258262890689, + -0.2588190451025185, + -0.5000000000000012, + 0.8660254037844379, + -0.7071067811865468, + -0.7071067811865482, + 0.5000000000000003, + -0.8660254037844385, + 0.9659258262890683, + -0.2588190451025207, + 1.0, + -0.0, + 0.9659258262890683, + -0.2588190451025207, + 0.5000000000000003, + -0.8660254037844385, + -0.7071067811865468, + -0.7071067811865482, + -0.5000000000000012, + 0.8660254037844379, + 0.9659258262890689, + -0.2588190451025185, + -1.0, + -3.920107718544707E-15, + 0.9659258262890694, + -0.2588190451025166, + -0.5000000000000047, + 0.8660254037844359, + -0.7071067811865438, + -0.7071067811865512, + 0.5000000000000081, + -0.8660254037844339, + 0.965925826289071, + -0.2588190451025107 + ) assertEquals(expected_chirp.size, bluestein.chirp.size) expected_chirp.forEachIndexed { index, it -> assertEquals(it, bluestein.chirp[index], 1e-8) @@ -297,8 +335,8 @@ class TestFFT { @Test fun benchBluestein() = runTest { - val input = DoubleArray(60000*2) { cos(it.toDouble()) } - val bluestein = Bluestein(input.size/2) + val input = DoubleArray(60000 * 2) { cos(it.toDouble()) } + val bluestein = Bluestein(input.size / 2) val timings = LongArray(64) { measureTime { bluestein.fft(input) @@ -307,7 +345,7 @@ class TestFFT { val timings2 = LongArray(64) { measureTime { val dim = nextPowerOfTwo(input.size) - fft(dim/2, DoubleArray(dim)) + fft(dim / 2, DoubleArray(dim)) }.inWholeMilliseconds } val sc = SpectrumChannel() @@ -317,15 +355,27 @@ class TestFFT { sc.processSamples(FloatArray(input.size / 2), false).sum() }.inWholeMilliseconds } - println("min ${timings.min()} max ${timings.max()}" + - " median ${timings.sorted() - [if(timings.size % 2==0) timings.size / 2 - 1 else timings.size / 2 ]}") - println("min ${timings2.min()} max ${timings2.max()}" + - " median ${timings2.sorted() - [if(timings2.size % 2==0) timings2.size / 2 - 1 else timings2.size / 2 ]}") - println("min ${timings3.min()} max ${timings3.max()}" + - " median ${timings3.sorted() - [if(timings3.size % 2==0) timings3.size / 2 - 1 else timings3.size / 2 ]}") + println( + "min ${timings.min()} max ${timings.max()}" + + " median ${ + timings.sorted() + [if (timings.size % 2 == 0) timings.size / 2 - 1 else timings.size / 2] + }" + ) + println( + "min ${timings2.min()} max ${timings2.max()}" + + " median ${ + timings2.sorted() + [if (timings2.size % 2 == 0) timings2.size / 2 - 1 else timings2.size / 2] + }" + ) + println( + "min ${timings3.min()} max ${timings3.max()}" + + " median ${ + timings3.sorted() + [if (timings3.size % 2 == 0) timings3.size / 2 - 1 else timings3.size / 2] + }" + ) } } diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/TestWindowAnalysis.kt b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/TestWindowAnalysis.kt similarity index 64% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/TestWindowAnalysis.kt rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/TestWindowAnalysis.kt index 66f3235..f420e97 100644 --- a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/TestWindowAnalysis.kt +++ b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/TestWindowAnalysis.kt @@ -1,27 +1,25 @@ -package org.noise_planet.noisecapture.shared.signal +package org.noiseplanet.noisecapture.shared.signal import kotlinx.coroutines.test.runTest -import org.noise_planet.noisecapture.AudioSamples -import org.noise_planet.noisecapture.shared.AcousticIndicatorsProcessing -import org.noise_planet.noisecapture.shared.FFT_HOP -import org.noise_planet.noisecapture.shared.FFT_SIZE -import org.noise_planet.noisecapture.shared.WINDOW_TIME +import org.noiseplanet.noisecapture.AudioSamples +import org.noiseplanet.noisecapture.shared.AcousticIndicatorsProcessing +import org.noiseplanet.noisecapture.shared.WINDOW_TIME import kotlin.math.PI -import kotlin.math.log10 import kotlin.math.min import kotlin.math.pow import kotlin.math.sin import kotlin.math.sqrt import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue class TestWindowAnalysis { @Test fun testHannWindow() { - val expected = floatArrayOf(0f , 0.0954915f, 0.3454915f, 0.6545085f, 0.9045085f, 1f , - 0.9045085f, 0.6545085f, 0.3454915f, 0.0954915f, 0f) + val expected = floatArrayOf( + 0f, 0.0954915f, 0.3454915f, 0.6545085f, 0.9045085f, 1f, + 0.9045085f, 0.6545085f, 0.3454915f, 0.0954915f, 0f + ) val windowAnalysis = WindowAnalysis(44100, expected.size, 1) expected.forEachIndexed { index, value -> @@ -32,7 +30,7 @@ class TestWindowAnalysis { @Test fun testOverlapWindows() { val arraySize = 13 - val ones = FloatArray(arraySize) {if(it in 2..arraySize-3) 1f else 0f} + val ones = FloatArray(arraySize) { if (it in 2..arraySize - 3) 1f else 0f } val windowAnalysis = WindowAnalysis(1, 5, 2) val processedWindows = ArrayList() windowAnalysis.pushSamples(0, ones, processedWindows).toList() @@ -42,7 +40,7 @@ class TestWindowAnalysis { @Test fun testOverlapWindowsSegments() { - for(arraySize in 9..13) { + for (arraySize in 9..13) { val ones = FloatArray(arraySize) { if (it in 2..arraySize - 3) 1f else 0f } val windowAnalysis = WindowAnalysis(1, 5, 2) val processedWindows = ArrayList() @@ -73,13 +71,17 @@ class TestWindowAnalysis { @Test fun testOverlapWindowsSmallPush() { val arraySize = 13 - val ones = FloatArray(arraySize) {if(it in 2..arraySize-3) 1f else 0f} + val ones = FloatArray(arraySize) { if (it in 2..arraySize - 3) 1f else 0f } //val ones = FloatArray(arraySize) {it.toFloat()} val windowAnalysis = WindowAnalysis(1, 5, 2) val processedWindows = ArrayList() val step = 3 - for(i in ones.indices step step) { - windowAnalysis.pushSamples(0, ones.copyOfRange(i, min(i + step, ones.size)), processedWindows).toList() + for (i in ones.indices step step) { + windowAnalysis.pushSamples( + 0, + ones.copyOfRange(i, min(i + step, ones.size)), + processedWindows + ).toList() } windowAnalysis.pushSamples( (ones.size + windowAnalysis.samplesUntilWindow).toLong(), @@ -97,14 +99,19 @@ class TestWindowAnalysis { fun testSinusSTFFTRectangularWindow() = runTest { val sampleRate = 32768 val expectedLevel = 94.0 - val peak = 10.0.pow(expectedLevel/20.0)* sqrt(2.0) + val peak = 10.0.pow(expectedLevel / 20.0) * sqrt(2.0) val sum: (Float, Float) -> Float = { x: Float, y: Float -> x + y } val frequencyPeaks = doubleArrayOf(1000.0) var signal = FloatArray(sampleRate) // sum multiple sinusoidal signals frequencyPeaks.forEach { frequencyPeak -> - signal = signal.zip(TestFFT.generateSinusoidalFloatSignal(frequencyPeak, - sampleRate.toDouble(), 1.0){peak.toFloat()}, sum).toFloatArray()} + signal = signal.zip( + TestFFT.generateSinusoidalFloatSignal( + frequencyPeak, + sampleRate.toDouble(), 1.0 + ) { peak.toFloat() }, sum + ).toFloatArray() + } val bufferSize = (sampleRate * 0.1).toInt() var cursor = 0 @@ -112,16 +119,18 @@ class TestWindowAnalysis { val spectrumDataArray = ArrayList() while (cursor < signal.size) { val windowSize = min(bufferSize, signal.size - cursor) - val windowBuffer = signal.copyOfRange(cursor, cursor+windowSize) - val epoch = (((cursor+windowSize)/sampleRate.toDouble())*1000).toLong() + val windowBuffer = signal.copyOfRange(cursor, cursor + windowSize) + val epoch = (((cursor + windowSize) / sampleRate.toDouble()) * 1000).toLong() spectrumDataArray.addAll(wa.pushSamples(epoch, windowBuffer)) cursor += windowSize } - val hertzPerCell = wa.windowSize/sampleRate.toDouble() + val hertzPerCell = wa.windowSize / sampleRate.toDouble() spectrumDataArray.forEach { spectrumData -> - assertEquals(expectedLevel, - spectrumData.spectrum[(hertzPerCell*frequencyPeaks[0]).toInt()].toDouble(), - 0.01) + assertEquals( + expectedLevel, + spectrumData.spectrum[(hertzPerCell * frequencyPeaks[0]).toInt()].toDouble(), + 0.01 + ) } } @@ -130,14 +139,19 @@ class TestWindowAnalysis { fun testSinusSTFFTHannWindowThirdOctave() = runTest { val sampleRate = 32768 val expectedLevel = 94.0 - val peak = 10.0.pow(expectedLevel/20.0)* sqrt(2.0) + val peak = 10.0.pow(expectedLevel / 20.0) * sqrt(2.0) val sum: (Float, Float) -> Float = { x: Float, y: Float -> x + y } val frequencyPeaks = doubleArrayOf(1000.0) var signal = FloatArray(sampleRate) // sum multiple sinusoidal signals frequencyPeaks.forEach { frequencyPeak -> - signal = signal.zip(TestFFT.generateSinusoidalFloatSignal(frequencyPeak, - sampleRate.toDouble(), 1.0){peak.toFloat()}, sum).toFloatArray()} + signal = signal.zip( + TestFFT.generateSinusoidalFloatSignal( + frequencyPeak, + sampleRate.toDouble(), 1.0 + ) { peak.toFloat() }, sum + ).toFloatArray() + } val bufferSize = (sampleRate * 0.1).toInt() var cursor = 0 @@ -145,19 +159,34 @@ class TestWindowAnalysis { val spectrumDataArray = ArrayList() while (cursor < signal.size) { val windowSize = min(bufferSize, signal.size - cursor) - val windowBuffer = signal.copyOfRange(cursor, cursor+windowSize) - val epoch = (((cursor+windowSize)/sampleRate.toDouble())*1000).toLong() + val windowBuffer = signal.copyOfRange(cursor, cursor + windowSize) + val epoch = (((cursor + windowSize) / sampleRate.toDouble()) * 1000).toLong() spectrumDataArray.addAll(wa.pushSamples(epoch, windowBuffer)) cursor += windowSize } spectrumDataArray.forEachIndexed { index, spectrumData -> - val thirdOctaveSquare = spectrumData.thirdOctaveProcessing(50.0, 12000.0, - octaveWindow = SpectrumData.OCTAVE_WINDOW.RECTANGULAR).asList() - val thirdOctaveFractional = spectrumData.thirdOctaveProcessing(50.0, - 12000.0, octaveWindow = SpectrumData.OCTAVE_WINDOW.FRACTIONAL).asList() - val indexOf1000Hz = thirdOctaveSquare.indexOfFirst { t -> t.midFrequency.toInt() == 1000 } - assertEquals(expectedLevel, thirdOctaveSquare[indexOf1000Hz].spl, 0.01, message = "[$index]") - assertEquals(expectedLevel, thirdOctaveFractional[indexOf1000Hz].spl, 0.01, message = "[$index]") + val thirdOctaveSquare = spectrumData.thirdOctaveProcessing( + 50.0, 12000.0, + octaveWindow = SpectrumData.OctaveWindow.RECTANGULAR + ).asList() + val thirdOctaveFractional = spectrumData.thirdOctaveProcessing( + 50.0, + 12000.0, octaveWindow = SpectrumData.OctaveWindow.FRACTIONAL + ).asList() + val indexOf1000Hz = + thirdOctaveSquare.indexOfFirst { t -> t.midFrequency.toInt() == 1000 } + assertEquals( + expectedLevel, + thirdOctaveSquare[indexOf1000Hz].spl, + 0.01, + message = "[$index]" + ) + assertEquals( + expectedLevel, + thirdOctaveFractional[indexOf1000Hz].spl, + 0.01, + message = "[$index]" + ) } } @@ -165,16 +194,17 @@ class TestWindowAnalysis { fun testFastLevelDecayRateTest() { // generate noise levels each 125 ms val cutoffTime = 5 - val timeInterval=0.125 - val levels = DoubleArray((8/timeInterval).toInt()) { t -> if(t*timeInterval < cutoffTime) 94.0 else -99.0 } + val timeInterval = 0.125 + val levels = + DoubleArray((8 / timeInterval).toInt()) { t -> if (t * timeInterval < cutoffTime) 94.0 else -99.0 } // fast level should reach input noise in 0.6 seconds // fast level decay should be at the rate of 34.7 dB/s val levelDisplayWeightedDecay = LevelDisplayWeightedDecay(FAST_DECAY_RATE, timeInterval) var previousValue = 0.0 - levels.forEachIndexed { i , it-> + levels.forEachIndexed { i, it -> val dbValue = levelDisplayWeightedDecay.getWeightedValue(it) - if(i*timeInterval >= cutoffTime) { - assertEquals(FAST_DECAY_RATE, (dbValue-previousValue)/timeInterval, 0.01) + if (i * timeInterval >= cutoffTime) { + assertEquals(FAST_DECAY_RATE, (dbValue - previousValue) / timeInterval, 0.01) } previousValue = dbValue } @@ -188,16 +218,18 @@ class TestWindowAnalysis { val peak = (2500 * sqrt(2.0)).toInt().toShort() val angularFrequency = 2.0 * PI * 1000 / sampleRate - val signal = FloatArray((sampleRate* WINDOW_TIME).toInt()) { + val signal = FloatArray((sampleRate * WINDOW_TIME).toInt()) { (sin(it * angularFrequency).toFloat() * peak) / 32768F } val acousticIndicatorProcessing = AcousticIndicatorsProcessing(sampleRate) val processed = acousticIndicatorProcessing.processSamples( - AudioSamples(0, signal, - AudioSamples.ErrorCode.OK, sampleRate) + AudioSamples( + 0, signal, + AudioSamples.ErrorCode.OK, sampleRate + ) ) - val averageLeq = processed.map{indicators -> indicators.leq}.average() + val averageLeq = processed.map { indicators -> indicators.leq }.average() assertEquals(expectedLevel, averageLeq, 0.01) } } diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/fft_test.py b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/fft_test.py similarity index 100% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/fft_test.py rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/fft_test.py diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/requirements.txt b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/requirements.txt similarity index 100% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/requirements.txt rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/requirements.txt diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/signal_reference.py b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/signal_reference.py similarity index 100% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/signal_reference.py rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/signal_reference.py diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/stft_test.py b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/stft_test.py similarity index 100% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/stft_test.py rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/stft_test.py diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/test_nonpoweroftwo.py b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/test_nonpoweroftwo.py similarity index 100% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/test_nonpoweroftwo.py rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/test_nonpoweroftwo.py diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/test_spectrum.py b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/test_spectrum.py similarity index 100% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/test_spectrum.py rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/test_spectrum.py diff --git a/shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/test_spectrum2.py b/shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/test_spectrum2.py similarity index 100% rename from shared/src/commonTest/kotlin/org/noise_planet/noisecapture/shared/signal/ref/test_spectrum2.py rename to shared/src/commonTest/kotlin/org/noiseplanet/noisecapture/shared/signal/ref/test_spectrum2.py diff --git a/shared/src/iosMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.ios.kt b/shared/src/iosMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.ios.kt similarity index 86% rename from shared/src/iosMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.ios.kt rename to shared/src/iosMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.ios.kt index a64a7c5..5bfbe8b 100644 --- a/shared/src/iosMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.ios.kt +++ b/shared/src/iosMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.ios.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap diff --git a/shared/src/jsMain/kotlin/org/noise_planet/noisecapture/Audio.kt b/shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/Audio.kt similarity index 81% rename from shared/src/jsMain/kotlin/org/noise_planet/noisecapture/Audio.kt rename to shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/Audio.kt index 47afaef..b72905c 100644 --- a/shared/src/jsMain/kotlin/org/noise_planet/noisecapture/Audio.kt +++ b/shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/Audio.kt @@ -1,4 +1,5 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture + import js.core.Void import org.khronos.webgl.Float32Array import web.media.streams.MediaStream @@ -15,6 +16,7 @@ external class AudioContext { ): ScriptProcessorNode } + external class AudioDestinationNode : AudioNode { /** @@ -33,8 +35,10 @@ external class AudioBuffer { } open external class AudioNode { - fun connect(destination: AudioNode, output: Int = definedExternally, - input: Int = definedExternally): AudioNode + fun connect( + destination: AudioNode, output: Int = definedExternally, + input: Int = definedExternally + ): AudioNode fun disconnect() @@ -45,6 +49,6 @@ external class AudioProcessingEvent { } external class ScriptProcessorNode : AudioNode { - var onaudioprocess : (AudioProcessingEvent) -> Unit + var onaudioprocess: (AudioProcessingEvent) -> Unit } diff --git a/shared/src/jsMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.js.kt b/shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.js.kt similarity index 86% rename from shared/src/jsMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.js.kt rename to shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.js.kt index a64a7c5..5bfbe8b 100644 --- a/shared/src/jsMain/kotlin/org/noise_planet/noisecapture/ImageBitmap.js.kt +++ b/shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/ImageBitmap.js.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap diff --git a/shared/src/jsMain/kotlin/org/noise_planet/noisecapture/JsAudioSource.kt b/shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/JsAudioSource.kt similarity index 67% rename from shared/src/jsMain/kotlin/org/noise_planet/noisecapture/JsAudioSource.kt rename to shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/JsAudioSource.kt index b5d4143..6f306cf 100644 --- a/shared/src/jsMain/kotlin/org/noise_planet/noisecapture/JsAudioSource.kt +++ b/shared/src/jsMain/kotlin/org/noiseplanet/noisecapture/JsAudioSource.kt @@ -1,4 +1,4 @@ -package org.noise_planet.noisecapture +package org.noiseplanet.noisecapture import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.Channel @@ -10,18 +10,21 @@ import web.media.streams.MediaStream import web.navigator.navigator const val SAMPLES_BUFFER_SIZE = 1024 -const val AUDIO_CONSTRAINT = "{audio: {echoCancellation: false, autoGainControl: false, noiseSuppression: false}}" +const val AUDIO_CONSTRAINT = + "{audio: {echoCancellation: false, autoGainControl: false, noiseSuppression: false}}" class JsAudioSource : AudioSource { - var audioContext : AudioContext? = null - var mediaStream : MediaStream? = null - var micNode : AudioNode? = null - var scriptProcessorNode : AudioNode? = null + + var audioContext: AudioContext? = null + var mediaStream: MediaStream? = null + var micNode: AudioNode? = null + var scriptProcessorNode: AudioNode? = null + //var dummyGainNode : GainNode? = null val audioSamplesChannel = Channel(onBufferOverflow = BufferOverflow.DROP_OLDEST) override suspend fun setup(): Flow { - println("Launch JSAudioSource") + println("Launch JSAudioSource") navigator.mediaDevices.getUserMedia( js(AUDIO_CONSTRAINT) ).then(onFulfilled = { mediaStream -> @@ -29,13 +32,18 @@ class JsAudioSource : AudioSource { audioContext = AudioContext() println("AudioContext ready $audioContext.") micNode = audioContext!!.createMediaStreamSource(mediaStream) - val scriptProcessorNode = audioContext!!.createScriptProcessor(SAMPLES_BUFFER_SIZE, 1, 1) + val scriptProcessorNode = + audioContext!!.createScriptProcessor(SAMPLES_BUFFER_SIZE, 1, 1) scriptProcessorNode.onaudioprocess = { audioProcessingEvent -> val buffer = audioProcessingEvent.inputBuffer val jsBuffer = buffer.getChannelData(0) - val samplesBuffer = FloatArray(jsBuffer.length) { i -> jsBuffer[i] } - audioSamplesChannel.trySend(AudioSamples(Clock.System.now().toEpochMilliseconds(), samplesBuffer, - AudioSamples.ErrorCode.OK, buffer.sampleRate.toInt())) + val samplesBuffer = FloatArray(jsBuffer.length) { i -> jsBuffer[i] } + audioSamplesChannel.trySend( + AudioSamples( + Clock.System.now().toEpochMilliseconds(), samplesBuffer, + AudioSamples.ErrorCode.OK, buffer.sampleRate.toInt() + ) + ) } micNode!!.connect(scriptProcessorNode) scriptProcessorNode.connect(audioContext!!.destination) @@ -54,12 +62,12 @@ class JsAudioSource : AudioSource { scriptProcessorNode?.disconnect() mediaStream?.getTracks()?.forEach { track -> track.stop() } try { - audioContext?.close()!!.catch { + audioContext?.close()?.catch { // ignore } - } catch (error : Exception) { + } catch (ignore: Exception) { // Ignore - println(error.stackTraceToString()) + println(ignore.stackTraceToString()) } } diff --git a/shared/src/jsMain/resources/org/noise_planet/noisecapture/raw_audio_processor.js b/shared/src/jsMain/resources/org/noiseplanet/noisecapture/raw_audio_processor.js similarity index 100% rename from shared/src/jsMain/resources/org/noise_planet/noisecapture/raw_audio_processor.js rename to shared/src/jsMain/resources/org/noiseplanet/noisecapture/raw_audio_processor.js diff --git a/webApp/src/jsMain/kotlin/Main.kt b/webApp/src/jsMain/kotlin/Main.kt index 63589c7..b30f426 100644 --- a/webApp/src/jsMain/kotlin/Main.kt +++ b/webApp/src/jsMain/kotlin/Main.kt @@ -1,4 +1,3 @@ - import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.LaunchedEffect @@ -31,11 +30,11 @@ import kotlinx.coroutines.launch import org.jetbrains.skiko.wasm.onWasmReady import org.koin.core.logger.PrintLogger import org.koin.dsl.module -import org.noise_planet.noisecapture.JsAudioSource -import org.noise_planet.noisecapture.shared.MeasurementService -import org.noise_planet.noisecapture.shared.initKoin -import org.noise_planet.noisecapture.shared.root.RootNode -import org.noise_planet.noisecapture.shared.ui.theme.AppyxStarterKitTheme +import org.noiseplanet.noisecapture.JsAudioSource +import org.noiseplanet.noisecapture.shared.MeasurementService +import org.noiseplanet.noisecapture.shared.initKoin +import org.noiseplanet.noisecapture.shared.root.RootNode +import org.noiseplanet.noisecapture.shared.ui.theme.AppyxStarterKitTheme @OptIn(ExperimentalComposeUiApi::class) fun main() { @@ -61,11 +60,10 @@ fun main() { .focusRequester(requester) .focusable() .onFocusChanged { hasFocus = it.hasFocus }, - ) { - buildContext -> + ) { buildContext -> val koinApplication = initKoin(additionalModules = listOf(module { - factory { MeasurementService(JsAudioSource(), logger) } - } )).logger(logger) + factory { MeasurementService(JsAudioSource()) } + })).logger(logger) RootNode( nodeContext = buildContext, koin = koinApplication.koin