Compare commits
6 Commits
310f77fc3a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7110188d33 | |||
| 2cfc8a8201 | |||
| ce353d3113 | |||
| ff8a4dbf92 | |||
| dc50084e84 | |||
| 7fe29916f7 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -42,6 +42,10 @@ bin/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
/web
|
/web
|
||||||
|
/web1
|
||||||
|
/web2
|
||||||
|
|
||||||
|
/data/*.db*
|
||||||
|
**/kotlin-js-store/*
|
||||||
.kotlin
|
.kotlin
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
105
.junie/guidelines.md
Normal file
105
.junie/guidelines.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# VST Chip Synthesizer Development Guidelines
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
VST Chip is a Kotlin multiplatform project that implements a chip-style synthesizer with a web interface. The project consists of:
|
||||||
|
|
||||||
|
- JVM backend that serves the web application
|
||||||
|
- JS frontend that runs in the browser
|
||||||
|
- Audio worklet processor for real-time audio processing
|
||||||
|
|
||||||
|
## Build/Configuration Instructions
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- JDK 11 or higher
|
||||||
|
- Gradle 7.x or higher
|
||||||
|
- Node.js and npm (for JS development)
|
||||||
|
|
||||||
|
### Building the Project
|
||||||
|
|
||||||
|
1. **Full Build**:
|
||||||
|
```bash
|
||||||
|
./gradlew build
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **JS Build Only**:
|
||||||
|
```bash
|
||||||
|
./gradlew buildJS
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Deployment**:
|
||||||
|
```bash
|
||||||
|
./gradlew deploy
|
||||||
|
```
|
||||||
|
This will build the project, copy web assets, and deploy to the configured server location.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
- Server port: 9005 (configured in `Main.kt`)
|
||||||
|
- JDBC stats port: 6005
|
||||||
|
- Deployment directory: configured in `build.gradle.kts` as `vst-chip.midi-vst.com`
|
||||||
|
|
||||||
|
## Testing Information
|
||||||
|
|
||||||
|
- **Do not generate tests** for this project. The audio processing code is highly specialized and requires manual testing with audio
|
||||||
|
equipment.
|
||||||
|
- Manual testing should be performed using MIDI controllers and audio monitoring tools.
|
||||||
|
|
||||||
|
## Development Information
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
- **src/commonMain**: Shared code between JS and JVM
|
||||||
|
- **src/jsMain**: Browser-specific code
|
||||||
|
- **src/jvmMain**: Server-specific code
|
||||||
|
- **audio-worklet**: Audio processing code that runs in a separate thread in the browser
|
||||||
|
|
||||||
|
### Key Components
|
||||||
|
|
||||||
|
1. **JVM Server** (`src/jvmMain/kotlin/nl/astraeus/vst/chip/Main.kt`):
|
||||||
|
- Undertow server that serves the web application
|
||||||
|
- Database initialization
|
||||||
|
- Logging setup
|
||||||
|
|
||||||
|
2. **JS Frontend** (`src/jsMain/kotlin/nl/astraeus/vst/chip/Main.kt`):
|
||||||
|
- UI implementation using Komponent library
|
||||||
|
- MIDI handling
|
||||||
|
- WebSocket client for server communication
|
||||||
|
|
||||||
|
3. **Audio Processor** (`audio-worklet/src/jsMain/kotlin/nl/astraeus/vst/chip/ChipProcessor.kt`):
|
||||||
|
- Real-time audio synthesis
|
||||||
|
- MIDI message handling
|
||||||
|
- Sound generation with multiple waveforms (sine, square, triangle, sawtooth)
|
||||||
|
- Effects processing (FM, AM, ADSR envelope, delay, feedback)
|
||||||
|
|
||||||
|
### Development Workflow
|
||||||
|
|
||||||
|
1. Make changes to the code
|
||||||
|
2. Run `./gradlew buildJS` to build the JS part
|
||||||
|
3. Run the JVM application to test locally
|
||||||
|
4. Use `./gradlew deploy` to deploy to the server
|
||||||
|
|
||||||
|
### Audio Worklet Development
|
||||||
|
|
||||||
|
The audio worklet runs in a separate thread in the browser and handles real-time audio processing. When modifying the audio worklet code:
|
||||||
|
|
||||||
|
1. Understand that it runs in a separate context from the main JS code
|
||||||
|
2. Communication happens via message passing
|
||||||
|
3. Performance is critical - avoid garbage collection and heavy operations in the audio processing loop
|
||||||
|
|
||||||
|
### MIDI Implementation
|
||||||
|
|
||||||
|
The synthesizer responds to standard MIDI messages:
|
||||||
|
|
||||||
|
- Note On/Off (0x90/0x80)
|
||||||
|
- Control Change (0xb0) for various parameters
|
||||||
|
- Program Change (0xc9) for waveform selection
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
The project is configured to deploy to a specific server location. The deployment process:
|
||||||
|
|
||||||
|
1. Builds the project
|
||||||
|
2. Copies web assets
|
||||||
|
3. Creates a symbolic link for the latest version
|
||||||
@@ -31,7 +31,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
distribution {
|
distribution {
|
||||||
outputDirectory.set(File("$projectDir/../web/"))
|
outputDirectory.set(File("$projectDir/../web2/"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package nl.astraeus.vst.midi
|
||||||
|
|
||||||
|
typealias MidiHandler = (Byte, Byte, Byte) -> Unit
|
||||||
|
|
||||||
|
val Byte.channel: Byte
|
||||||
|
get() {
|
||||||
|
return (this.toInt() and 0x0F).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val Byte.command: Byte
|
||||||
|
get() {
|
||||||
|
return ((this.toInt() and 0xF0) shr 4).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val Byte.channelCommand: Boolean
|
||||||
|
get() {
|
||||||
|
return this.command >= 8 && this.command <= 13
|
||||||
|
}
|
||||||
|
|
||||||
|
class MidiMessageHandler(
|
||||||
|
var channel: Byte = -1
|
||||||
|
) {
|
||||||
|
val singleByteHandlers = mutableMapOf<Byte, MidiHandler>()
|
||||||
|
val doubleByteHandlers = mutableMapOf<Byte, MutableMap<Byte, MidiHandler>>()
|
||||||
|
|
||||||
|
fun addHandler(
|
||||||
|
byte1: Byte,
|
||||||
|
byte2: Byte = 0,
|
||||||
|
handler: MidiHandler
|
||||||
|
) {
|
||||||
|
val b1 = if (byte1.channelCommand) {
|
||||||
|
(byte1.toInt() and 0xF0).toByte()
|
||||||
|
} else {
|
||||||
|
byte1
|
||||||
|
}
|
||||||
|
if (byte2 == 0.toByte()) {
|
||||||
|
singleByteHandlers[b1] = handler
|
||||||
|
} else {
|
||||||
|
val map = doubleByteHandlers.getOrPut(b1) {
|
||||||
|
mutableMapOf()
|
||||||
|
}
|
||||||
|
map[byte2] = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addHandler(
|
||||||
|
byte1: Int,
|
||||||
|
byte2: Int = 0,
|
||||||
|
handler: MidiHandler
|
||||||
|
) = addHandler(
|
||||||
|
byte1.toByte(),
|
||||||
|
byte2.toByte(),
|
||||||
|
handler
|
||||||
|
)
|
||||||
|
|
||||||
|
fun handle(
|
||||||
|
byte1: Byte,
|
||||||
|
byte2: Byte,
|
||||||
|
byte3: Byte
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
channel < 0 ||
|
||||||
|
!byte1.channelCommand ||
|
||||||
|
(byte1.channelCommand && channel == byte1.channel)
|
||||||
|
) {
|
||||||
|
val b1 = if (byte1.channelCommand) {
|
||||||
|
(byte1.toInt() and 0xF0).toByte()
|
||||||
|
} else {
|
||||||
|
byte1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doubleByteHandlers.containsKey(b1)) {
|
||||||
|
doubleByteHandlers[b1]?.get(byte2)?.invoke(byte1, byte2, byte3)
|
||||||
|
} else if (singleByteHandlers.containsKey(b1)) {
|
||||||
|
singleByteHandlers[b1]?.invoke(byte1, byte2, byte3)
|
||||||
|
} else {
|
||||||
|
println("Unhandled message: $byte1 $byte2 $byte3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -73,7 +73,7 @@ class VstChipProcessor : AudioWorkletProcessor() {
|
|||||||
var waveform = Waveform.SINE.ordinal
|
var waveform = Waveform.SINE.ordinal
|
||||||
var volume = 0.75f
|
var volume = 0.75f
|
||||||
var dutyCycle = 0.5
|
var dutyCycle = 0.5
|
||||||
var fmFreq = 0.0
|
var fmFreq = 0.5
|
||||||
var fmAmp = 0.0
|
var fmAmp = 0.0
|
||||||
var amFreq = 0.0
|
var amFreq = 0.0
|
||||||
var amAmp = 0.0
|
var amAmp = 0.0
|
||||||
@@ -340,8 +340,10 @@ class VstChipProcessor : AudioWorkletProcessor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cycleOffset = note.cycleOffset
|
var cycleOffset = note.cycleOffset
|
||||||
|
val fmMult = sin(currentTime * fmFreq * midiNote.freq * 2f * PI2) * fmAmp
|
||||||
val fmModulation =
|
val fmModulation =
|
||||||
sampleDelta + (sin(fmFreq * 1000f * PI2 * (note.sample / sampleRate.toDouble())).toFloat() * (100f * fmAmp * sampleDelta))
|
sampleDelta * fmMult //+ (sin(fmFreq * 1000f * PI2 * (note.sample / sampleRate.toDouble())).toFloat() * (100f * fmAmp * sampleDelta))
|
||||||
|
|
||||||
val amModulation =
|
val amModulation =
|
||||||
1f + (sin(sampleLength * amFreq * 1000f * PI2 * note.sample) * amAmp).toFloat()
|
1f + (sin(sampleLength * amFreq * 1000f * PI2 * note.sample) * amAmp).toFloat()
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ kotlin {
|
|||||||
commonWebpackConfig {
|
commonWebpackConfig {
|
||||||
outputFileName = "vst-chip-worklet-ui.js"
|
outputFileName = "vst-chip-worklet-ui.js"
|
||||||
sourceMaps = true
|
sourceMaps = true
|
||||||
|
devtool = "inline-source-map"
|
||||||
}
|
}
|
||||||
|
|
||||||
distribution {
|
distribution {
|
||||||
outputDirectory.set(File("$projectDir/web/"))
|
outputDirectory.set(File("$projectDir/web1/"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,44 +56,17 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
//base
|
api("nl.astraeus:vst-ui-base:2.0.0")
|
||||||
implementation("nl.astraeus:kotlin-css-generator:1.0.10")
|
|
||||||
implementation("nl.astraeus:vst-ui-base:1.2.0")
|
|
||||||
implementation("nl.astraeus:midi-arrays:0.3.4")
|
implementation("nl.astraeus:midi-arrays:0.3.4")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val jsMain by getting {
|
val jsMain by getting
|
||||||
dependencies {
|
|
||||||
implementation("nl.astraeus:kotlin-komponent:1.2.4")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val jsTest by getting {
|
val jsTest by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("test-js"))
|
implementation(kotlin("test-js"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* val wasmJsMain by getting {
|
val jvmMain by getting
|
||||||
dependencies {
|
|
||||||
implementation("nl.astraeus:kotlin-komponent:1.2.4-SNAPSHOT")
|
|
||||||
implementation("nl.astraeus:vst-ui-base:1.0.1-SNAPSHOT")
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
val jvmMain by getting {
|
|
||||||
dependencies {
|
|
||||||
//base
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
|
|
||||||
|
|
||||||
implementation("io.undertow:undertow-core:2.3.14.Final")
|
|
||||||
implementation("io.undertow:undertow-websockets-jsr:2.3.14.Final")
|
|
||||||
implementation("org.jboss.xnio:xnio-nio:3.8.16.Final")
|
|
||||||
|
|
||||||
implementation("org.xerial:sqlite-jdbc:3.46.0.0")
|
|
||||||
implementation("com.zaxxer:HikariCP:4.0.3")
|
|
||||||
implementation("nl.astraeus:simple-jdbc-stats:1.6.1")
|
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +74,18 @@ application {
|
|||||||
mainClass.set("nl.astraeus.vst.chip.MainKt")
|
mainClass.set("nl.astraeus.vst.chip.MainKt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register<Copy>("buildJS") {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||||
|
dependsOn("audio-worklet:jsBrowserDevelopmentExecutableDistribution")
|
||||||
|
dependsOn("jsBrowserDevelopmentExecutableDistribution")
|
||||||
|
|
||||||
|
from(layout.projectDirectory.dir("web1"))
|
||||||
|
into(layout.projectDirectory.dir("web"))
|
||||||
|
|
||||||
|
from(layout.projectDirectory.dir("web2"))
|
||||||
|
into(layout.projectDirectory.dir("web"))
|
||||||
|
}
|
||||||
|
|
||||||
/* Hardcoded deploy configuration */
|
/* Hardcoded deploy configuration */
|
||||||
|
|
||||||
val deployDirectory = "vst-chip.midi-vst.com"
|
val deployDirectory = "vst-chip.midi-vst.com"
|
||||||
|
|||||||
1
data/readme.md
Normal file
1
data/readme.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Data directory for the db
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "2.1.10"
|
kotlin("multiplatform") version "2.1.20"
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
|
|||||||
@@ -9,12 +9,20 @@ import nl.astraeus.vst.chip.view.MainView
|
|||||||
import nl.astraeus.vst.chip.ws.WebsocketClient
|
import nl.astraeus.vst.chip.ws.WebsocketClient
|
||||||
import nl.astraeus.vst.ui.css.CssSettings
|
import nl.astraeus.vst.ui.css.CssSettings
|
||||||
|
|
||||||
fun main() {
|
object Views {
|
||||||
CssSettings.shortId = false
|
val mainView by lazy {
|
||||||
CssSettings.preFix = "vst-chip"
|
MainView()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
CssSettings.shortId = false
|
||||||
|
CssSettings.preFix = "vst"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
Komponent.unsafeMode = UnsafeMode.UNSAFE_SVG_ONLY
|
Komponent.unsafeMode = UnsafeMode.UNSAFE_SVG_ONLY
|
||||||
Komponent.create(document.body!!, MainView)
|
Komponent.create(document.body!!, Views.mainView)
|
||||||
|
|
||||||
Midi.start()
|
Midi.start()
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ package nl.astraeus.vst.chip.audio
|
|||||||
|
|
||||||
import nl.astraeus.midi.message.TimedMidiMessage
|
import nl.astraeus.midi.message.TimedMidiMessage
|
||||||
import nl.astraeus.vst.chip.PatchDTO
|
import nl.astraeus.vst.chip.PatchDTO
|
||||||
import nl.astraeus.vst.chip.view.MainView
|
import nl.astraeus.vst.chip.Views
|
||||||
import nl.astraeus.vst.chip.view.WaveformView
|
import nl.astraeus.vst.chip.view.WaveformView
|
||||||
import org.khronos.webgl.Float32Array
|
import org.khronos.webgl.Float32Array
|
||||||
import org.w3c.dom.MessageEvent
|
import org.w3c.dom.MessageEvent
|
||||||
@@ -40,7 +40,7 @@ object VstChipWorklet : AudioNode(
|
|||||||
0xb0 + midiChannel, 0x47, (value * 127).toInt()
|
0xb0 + midiChannel, 0x47, (value * 127).toInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var fmModFreq = 0.0
|
var fmModFreq = 1.0
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
super.postMessage(
|
super.postMessage(
|
||||||
@@ -168,32 +168,32 @@ object VstChipWorklet : AudioNode(
|
|||||||
when (knob) {
|
when (knob) {
|
||||||
0x46.toByte() -> {
|
0x46.toByte() -> {
|
||||||
volume = value / 127.0
|
volume = value / 127.0
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
0x4a.toByte() -> {
|
0x4a.toByte() -> {
|
||||||
dutyCycle = value / 127.0
|
dutyCycle = value / 127.0
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
0x40.toByte() -> {
|
0x40.toByte() -> {
|
||||||
fmModFreq = value / 127.0
|
fmModFreq = value / 127.0
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
0x41.toByte() -> {
|
0x41.toByte() -> {
|
||||||
fmModAmp = value / 127.0
|
fmModAmp = value / 127.0
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
0x42.toByte() -> {
|
0x42.toByte() -> {
|
||||||
amModFreq = value / 127.0
|
amModFreq = value / 127.0
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
0x43.toByte() -> {
|
0x43.toByte() -> {
|
||||||
amModAmp = value / 127.0
|
amModAmp = value / 127.0
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package nl.astraeus.vst.chip.midi
|
|||||||
|
|
||||||
import kotlinx.browser.window
|
import kotlinx.browser.window
|
||||||
import nl.astraeus.midi.message.TimedMidiMessage
|
import nl.astraeus.midi.message.TimedMidiMessage
|
||||||
|
import nl.astraeus.vst.chip.Views
|
||||||
import nl.astraeus.vst.chip.audio.AudioContextHandler
|
import nl.astraeus.vst.chip.audio.AudioContextHandler
|
||||||
import nl.astraeus.vst.chip.audio.VstChipWorklet
|
import nl.astraeus.vst.chip.audio.VstChipWorklet
|
||||||
import nl.astraeus.vst.chip.view.MainView
|
|
||||||
import org.khronos.webgl.Uint8Array
|
import org.khronos.webgl.Uint8Array
|
||||||
import org.khronos.webgl.get
|
import org.khronos.webgl.get
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ object Midi {
|
|||||||
outputs.add(output)
|
outputs.add(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
},
|
},
|
||||||
{ e ->
|
{ e ->
|
||||||
println("Failed to get MIDI access - $e")
|
println("Failed to get MIDI access - $e")
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import nl.astraeus.komp.Komponent
|
|||||||
import nl.astraeus.komp.currentElement
|
import nl.astraeus.komp.currentElement
|
||||||
import nl.astraeus.midi.message.TimedMidiMessage
|
import nl.astraeus.midi.message.TimedMidiMessage
|
||||||
import nl.astraeus.midi.message.getCurrentTime
|
import nl.astraeus.midi.message.getCurrentTime
|
||||||
|
import nl.astraeus.vst.chip.Views
|
||||||
import nl.astraeus.vst.chip.audio.VstChipWorklet
|
import nl.astraeus.vst.chip.audio.VstChipWorklet
|
||||||
import nl.astraeus.vst.chip.audio.VstChipWorklet.midiChannel
|
import nl.astraeus.vst.chip.audio.VstChipWorklet.midiChannel
|
||||||
import nl.astraeus.vst.chip.midi.Midi
|
import nl.astraeus.vst.chip.midi.Midi
|
||||||
@@ -61,7 +62,7 @@ object WaveformView: Komponent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onAnimationFrame(time: Double) {
|
fun onAnimationFrame(time: Double) {
|
||||||
if (MainView.started) {
|
if (Views.mainView.started) {
|
||||||
VstChipWorklet.postMessage("start_recording")
|
VstChipWorklet.postMessage("start_recording")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ object WaveformView: Komponent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object MainView : Komponent(), CssName {
|
class MainView : Komponent() {
|
||||||
private var messages: MutableList<String> = ArrayList()
|
private var messages: MutableList<String> = ArrayList()
|
||||||
var started = false
|
var started = false
|
||||||
|
|
||||||
@@ -275,7 +276,7 @@ object MainView : Komponent(), CssName {
|
|||||||
value = VstChipWorklet.fmModFreq,
|
value = VstChipWorklet.fmModFreq,
|
||||||
label = "FM Freq",
|
label = "FM Freq",
|
||||||
minValue = 0.0,
|
minValue = 0.0,
|
||||||
maxValue = 1.0,
|
maxValue = 2.0,
|
||||||
step = 5.0 / 127.0,
|
step = 5.0 / 127.0,
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 120,
|
height = 120,
|
||||||
@@ -420,18 +421,19 @@ object MainView : Komponent(), CssName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object MainDivCss : CssName
|
companion object MainViewCss : CssName() {
|
||||||
object ActiveCss : CssName
|
object MainDivCss : CssName()
|
||||||
object ButtonCss : CssName
|
object ActiveCss : CssName()
|
||||||
object ButtonBarCss : CssName
|
object ButtonCss : CssName()
|
||||||
object SelectedCss : CssName
|
object ButtonBarCss : CssName()
|
||||||
object NoteBarCss : CssName
|
object SelectedCss : CssName()
|
||||||
object StartSplashCss : CssName
|
object NoteBarCss : CssName()
|
||||||
object StartBoxCss : CssName
|
object StartSplashCss : CssName()
|
||||||
object StartButtonCss : CssName
|
object StartBoxCss : CssName()
|
||||||
object ControlsCss : CssName
|
object StartButtonCss : CssName()
|
||||||
|
object ControlsCss : CssName()
|
||||||
|
|
||||||
private fun css() {
|
private fun css() {
|
||||||
defineCss {
|
defineCss {
|
||||||
select("*") {
|
select("*") {
|
||||||
select("*:before") {
|
select("*:before") {
|
||||||
@@ -530,23 +532,23 @@ object MainView : Komponent(), CssName {
|
|||||||
backgroundColor(Css.currentStyle.mainBackgroundColor)
|
backgroundColor(Css.currentStyle.mainBackgroundColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun Style.commonButton() {
|
|
||||||
display(Display.inlineBlock)
|
|
||||||
padding(1.rem)
|
|
||||||
backgroundColor(Css.currentStyle.buttonBackgroundColor)
|
|
||||||
borderColor(Css.currentStyle.buttonBorderColor)
|
|
||||||
borderWidth(Css.currentStyle.buttonBorderWidth)
|
|
||||||
color(Css.currentStyle.mainFontColor)
|
|
||||||
|
|
||||||
hover {
|
|
||||||
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover())
|
|
||||||
}
|
}
|
||||||
and(SelectedCss.cls()) {
|
|
||||||
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover().hover().hover())
|
private fun Style.commonButton() {
|
||||||
|
display(Display.inlineBlock)
|
||||||
|
padding(1.rem)
|
||||||
|
backgroundColor(Css.currentStyle.buttonBackgroundColor)
|
||||||
|
borderColor(Css.currentStyle.buttonBorderColor)
|
||||||
|
borderWidth(Css.currentStyle.buttonBorderWidth)
|
||||||
|
color(Css.currentStyle.mainFontColor)
|
||||||
|
|
||||||
|
hover {
|
||||||
|
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover())
|
||||||
|
}
|
||||||
|
and(SelectedCss.cls()) {
|
||||||
|
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover().hover().hover())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ package nl.astraeus.vst.chip.ws
|
|||||||
|
|
||||||
import kotlinx.browser.window
|
import kotlinx.browser.window
|
||||||
import nl.astraeus.vst.chip.PatchDTO
|
import nl.astraeus.vst.chip.PatchDTO
|
||||||
|
import nl.astraeus.vst.chip.Views
|
||||||
import nl.astraeus.vst.chip.audio.VstChipWorklet
|
import nl.astraeus.vst.chip.audio.VstChipWorklet
|
||||||
import nl.astraeus.vst.chip.midi.Midi
|
import nl.astraeus.vst.chip.midi.Midi
|
||||||
import nl.astraeus.vst.chip.view.MainView
|
|
||||||
import org.w3c.dom.MessageEvent
|
import org.w3c.dom.MessageEvent
|
||||||
import org.w3c.dom.WebSocket
|
import org.w3c.dom.WebSocket
|
||||||
import org.w3c.dom.events.Event
|
import org.w3c.dom.events.Event
|
||||||
@@ -90,7 +90,7 @@ object WebsocketClient {
|
|||||||
|
|
||||||
Midi.setInput(patch.midiId, patch.midiName)
|
Midi.setInput(patch.midiId, patch.midiName)
|
||||||
VstChipWorklet.load(patch)
|
VstChipWorklet.load(patch)
|
||||||
MainView.requestUpdate()
|
Views.mainView.requestUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user