Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/galaxy-sketch #24

Merged
merged 9 commits into from
Oct 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
build/
out/
*.iml
local.properties
local.properties
data/midi
data/movies
data/music
2 changes: 1 addition & 1 deletion src/main/kotlin/dev/matsem/astral/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dev.matsem.astral
object Config {

object Sketch {
const val DEFAULT_SELECTOR = '1'
const val DEFAULT_SELECTOR = 's'
}

object Color {
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/dev/matsem/astral/di/koin_modules.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import dev.matsem.astral.sketches.gameoflife.GameOfLifeSketch
import dev.matsem.astral.sketches.patterns.PatternsSketch
import dev.matsem.astral.sketches.polygonal.PolygonalSketch
import dev.matsem.astral.sketches.spikes.SpikesSketch
import dev.matsem.astral.sketches.starfield.StarfieldSketch
import dev.matsem.astral.sketches.galaxy.GalaxySketch
import dev.matsem.astral.sketches.starglitch.StarGlitchSketch
import dev.matsem.astral.sketches.terrain.TerrainSketch
import dev.matsem.astral.sketches.video.VideoSketch
Expand Down Expand Up @@ -83,6 +83,6 @@ val appModule = module {
factory { SpikesSketch() }
factory { CubesSketch() }
factory { VideoSketch() }
factory { StarfieldSketch() }
factory { GalaxySketch() }
factory { GameOfLifeSketch() }
}
6 changes: 3 additions & 3 deletions src/main/kotlin/dev/matsem/astral/sketches/SketchLoader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import dev.matsem.astral.sketches.gameoflife.GameOfLifeSketch
import dev.matsem.astral.sketches.patterns.PatternsSketch
import dev.matsem.astral.sketches.polygonal.PolygonalSketch
import dev.matsem.astral.sketches.spikes.SpikesSketch
import dev.matsem.astral.sketches.starfield.StarfieldSketch
import dev.matsem.astral.sketches.galaxy.GalaxySketch
import dev.matsem.astral.sketches.starglitch.StarGlitchSketch
import dev.matsem.astral.sketches.terrain.TerrainSketch
import dev.matsem.astral.sketches.video.VideoSketch
Expand Down Expand Up @@ -88,7 +88,7 @@ class SketchLoader : PApplet(), KoinComponent {
private val spikesSketch: SpikesSketch by inject()
private val cubesSketch: CubesSketch by inject()
private val videoSketch: VideoSketch by inject()
private val starfieldSketch: StarfieldSketch by inject()
private val galaxySketch: GalaxySketch by inject()
private val gameOfLifeSketch: GameOfLifeSketch by inject()

// endregion
Expand Down Expand Up @@ -149,7 +149,7 @@ class SketchLoader : PApplet(), KoinComponent {
put('9', spikesSketch)
put('p', cubesSketch)
put('m', videoSketch)
put('s', starfieldSketch)
put('s', galaxySketch)
put('g', gameOfLifeSketch)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.matsem.astral.sketches.starfield
package dev.matsem.astral.sketches.galaxy

data class GalaxyImage(
val path: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
package dev.matsem.astral.sketches.starfield
package dev.matsem.astral.sketches.galaxy

import dev.matsem.astral.sketches.BaseSketch
import dev.matsem.astral.sketches.SketchLoader
import dev.matsem.astral.tools.audio.AudioProcessor
import dev.matsem.astral.tools.audio.beatcounter.BeatCounter
import dev.matsem.astral.tools.audio.beatcounter.OnKick
import dev.matsem.astral.tools.audio.beatcounter.OnSnare
import dev.matsem.astral.tools.automator.MidiAutomator
import dev.matsem.astral.tools.extensions.*
import dev.matsem.astral.tools.kontrol.KontrolF1
import dev.matsem.astral.tools.kontrol.onTogglePad
import dev.matsem.astral.tools.kontrol.onTriggerPad
import dev.matsem.astral.tools.galaxy.Galaxy
import dev.matsem.astral.tools.logging.SketchLogger
import org.koin.core.KoinComponent
import org.koin.core.inject
import processing.core.PApplet.sin
import processing.core.PConstants
import processing.core.PImage
import processing.core.PVector
import kotlin.math.sin

class StarfieldSketch : BaseSketch(), KoinComponent {

override val sketch: SketchLoader by inject()
class GalaxySketch : BaseSketch(), KoinComponent {

data class Star(
val vec: PVector,
Expand All @@ -37,53 +34,55 @@ class StarfieldSketch : BaseSketch(), KoinComponent {
.withFps()
.build()

override val sketch: SketchLoader by inject()
private val audioProcessor: AudioProcessor by inject()
private val beatCounter: BeatCounter by inject()
private val kontrol: KontrolF1 by inject()
private val galaxy: Galaxy by inject()
private val automator: MidiAutomator by inject()

private val lock = Any()
private val starField = mutableListOf<Star>()
private val galaxy = mutableListOf<Star>()
private val galaxyStars = mutableListOf<Star>()
private val images = arrayOf(
GalaxyImage(path = "images/galaxy1.png", pixelStep = 3, threshold = 60f),
GalaxyImage(path = "images/galaxy2.png", pixelStep = 2, threshold = 50f)
GalaxyImage(path = "images/galaxy2.png", pixelStep = 2, threshold = 50f),
GalaxyImage(path = "images/galaxy1.png", pixelStep = 3, threshold = 50f),
GalaxyImage(path = "images/galaxy2.png", pixelStep = 2, threshold = 40f)
)

private var bassGain: Float = 0f
private var expandingQuantized: Boolean = false
private var expandingOnBeat: Boolean = false
private var expandingValue = 1f
private var randomDiameters: Boolean = false
// region remote control

override fun onBecameActive() {
kontrol.reset()
private val galaxyImageButtons = galaxy.createPushButtonGroup(10, listOf(4, 5, 6, 7)) {
createGalaxy(images[it])
}

kontrol.onTriggerPad(0, 0, 50) {
if (it) {
createGalaxy(images[0])
}
}
private val zoomQuantizeButton = galaxy.createToggleButton(channel = 10, cc = 8, defaultValue = false)
private val zoomOnBeatButton = galaxy.createToggleButton(channel = 10, cc = 9, defaultValue = false)
private val zoomQuantSlider = galaxy.createPot(channel = 10, cc = 10, min = 0.005f, max = 0.5f, initialValue = 0.5f)
private val zoomHzSlider = galaxy.createPot(channel = 10, cc = 11, min = 1 / 60f, max = 1 / 5f, initialValue = 1 / 60f)
private val zoomMinSlider = galaxy.createPot(channel = 10, cc = 12, min = 1f, max = 4f, initialValue = 1f)
private val zoomMaxSlider = galaxy.createPot(channel = 10, cc = 13, min = 1f, max = 4f, initialValue = 2f)
private var zoomValue = 1f

kontrol.onTriggerPad(0, 1, 50) {
if (it) {
createGalaxy(images[1])
}
}
private val randomDiametersButton = galaxy.createToggleButton(channel = 10, cc = 14, defaultValue = false)
private val starDiameterSlider = galaxy.createPot(channel = 10, cc = 15, min = 0.5f, max = 1.5f, initialValue = 1f)

kontrol.onTogglePad(0, 2, 0) {
expandingQuantized = it
}
private val bassGainSlider = galaxy.createPot(channel = 10, cc = 16, min = 0f, max = 1f)

kontrol.onTogglePad(1, 2, 10) {
expandingOnBeat = it
}
// endregion

kontrol.onTogglePad(0, 3, 70) {
randomDiameters = it
}
}
override fun onBecameActive() = Unit

override fun setup() = with(sketch) {
automator.setupWithGalaxy(
channel = 10,
recordButtonCC = 0,
playButtonCC = 1,
loopButtonCC = 2,
clearButtonCC = 3,
channelFilter = null
)

// Create galaxy from image
createGalaxy(images[0])

Expand All @@ -100,21 +99,21 @@ class StarfieldSketch : BaseSketch(), KoinComponent {
}

beatCounter.addListener(OnSnare, 1) {
if (randomDiameters) {
if (randomDiametersButton.isPressed) {
randomizeDiameters()
}
}

beatCounter.addListener(OnKick, 4) {
if (expandingOnBeat) {
expandingValue = random(1f, 1.5f)
if (zoomOnBeatButton.isPressed) {
zoomValue = random(zoomMinSlider.value, zoomMaxSlider.value)
}
}
}

private fun createGalaxy(image: GalaxyImage) = with(sketch) {
synchronized(lock) {
galaxy.clear()
galaxyStars.clear()
val galaxyImage: PImage = loadImage(image.path).apply {
val ratio = pixelWidth / pixelHeight.toFloat()
resize(720, (720 / ratio).toInt())
Expand All @@ -126,7 +125,7 @@ class StarfieldSketch : BaseSketch(), KoinComponent {
for (y in 0 until galaxyImage.height step image.pixelStep) {
pixelBrightness = brightness(galaxyImage[x, y])
if (pixelBrightness > image.threshold) {
galaxy += Star(
galaxyStars += Star(
vec = PVector(
x.toFloat() - galaxyImage.width / 2f,
random(-4f, 4f),
Expand All @@ -144,7 +143,7 @@ class StarfieldSketch : BaseSketch(), KoinComponent {
}

private fun randomizeDiameters() = synchronized(lock) {
galaxy.forEach {
galaxyStars.forEach {
it.diameter = generateDiameter()
}
}
Expand All @@ -154,33 +153,37 @@ class StarfieldSketch : BaseSketch(), KoinComponent {
}

override fun draw() = with(sketch) {
bassGain = kontrol.slider1.midiRange(1f)
automator.update()
val diameterFactor = starDiameterSlider.value

beatCounter.update()
background(30)
background(bgColor)

noFill()
stroke(0f, 0f, 100f)
stroke(fgColor)

if (zoomQuantizeButton.isPressed) {
zoomValue = sin(angularTimeHz(zoomHzSlider.value))
.mapSin(zoomMinSlider.value, zoomMaxSlider.value)
.quantize(zoomQuantSlider.value)
}

// Galaxy
synchronized(lock) {
galaxy.forEach {
galaxyStars.forEach {
pushMatrix()
translateCenter()
scale(zoomValue)

it.rotationExtra += audioProcessor.getRange(1000f..4000f).remap(0f, 100f, 0f, 0.02f) * it.randomFactor
rotateX(-0.34f)
rotateY((millis() - it.birth) * it.ySpeed + it.rotationExtra)
rotateZ((millis() - it.birth) * it.zSpeed)

strokeWeight(it.diameter)
val amp = audioProcessor.getRange(20f..200f) * random(-0.1f, 0.1f) * bassGain

if (expandingQuantized) {
expandingValue = sin(saw(1 / 5f)).mapp(1f, 1.5f).quantize(0.05f)
}
strokeWeight(it.diameter * diameterFactor)
val amp = audioProcessor.getRange(20f..200f) * random(-0.1f, 0.1f) * bassGainSlider.value

val v = it.vec.copy().mult(expandingValue)
val v = it.vec
point(v.x, v.y + amp, v.z)
popMatrix()
}
Expand All @@ -192,25 +195,27 @@ class StarfieldSketch : BaseSketch(), KoinComponent {
.take(audioProcessor
.getRange(20f..60f)
.remap(0f, 400f, starField.size.toFloat(), starField.size.toFloat() / 2f).toInt()
.constrain(high = starField.size - 1)
.constrain(low = 0, high = starField.size - 1)
)
.forEach {
pushMatrix()
translateCenter()
scale(zoomValue)
it.rotationExtra += audioProcessor.getRange(2500f..16000f).remap(0f, 100f, 0f, 0.2f) * it.randomFactor
rotateY(millis() * it.ySpeed + it.rotationExtra)
rotateZ(millis() * it.zSpeed)

strokeWeight(it.diameter)
strokeWeight(it.diameter * diameterFactor)
point(it.vec.x, it.vec.y, it.vec.z)
popMatrix()
}

// Black hole
pushMatrix()
translateCenter()
scale(zoomValue)
noStroke()
fill(0)
fill(bgColor)
ellipseMode(PConstants.CENTER)
beginShape()
sphere(25f)
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/dev/matsem/astral/tools/galaxy/Galaxy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class Galaxy : MidiDevice {
fun createButtonGroup(channel: Int, ccs: List<Int>, activeCCs: List<Int>) =
ButtonGroup(midiBus, channel, ccs, activeCCs).also { controls.add(it) }

fun createPushButtonGroup(channel: Int, ccs: List<Int>, listener: (Int) -> Unit) =
PushButtonGroup(midiBus, channel, ccs, listener).also { controls.add(it) }

fun createEncoder(channel: Int, cc: Int, min: Int, max: Int, initialValue: Int = 0) =
Encoder(midiBus, channel, cc, min, max, initialValue).also { controls.add(it) }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.matsem.astral.tools.galaxy.controls

import themidibus.MidiBus

class PushButtonGroup(
private val midiBus: MidiBus,
val ch: Int,
val ccs: List<Int>,
val listener: (Int) -> Unit
) : GalaxyControl {


override fun controllerChange(channel: Int, control: Int, v: Int) {
if (ch == channel && ccs.contains(control) && v == 127) {
listener(ccs.indexOf(control))
}
}

override fun update() = Unit

override fun updatePhone() = Unit
}
Binary file modified touchosc/Astral.touchosc
Binary file not shown.