Skip to content

Commit 75b1340

Browse files
committed
WIP: midi device simulator
1 parent 8ae57a8 commit 75b1340

File tree

19 files changed

+267
-117
lines changed

19 files changed

+267
-117
lines changed

src/commonMain/kotlin/baaahs/ShowPlayer.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package baaahs
22

3+
import baaahs.gl.GlContext
34
import baaahs.gl.Toolchain
5+
import baaahs.gl.data.EngineFeedContext
46
import baaahs.gl.data.FeedContext
7+
import baaahs.gl.data.ProgramFeedContext
8+
import baaahs.gl.glsl.GlslProgram
59
import baaahs.gl.shader.OpenShader
610
import baaahs.gl.withCache
711
import baaahs.plugin.Plugins
@@ -11,6 +15,9 @@ import baaahs.show.live.OpenShow
1115
import baaahs.show.live.ShowOpener
1216
import baaahs.ui.addObserver
1317
import baaahs.util.Clock
18+
import baaahs.util.Logger
19+
import baaahs.util.RefCounted
20+
import baaahs.util.RefCounter
1421

1522
interface ShowPlayer {
1623
fun <T : Gadget> registerGadget(id: String, gadget: T, controlledFeed: Feed? = null)
@@ -43,7 +50,19 @@ abstract class BaseShowPlayer(
4350
// TODO: This is another reference to feeds, so we should .use() it... but then we'll never release them!
4451
// TODO: Also, it could conceivably be handed out after it's had onRelease() called. How should we handle this?
4552
return feeds.getOrPut(feed) {
46-
feed.open(this, id)
53+
try {
54+
feed.open(this, id)
55+
} catch (e: Error) {
56+
logger.error(e) { "Can't open feed $id" }
57+
58+
return object : FeedContext, RefCounted by RefCounter() {
59+
override fun bind(gl: GlContext): EngineFeedContext = object : EngineFeedContext {
60+
override fun bind(glslProgram: GlslProgram): ProgramFeedContext {
61+
return object : ProgramFeedContext {}
62+
}
63+
}
64+
}
65+
}
4766
}
4867
}
4968

@@ -76,4 +95,8 @@ abstract class BaseShowPlayer(
7695
if (!openShader.inUse()) shaders.remove(shader)
7796
}
7897
}
98+
99+
companion object {
100+
private val logger = Logger<BaseShowPlayer>()
101+
}
79102
}

src/commonMain/kotlin/baaahs/di/Modules.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import baaahs.scene.SceneProvider
3535
import baaahs.sim.FakeDmxUniverse
3636
import baaahs.sim.FakeFs
3737
import baaahs.sim.FakeNetwork
38+
import baaahs.sim.SimulatorSettingsManager
3839
import baaahs.sm.brain.BrainManager
3940
import baaahs.sm.brain.FirmwareDaddy
4041
import baaahs.sm.brain.ProdBrainSimulator
@@ -202,6 +203,7 @@ interface SimulatorModule : KModule {
202203
single(named(Qualifier.MapperFs)) { FakeFs("Temporary Mapping Files") }
203204
single<Fs>(named(Qualifier.MapperFs)) { get<FakeFs>(named(Qualifier.MapperFs)) }
204205
single(named(WebClientModule.Qualifier.PinkyAddress)) { get<Network.Link>(named(Qualifier.PinkyLink)).myAddress }
206+
single { SimulatorSettingsManager(get(named(Qualifier.PinkyFs))) }
205207
}
206208

207209
enum class Qualifier {

src/commonMain/kotlin/baaahs/plugin/OpenPlugin.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import baaahs.show.FeedBuilder
1414
import baaahs.show.mutable.MutableControl
1515
import baaahs.show.mutable.MutableShow
1616
import baaahs.sim.BridgeClient
17+
import baaahs.sim.SimulatorSettingsManager
1718
import baaahs.ui.Icon
1819
import baaahs.util.Clock
1920
import kotlinx.cli.ArgParser
@@ -99,6 +100,14 @@ interface OpenSimulatorPlugin {
99100

100101
/** This plugin is used on the client when running in the Simulator. */
101102
fun getClientPlugin(pluginContext: PluginContext): OpenClientPlugin
103+
104+
fun getHardwareSimulators(): List<HardwareSimulator> = emptyList()
105+
}
106+
107+
interface HardwareSimulator {
108+
val title: String
109+
110+
suspend fun start() {}
102111
}
103112

104113
class SerializerRegistrar<T : Any>(val klass: KClass<T>, val serializer: KSerializer<T>) {
@@ -149,7 +158,7 @@ interface Plugin<T> {
149158
* client plugins via [#openForSimulator].
150159
*/
151160
interface SimulatorPlugin {
152-
fun openForSimulator(): OpenSimulatorPlugin
161+
fun openForSimulator(simulatorSettingsManager: SimulatorSettingsManager): OpenSimulatorPlugin
153162
}
154163

155164
class PluginContext(

src/commonMain/kotlin/baaahs/plugin/Plugins.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import baaahs.scene.MutableControllerConfig
2929
import baaahs.show.*
3030
import baaahs.show.mutable.MutableFeedPort
3131
import baaahs.sim.BridgeClient
32+
import baaahs.sim.SimulatorSettingsManager
3233
import baaahs.sm.brain.BrainControllerConfig
3334
import baaahs.sm.brain.BrainManager
3435
import baaahs.sm.server.PinkyArgs
@@ -110,18 +111,20 @@ class ClientPlugins : Plugins {
110111
}
111112

112113
class SimulatorPlugins(
114+
simulatorSettingsManager: SimulatorSettingsManager,
113115
private val bridgeClient: BridgeClient,
114116
plugins: List<Plugin<*>>
115117
) {
116118
private val simulatorPlugins: List<OpenSimulatorPlugin>
117119
private var pluginsToSimulatorPlugins: List<Pair<Plugin<*>, OpenSimulatorPlugin?>>
120+
lateinit var hardwareSimulators: List<HardwareSimulator>
118121

119122
init {
120123
val forSimulator = mutableListOf<OpenSimulatorPlugin>()
121124

122125
pluginsToSimulatorPlugins = plugins.map {
123126
it as Plugin<Any>
124-
it to (it as? SimulatorPlugin)?.openForSimulator()
127+
it to (it as? SimulatorPlugin)?.openForSimulator(simulatorSettingsManager)
125128
}
126129
simulatorPlugins = forSimulator
127130
}
@@ -150,6 +153,11 @@ class SimulatorPlugins(
150153
},
151154
pluginContext
152155
)
156+
157+
fun getHardwareSimulators() =
158+
pluginsToSimulatorPlugins.flatMap { (plugin, simulatorPlugin) ->
159+
simulatorPlugin?.getHardwareSimulators() ?: emptyList()
160+
}
153161
}
154162

155163
sealed class Plugins(
@@ -436,8 +444,12 @@ sealed class Plugins(
436444
fun buildForClient(pluginContext: PluginContext, plugins: List<Plugin<*>>): ClientPlugins =
437445
ClientPlugins(pluginContext, listOf(CorePlugin) + plugins)
438446

439-
fun buildForSimulator(bridgeClient: BridgeClient, plugins: List<Plugin<*>>): SimulatorPlugins =
440-
SimulatorPlugins(bridgeClient, listOf(CorePlugin) + plugins)
447+
fun buildForSimulator(
448+
simulatorSettingsManager: SimulatorSettingsManager,
449+
bridgeClient: BridgeClient,
450+
plugins: List<Plugin<*>>
451+
): SimulatorPlugins =
452+
SimulatorPlugins(simulatorSettingsManager, bridgeClient, listOf(CorePlugin) + plugins)
441453

442454
fun safe(pluginContext: PluginContext): Plugins =
443455
SafePlugins(pluginContext, listOf(CorePlugin.openSafe(pluginContext)))

src/commonMain/kotlin/baaahs/plugin/beatlink/BeatLinkPlugin.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import baaahs.PubSub
44
import baaahs.app.ui.CommonIcons
55
import baaahs.gl.patch.ContentType
66
import baaahs.plugin.*
7-
import baaahs.show.Feed
8-
import baaahs.show.FeedBuilder
97
import baaahs.sim.BridgeClient
8+
import baaahs.sim.SimulatorSettingsManager
109
import baaahs.ui.Facade
1110
import baaahs.ui.Observable
1211
import baaahs.util.Logger
@@ -85,7 +84,7 @@ class BeatLinkPlugin internal constructor(
8584
override fun openForClient(pluginContext: PluginContext): OpenClientPlugin =
8685
BeatLinkPlugin(PubSubSubscriber(pluginContext.pubSub), pluginContext)
8786

88-
override fun openForSimulator(): OpenSimulatorPlugin =
87+
override fun openForSimulator(simulatorSettingsManager: SimulatorSettingsManager): OpenSimulatorPlugin =
8988
object : OpenSimulatorPlugin {
9089
override fun getBridgePlugin(pluginContext: PluginContext): OpenBridgePlugin =
9190
BeatLinkBridgePlugin(createServerBeatSource(pluginContext), pluginContext)

src/commonMain/kotlin/baaahs/plugin/midi/MidiData.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,26 @@ data class MidiData(
1616

1717

1818
interface MidiSource : IObservable {
19+
val name: String
20+
1921
fun getMidiData(): MidiData
2022

2123
object None : Observable(), MidiSource {
22-
val none = MidiData(0, 0)
24+
override val name = "None"
25+
26+
private val none = MidiData(0, 0)
2327

2428
override fun getMidiData(): MidiData = none
2529
}
2630
}
2731

32+
interface MidiSystem : IObservable {
33+
val midiSources: List<MidiSource>
34+
35+
suspend fun start() {}
36+
37+
object None : Observable(), MidiSystem {
38+
override val midiSources: List<MidiSource>
39+
get() = emptyList()
40+
}
41+
}

src/commonMain/kotlin/baaahs/plugin/midi/MidiPlugin.kt

Lines changed: 60 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package baaahs.plugin.midi
22

3-
import baaahs.PubSub
43
import baaahs.ShowPlayer
54
import baaahs.gl.GlContext
65
import baaahs.gl.data.EngineFeedContext
@@ -14,19 +13,24 @@ import baaahs.plugin.*
1413
import baaahs.show.Feed
1514
import baaahs.show.FeedBuilder
1615
import baaahs.sim.BridgeClient
16+
import baaahs.sim.SimulatorSettingsManager
1717
import baaahs.ui.Observable
1818
import baaahs.ui.addObserver
1919
import baaahs.util.Logger
2020
import baaahs.util.RefCounted
2121
import baaahs.util.RefCounter
22+
import baaahs.util.globalLaunch
2223
import kotlinx.cli.ArgParser
2324
import kotlinx.cli.ArgType
2425
import kotlinx.cli.default
2526
import kotlinx.serialization.SerialName
27+
import kotlinx.serialization.Serializable
2628

2729
class MidiPlugin internal constructor(
28-
internal val midiSource: MidiSource,
30+
internal val midiSystem: MidiSystem,
2931
) : OpenServerPlugin, OpenClientPlugin {
32+
private val midiSource = midiSystem.midiSources.firstOrNull() ?: MidiSource.None
33+
3034
override val packageName: String = MidiPlugin.id
3135
override val title: String = "Midi"
3236

@@ -98,7 +102,6 @@ class MidiPlugin internal constructor(
98102

99103
interface Args {
100104
val enableMidi: Boolean
101-
val midiSource: MidiSource? get() = null
102105
}
103106

104107
companion object : Plugin<Args>, SimulatorPlugin {
@@ -120,85 +123,83 @@ class MidiPlugin internal constructor(
120123
override fun getArgs(parser: ArgParser): Args = ParserArgs(parser)
121124

122125
override fun openForServer(pluginContext: PluginContext, args: Args): OpenServerPlugin {
123-
val midiSource = if (args.enableMidi) {
124-
args.midiSource ?: createServerMidiSource(pluginContext)
125-
} else MidiSource.None
126-
return MidiPlugin(
127-
PubSubPublisher(midiSource, pluginContext)
128-
)
126+
val midiSystem = if (args.enableMidi) {
127+
createMidiSystem(pluginContext)
128+
} else MidiSystem.None
129+
130+
return MidiPlugin(midiSystem)
129131
}
130132

131133
override fun openForClient(pluginContext: PluginContext): OpenClientPlugin =
132-
MidiPlugin(PubSubSubscriber(pluginContext.pubSub))
134+
MidiPlugin(MidiSystem.None)
133135

134-
override fun openForSimulator(): OpenSimulatorPlugin =
136+
override fun openForSimulator(
137+
simulatorSettingsManager: SimulatorSettingsManager
138+
): OpenSimulatorPlugin =
135139
object : OpenSimulatorPlugin {
136-
override fun getBridgePlugin(pluginContext: PluginContext): OpenBridgePlugin =
137-
MidiBridgePlugin(createServerMidiSource(pluginContext), pluginContext)
140+
private val midiHardwareSimulator = MidiHardwareSimulator(simulatorSettingsManager)
141+
142+
override fun getBridgePlugin(pluginContext: PluginContext): OpenBridgePlugin? = null
138143

139144
override fun getServerPlugin(pluginContext: PluginContext, bridgeClient: BridgeClient) =
140145
openForServer(pluginContext, object : Args { override val enableMidi: Boolean get() = true })
141146

142147
override fun getClientPlugin(pluginContext: PluginContext): OpenClientPlugin =
143148
openForClient(pluginContext)
144-
}
145149

146-
private val midiDataTopic = PubSub.Topic("plugins/$id/midiData", MidiData.serializer())
147-
}
148-
149-
/** Copy midi data from [midiSource] to a bridge PubSub channel. */
150-
class MidiBridgePlugin(
151-
private val midiSource: MidiSource,
152-
pluginContext: PluginContext
153-
) : OpenBridgePlugin {
154-
private val channel = pluginContext.pubSub.openChannel(midiDataTopic, unknownMidi) { }
155-
156-
init {
157-
midiSource.addObserver { channel.onChange(it.getMidiData()) }
158-
}
150+
override fun getHardwareSimulators(): List<HardwareSimulator> =
151+
listOf(midiHardwareSimulator)
152+
}
159153
}
154+
}
160155

161-
class PubSubPublisher(
162-
midiSource: MidiSource,
163-
pluginContext: PluginContext
164-
) : Observable(), MidiSource {
165-
private var midiData: MidiData = midiSource.getMidiData()
156+
class MidiHardwareSimulator(
157+
private val simulatorSettingsManager: SimulatorSettingsManager
158+
) : HardwareSimulator, MidiSystem, Observable() {
159+
override val title: String = "MIDI"
166160

167-
val channel = pluginContext.pubSub.openChannel(midiDataTopic, midiData) {
168-
logger.warn { "MidiData update from client? Huh?" }
169-
midiData = it
170-
notifyChanged()
171-
}
161+
override var midiSources: List<MidiSource> = emptyList()
162+
private set
172163

173-
init {
174-
midiSource.addObserver {
175-
val newMidiData = it.getMidiData()
176-
midiData = newMidiData
177-
notifyChanged()
178-
channel.onChange(newMidiData)
164+
override suspend fun start() {
165+
logger.info { "Starting MIDI hardware simulator." }
166+
simulatorSettingsManager.addObserver(fireImmediately = true) {
167+
globalLaunch {
168+
onSettingsChange()
179169
}
180170
}
181-
182-
override fun getMidiData(): MidiData = midiData
183-
184171
}
185172

186-
class PubSubSubscriber(
187-
pubSub: PubSub.Endpoint,
188-
defaultMidiData: MidiData = unknownMidi
189-
) : Observable(), MidiSource {
190-
private var midiData: MidiData = defaultMidiData
173+
private fun onSettingsChange() {
174+
val config = simulatorSettingsManager.simSettings
175+
.getConfig(MidiPlugin.id, MidiHardwareSimulatorSettings.serializer())
176+
?: MidiHardwareSimulatorSettings(emptyList())
191177

192-
init {
193-
pubSub.openChannel(midiDataTopic, midiData) {
194-
midiData = it
195-
notifyChanged()
196-
}
197-
}
178+
println("config = $config")
179+
midiSources = config.devices.map { SimMidiSource(it.name) }
180+
}
198181

199-
override fun getMidiData(): MidiData = midiData
182+
companion object {
183+
private val logger = Logger<MidiHardwareSimulator>()
184+
}
185+
}
200186

187+
@Serializable
188+
data class MidiHardwareSimulatorSettings(
189+
val devices: List<SimMidiDevice>
190+
)
191+
192+
@Serializable
193+
data class SimMidiDevice(
194+
val name: String
195+
)
196+
197+
class SimMidiSource(
198+
override val name: String
199+
) : MidiSource, Observable() {
200+
override fun getMidiData(): MidiData {
201+
TODO("not implemented")
201202
}
202203
}
203204

204-
internal expect fun createServerMidiSource(pluginContext: PluginContext): MidiSource
205+
internal expect fun createMidiSystem(pluginContext: PluginContext): MidiSystem

0 commit comments

Comments
 (0)