Skip to content

Commit

Permalink
Now demo app is a KMP app (android/desktop/wasmJs).
Browse files Browse the repository at this point in the history
I could also add iosApp, but that'd be a few more hours work, which I don't
find worth... you can copy and edit files from ktmidi-ci-tool.
  • Loading branch information
atsushieno committed Mar 8, 2024
1 parent 3f08f1f commit 3d3e8c1
Show file tree
Hide file tree
Showing 38 changed files with 1,104 additions and 103 deletions.
137 changes: 91 additions & 46 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,74 +1,119 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
kotlin("multiplatform")
alias(libs.plugins.androidApplication)
alias(libs.plugins.kotlinAndroid)
alias(libs.plugins.jetbrainsComposePlugin)
}

dependencies.platform(libs.compose.bom)

kotlin {
wasmJs {
browser {
commonWebpackConfig {
outputFileName = "demoapp.js"
}
}
//nodejs {}
binaries.executable()
}

androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
}
}
}

jvm("desktop")

listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "ComposeAudioControlsDemoApp"
isStatic = true
}
}

sourceSets {
val desktopMain by getting

androidMain.dependencies {
implementation(libs.core.ktx)
implementation(libs.activity.compose)
}
commonMain.dependencies {
implementation(project(":compose-audio-controls"))
implementation(project(":compose-audio-controls-midi"))

implementation(libs.ktmidi)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
implementation(libs.ktmidi.jvm.desktop)
}

val iosMain by creating { dependsOn(commonMain.get()) }
val iosX64Main by getting { dependsOn(iosMain) }
val iosArm64Main by getting { dependsOn(iosMain) }
val iosSimulatorArm64Main by getting { dependsOn(iosMain)
}
}
}

android {
namespace = "org.androidaudioplugin.composeaudiocontrols.demoapp"
compileSdk = 34

sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
sourceSets["main"].res.srcDirs("src/androidMain/res")
sourceSets["main"].resources.srcDirs("src/commonMain/resources")

defaultConfig {
applicationId = "org.androidaudioplugin.composeaudiocontrols"
applicationId = "org.androidaudioplugin.composeaudiocontrols.demoapp"
minSdk = 23
targetSdk = 34
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}

buildTypes {
release {
getByName("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
dependencies {
debugImplementation(libs.compose.ui.tooling)
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.8"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}

compose.desktop {
application {
mainClass = "MainKt"

nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "dev.atsushieno.ktmidi.citool"
packageVersion = "1.0.0"
}
}
}

dependencies {
implementation(project(":compose-audio-controls"))
implementation(project(":compose-audio-controls-midi"))

implementation(libs.core.ktx)
implementation(libs.lifecycle.runtime.ktx)
implementation(libs.activity.compose)
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui)
implementation(libs.compose.ui.graphics)
implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.material3)
implementation(libs.ktmidi)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.espresso.core)
androidTestImplementation(platform(libs.compose.bom))
androidTestImplementation(libs.compose.ui.test.junit4)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
}
compose.experimental {
web.application {}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import org.androidaudioplugin.composeaudiocontrols.demoapp.ui.theme.ComposeAudioControlsTheme
import dev.atsushieno.ktmidi.AndroidMidi2Access
import org.androidaudioplugin.composeaudiocontrols.demoapp.MainContent
import org.androidaudioplugin.composeaudiocontrols.demoapp.theme.ComposeAudioControlsTheme
import kotlin.system.exitProcess

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
midiAccess = AndroidMidi2Access(this, true)
setContent {
ComposeAudioControlsTheme {
// A surface container using the 'background' color from the theme
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.androidaudioplugin.composeaudiocontrols.demoapp

import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import dev.atsushieno.ktmidi.EmptyMidiAccess
import org.androidaudioplugin.composeaudiocontrols.demoapp.theme.ComposeAudioControlsTheme
import org.androidaudioplugin.composeaudiocontrols.midi.DiatonicLiveMidiKeyboard
import org.androidaudioplugin.composeaudiocontrols.midi.KtMidiDeviceAccessScope
import org.androidaudioplugin.composeaudiocontrols.midi.MidiDeviceConfigurator


@Composable
fun KnobPreview() {
ComposeAudioControlsTheme {
ImageStripKnobDemo()
}
}


@Composable
fun DiatonicKeyboardPreview() {
ComposeAudioControlsTheme {
Column {
val scope by remember { mutableStateOf(KtMidiDeviceAccessScope(EmptyMidiAccess())) }
scope.MidiDeviceConfigurator()
scope.DiatonicLiveMidiKeyboard()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.androidaudioplugin.composeaudiocontrols.demoapp.ui.theme
package org.androidaudioplugin.composeaudiocontrols.demoapp.theme

import androidx.compose.ui.graphics.Color

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.androidaudioplugin.composeaudiocontrols.demoapp.ui.theme
package org.androidaudioplugin.composeaudiocontrols.demoapp.theme

import android.app.Activity
import android.os.Build
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.androidaudioplugin.composeaudiocontrols.demoapp.ui.theme
package org.androidaudioplugin.composeaudiocontrols.demoapp.theme

import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,32 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import dev.atsushieno.ktmidi.AndroidMidi2Access
import dev.atsushieno.ktmidi.AndroidMidiAccess
import composeaudiocontrols.app.generated.resources.*
import composeaudiocontrols.app.generated.resources.Res
import composeaudiocontrols.app.generated.resources.bright_life
import composeaudiocontrols.app.generated.resources.knob_blue
import composeaudiocontrols.app.generated.resources.vst_knob_01_100pix
import dev.atsushieno.ktmidi.EmptyMidiAccess
import dev.atsushieno.ktmidi.MidiAccess
import org.androidaudioplugin.composeaudiocontrols.DefaultKnobTooltip
import org.androidaudioplugin.composeaudiocontrols.ImageStripKnob
import org.androidaudioplugin.composeaudiocontrols.ImageStripKnobScope
import org.androidaudioplugin.composeaudiocontrols.defaultKnobMinSizeInDp
import org.androidaudioplugin.composeaudiocontrols.demoapp.ui.theme.ComposeAudioControlsTheme
import org.androidaudioplugin.composeaudiocontrols.midi.DiatonicLiveMidiKeyboard
import org.androidaudioplugin.composeaudiocontrols.midi.KtMidiDeviceAccessScope
import org.androidaudioplugin.composeaudiocontrols.midi.MidiDeviceConfigurator
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.imageResource
import kotlin.math.pow

internal fun formatLabelNumber(v: Float, charsInPositiveNumber: Int = 5) = v.toBigDecimal().toPlainString().take(charsInPositiveNumber + if (v < 0) 1 else 0)
internal fun formatLabelNumber(v: Float, charsInPositiveNumber: Int = 5) = v.toDouble().toString().take(charsInPositiveNumber + if (v < 0) 1 else 0)

var midiAccess: MidiAccess = EmptyMidiAccess()

@Composable
fun MainContent() {
Expand All @@ -40,8 +50,6 @@ fun MainContent() {

@Composable
fun DiatonicMidiKeyboardDemo() {
val context = LocalContext.current
val midiAccess by remember { mutableStateOf(AndroidMidi2Access(context, true)) }
val scope by remember { mutableStateOf(KtMidiDeviceAccessScope(midiAccess)) }
SectionLabel("DiagnosticKeyboard Demo")
scope.MidiDeviceConfigurator()
Expand All @@ -60,14 +68,7 @@ fun SectionLabel(text: String) {
Divider()
}

@Preview(showBackground = true)
@Composable
fun KnobPreview() {
ComposeAudioControlsTheme {
ImageStripKnobDemo()
}
}

@OptIn(ExperimentalResourceApi::class)
@Composable
fun ImageStripKnobDemo() {
Column {
Expand All @@ -84,7 +85,7 @@ fun ImageStripKnobDemo() {
var knobSize: Int? by remember { mutableStateOf(null) }
KnobSizeSelector(knobSize, onSizeChange = { knobSize = it })

var knobStyle by remember { mutableIntStateOf(R.drawable.bright_life) }
var knobStyle by remember { mutableStateOf(Res.drawable.bright_life) }
KnobStyleSelector(knobStyle, onSelectionChange = { knobStyle = it })

val scrollState = rememberScrollState()
Expand All @@ -97,7 +98,7 @@ fun ImageStripKnobDemo() {
if (minSizeCheckedState) 1.dp
else if ((48 > (knobSize ?: 48))) knobSize!!.dp
else defaultKnobMinSizeInDp
ImageStripKnob(drawableResId = knobStyle,
ImageStripKnob(drawableRes = knobStyle,
value = paramValue,
valueRange = 0f..1f * 2f.pow(paramIndex.toFloat()),
minSizeInDp = minSize,
Expand Down Expand Up @@ -145,14 +146,14 @@ fun KnobSizeSelector(size: Int?, onSizeChange: (size: Int?) -> Unit) {
}
}

@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalResourceApi::class)
@Composable
fun KnobStyleSelector(currentResId: Int, onSelectionChange: (id: Int) -> Unit) {
fun KnobStyleSelector(currentResId: DrawableResource, onSelectionChange: (id: DrawableResource) -> Unit) {
val imageOptions = mapOf(
R.drawable.bright_life to "bright_life",
R.drawable.chromed_knob to "chromed_knob",
R.drawable.knob_blue to "knob_blue",
R.drawable.vst_knob_01_100pix to "vst_knob_01_100pix")
Res.drawable.bright_life to "bright_life",
Res.drawable.chromed_knob to "chromed_knob",
Res.drawable.knob_blue to "knob_blue",
Res.drawable.vst_knob_01_100pix to "vst_knob_01_100pix")
var imageOptionsExpanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = imageOptionsExpanded,
Expand Down Expand Up @@ -187,14 +188,41 @@ fun KnobStyleSelector(currentResId: Int, onSelectionChange: (id: Int) -> Unit) {
}
}

@Preview(showBackground = true)

// FIXME: can we have this implementation in library, not app?
// Currently `DrawableResource` and `imageResource` are not in accessible libraries
// unless any resource is actually generated...
@OptIn(ExperimentalResourceApi::class)
@Composable
fun DiatonicKeyboardPreview() {
ComposeAudioControlsTheme {
Column {
val scope by remember { mutableStateOf(KtMidiDeviceAccessScope(EmptyMidiAccess())) }
scope.MidiDeviceConfigurator()
scope.DiatonicLiveMidiKeyboard()
}
}
fun ImageStripKnob(modifier: Modifier = Modifier,
drawableRes: DrawableResource,
value: Float = 0f,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
explicitSizeInDp: Dp? = null,
minSizeInDp: Dp = defaultKnobMinSizeInDp,
fineModeDelayMs: Int = 1000,
tooltipColor: Color = Color.Gray,
tooltip: @Composable ImageStripKnobScope.() -> Unit = {
// by default, show tooltip only when it is being dragged
DefaultKnobTooltip(
value = knobValue,
showTooltip = knobIsBeingDragged,
textColor = tooltipColor
)
},
onValueChange: (value: Float) -> Unit = {}
) {
val imageBitmap = imageResource(drawableRes)
ImageStripKnob(
modifier,
imageBitmap,
value,
valueRange,
explicitSizeInDp,
minSizeInDp,
fineModeDelayMs,
tooltipColor,
tooltip,
onValueChange
)
}
Loading

0 comments on commit 3d3e8c1

Please sign in to comment.