Skip to content

Commit

Permalink
Merge pull request #35 from Universite-Gustave-Eiffel/feature/navigat…
Browse files Browse the repository at this point in the history
…ion-architecture

Feature/navigation architecture
  • Loading branch information
nicolas-f authored Aug 19, 2024
2 parents 6b9bdd0 + b5188b2 commit 853cc0f
Show file tree
Hide file tree
Showing 62 changed files with 2,718 additions and 2,186 deletions.
2 changes: 2 additions & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ kotlin {
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.viewmodel.compose)
implementation(libs.koin.core)
implementation(libs.koin.compose)
implementation(libs.koin.compose.viewmodel)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.datetime)
}
Expand Down
1 change: 1 addition & 0 deletions composeApp/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:enableOnBackInvokedCallback="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
import org.koin.core.module.Module
import org.koin.dsl.module
import org.noiseplanet.noisecapture.measurements.MeasurementService
import org.noiseplanet.noisecapture.permission.DefaultPermissionService
import org.noiseplanet.noisecapture.permission.PermissionService
import org.noiseplanet.noisecapture.measurements.DefaultMeasurementService
import org.noiseplanet.noisecapture.measurements.MeasurementsService
import org.noiseplanet.noisecapture.permission.defaultPermissionModule
import org.noiseplanet.noisecapture.permission.platformPermissionModule
import org.noiseplanet.noisecapture.ui.features.home.homeModule
import org.noiseplanet.noisecapture.ui.features.measurement.measurementModule
import org.noiseplanet.noisecapture.ui.features.permission.requestPermissionModule

/**
* Create root Koin application and register modules shared between platforms
Expand All @@ -20,12 +22,20 @@ fun initKoin(
modules(
module {
includes(additionalModules)
},

single<PermissionService> { DefaultPermissionService() }
single<MeasurementService> { MeasurementService(audioSource = get()) }
module {
single<MeasurementsService> {
DefaultMeasurementService(audioSource = get(), logger = get())
}
},

defaultPermissionModule,
platformPermissionModule()
platformPermissionModule(),

homeModule,
requestPermissionModule,
measurementModule,
)
createEagerInstances()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package org.noiseplanet.noisecapture

import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
Expand All @@ -14,28 +11,26 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import org.koin.compose.koinInject
import org.noiseplanet.noisecapture.ui.AppBar
import org.noiseplanet.noisecapture.ui.NavigationRoute
import org.noiseplanet.noisecapture.ui.screens.HomeScreen
import org.noiseplanet.noisecapture.ui.screens.MeasurementScreen
import org.noiseplanet.noisecapture.ui.screens.PlatformInfoScreen
import org.noiseplanet.noisecapture.ui.screens.RequestPermissionScreen
import org.noiseplanet.noisecapture.ui.features.home.HomeScreen
import org.noiseplanet.noisecapture.ui.features.measurement.MeasurementScreen
import org.noiseplanet.noisecapture.ui.features.permission.RequestPermissionScreen
import org.noiseplanet.noisecapture.ui.navigation.Route
import org.noiseplanet.noisecapture.ui.navigation.Transitions


/**
* Root component of the app.
* Currently handles the navigation stack, and navigation bar management.
*/
@Composable
fun NoiseCaptureApp(
navController: NavHostController = rememberNavController(),
) {
fun NoiseCaptureApp() {
val navController: NavHostController = rememberNavController()
// Get current navigation back stack entry
val backStackEntry by navController.currentBackStackEntryAsState()
// Get the name of the current screen
val currentScreen = NavigationRoute.valueOf(
backStackEntry?.destination?.route ?: NavigationRoute.Home.name
val currentScreen = Route.valueOf(
backStackEntry?.destination?.route ?: Route.Home.name
)

Scaffold(
Expand All @@ -47,56 +42,31 @@ fun NoiseCaptureApp(
)
}
) { innerPadding ->
// TODO: Configure NavHost in a separate file
// TODO: Use ease out curve for slide transitions
// TODO: Handle swipe back gestures on iOS -> encapsulate UINavigationController?
// TODO: Handle predictive back gestures on Android
NavHost(
navController = navController,
startDestination = NavigationRoute.Home.name,
enterTransition = {
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, tween(300))
},
exitTransition = {
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, tween(300))
},
popEnterTransition = {
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(300))
},
popExitTransition = {
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(300))
},
startDestination = Route.Home.name,
enterTransition = Transitions.enterTransition,
exitTransition = Transitions.exitTransition,
popEnterTransition = Transitions.popEnterTransition,
popExitTransition = Transitions.popExitTransition,
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
composable(route = NavigationRoute.Home.name) {
HomeScreen(
onClick = {
// TODO: Silently check for permissions and bypass this step if they are already all granted
navController.navigate(NavigationRoute.RequestPermission.name)
},
)
}
composable(route = NavigationRoute.PlatformInfo.name) {
PlatformInfoScreen(
modifier = Modifier.fillMaxHeight()
)
composable(route = Route.Home.name) {
// TODO: Silently check for permissions and bypass this step if they are already all granted
HomeScreen(navigationController = navController)
}
composable(route = NavigationRoute.RequestPermission.name) {
composable(route = Route.RequestPermission.name) {
RequestPermissionScreen(
onClickNextButton = {
navController.navigate(NavigationRoute.Measurement.name)
navController.navigate(Route.Measurement.name)
}
)
}
composable(route = NavigationRoute.Measurement.name) {
// TODO: Decide of a standard for screens architecture:
// - class or compose function as root?
// - Inject dependencies in constructor or via Koin factories?
// - What should be the package structure?
MeasurementScreen(measurementService = koinInject())
.Content()
composable(route = Route.Measurement.name) {
MeasurementScreen()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.noiseplanet.noisecapture.audio.signal

import kotlin.math.pow

data class FrequencyBand(
val minFrequency: Double,
val midFrequency: Double,
val maxFrequency: Double,
var spl: Double,
) {

enum class BaseMethod {
B10,
B2
}

companion object {

/**
* Create (third-)octave array from the specified parameters (without spl values)
*
* @param firstFrequencyBand First frequency band (Hz)
* @param lastFrequencyBand Last frequency band (Hz)
* @param base Octave base 2 or 10
* @param bandDivision Octave bands division (defaults to 3 for third octaves)
*/
fun emptyFrequencyBands(
firstFrequencyBand: Double,
lastFrequencyBand: Double,
base: BaseMethod = BaseMethod.B10,
bandDivision: Double = 3.0,
): Array<FrequencyBand> {
val g = when (base) {
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 ->
val (fMin, fMid, fMax) = getBands(bandIndex + firstBandIndex, g, bandDivision)
FrequencyBand(fMin, fMid, fMax, 0.0)
}
}

private fun getBands(
bandIndex: Int,
g: Double,
bandDivision: Double,
): Triple<Double, Double, Double> {
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
return Triple(fMin, fMid, fMax)
}

private fun getBandIndexByFrequency(
targetFrequency: Double,
g: Double,
bandDivision: Double,
): Int {
var frequencyBandIndex = 0
var (fMin, fMid, fMax) = getBands(frequencyBandIndex, g, bandDivision)
while (!(fMin < targetFrequency && targetFrequency < fMax)) {
if (targetFrequency < fMin) {
frequencyBandIndex -= 1
} else if (targetFrequency > fMax) {
frequencyBandIndex += 1
}
val bandInfo = getBands(frequencyBandIndex, g, bandDivision)
fMin = bandInfo.first
fMax = bandInfo.third
}
return frequencyBandIndex
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@ const val SLOW_DECAY_RATE = -4.3

/**
* IEC 61672-1 standard for displayed sound level decay
*
* TODO: Document parameters
*/
class LevelDisplayWeightedDecay(decibelDecayPerSecond: Double, newValueTimeInterval: Double) {
class LevelDisplayWeightedDecay(
decibelDecayPerSecond: Double,
newValueTimeInterval: Double,
) {

val timeWeight = 10.0.pow(decibelDecayPerSecond * newValueTimeInterval / 10.0)
var timeIntegration = 0.0
private val timeWeight = 10.0.pow(decibelDecayPerSecond * newValueTimeInterval / 10.0)
private var timeIntegration = 0.0

/**
* TODO: add documentation
*/
fun getWeightedValue(newValue: Double): Double {
timeIntegration =
timeIntegration * timeWeight + 10.0.pow(newValue / 10.0) * (1 - timeWeight)
timeIntegration = timeIntegration * timeWeight +
10.0.pow(newValue / 10.0) * (1 - timeWeight)
return 10 * log10(timeIntegration)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package org.noiseplanet.noisecapture.audio.signal

import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import org.noiseplanet.noisecapture.audio.signal.filter.BiquadFilter
import org.noiseplanet.noisecapture.audio.signal.filter.DigitalFilter
import kotlin.math.pow

/**
* Digital filtering of audio samples
*
* @author Nicolas Fortin, Université Gustave Eiffel
* @author Valentin Le Bescond, Université Gustave Eiffel
* @link https://github.com/SonoMKR/sonomkr-core/blob/master/src/spectrumchannel.cpp
Expand Down
Loading

0 comments on commit 853cc0f

Please sign in to comment.