Skip to content

Commit

Permalink
WIP: Tweak noise generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Danand committed Mar 12, 2024
1 parent 2a89364 commit af4358d
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.danand.juicynoise.interfaces.SignalProcessor
import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioTrack
import com.danand.juicynoise.interfaces.Effect

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -15,6 +16,7 @@ import kotlinx.coroutines.launch

class AudioOutput(
private val signalProcessor: SignalProcessor,
private val effect: Effect,
) {
private lateinit var scope: CoroutineScope

Expand Down Expand Up @@ -63,6 +65,9 @@ class AudioOutput(
floatArray[i] = value
}

// TODO: Uncomment when delay effect will be fixed.
//effect.process(floatArray, bufferSize)

audioTrack.write(floatArray, 0, bufferSize, AudioTrack.WRITE_NON_BLOCKING)

timeCurrent += bufferSize * sampleTimeStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.core.text.isDigitsOnly
import com.danand.juicynoise.effects.EffectDelay
import com.danand.juicynoise.signalprocessors.SignalProcessorSensors
import com.danand.juicynoise.ui.theme.JuicyNoiseTheme
import com.google.android.gms.location.FusedLocationProviderClient
Expand Down Expand Up @@ -148,7 +149,9 @@ class MainActivity : ComponentActivity(), SensorEventListener {
proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)

val signalProcessor = SignalProcessorSensors(sensorsState)
audioOutput = AudioOutput(signalProcessor)
val effect = EffectDelay(sensorsState)

audioOutput = AudioOutput(signalProcessor, effect)

setContent {
JuicyNoiseTheme {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.danand.juicynoise

import kotlin.random.Random

class WeightedRandomChoice<T>(private val items: List<Pair<T, Double>>) {

private val cumulativeProbabilities: List<Double> = calculateCumulativeProbabilities()

private fun calculateCumulativeProbabilities(): List<Double> {
var cumulativeProbability = 0.0
return items.map { (_, weight) ->
cumulativeProbability += weight
cumulativeProbability
}
}

fun next(): T {
val randomValue = Random.nextDouble(0.0, cumulativeProbabilities.last())
val index = cumulativeProbabilities.indexOfFirst { it >= randomValue }

return items[index].first
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.danand.juicynoise.effects

import com.danand.juicynoise.Sensors
import com.danand.juicynoise.interfaces.Effect
import com.danand.juicynoise.magnitude

import kotlin.math.max
import kotlin.math.ceil

import androidx.compose.runtime.MutableState
import kotlin.math.abs

class EffectDelay(
private val sensorsState: MutableState<Sensors>,
) : Effect {
private val buffers: ArrayList<FloatArray> = ArrayList()

private var magneticMagnitudeMax: Float = 0.0f

override fun process(buffer: FloatArray, bufferSize: Int) {
val delayMilliseconds = max(0.0f, 5000.0f - this.sensorsState.value.light)

val magneticMagnitude = magnitude(
this.sensorsState.value.magneticX,
this.sensorsState.value.magneticY,
this.sensorsState.value.magneticZ,
)

magneticMagnitudeMax = max(magneticMagnitudeMax, magneticMagnitude)

val dry = magneticMagnitude / magneticMagnitudeMax
val wet = 1 - dry

val bufferAmount = ceil(delayMilliseconds / bufferSize).toInt()

val bufferCount = this.buffers.count()

val skipBuffersCount = bufferCount - bufferAmount

if (skipBuffersCount < 0) {
for (i in 0..abs(skipBuffersCount)) {
this.buffers.add(FloatArray(bufferSize))
}
}

val lastBuffer = this.buffers[0]

for (sampleIndex in buffer.indices) {
lastBuffer[sampleIndex] = buffer[sampleIndex]
}

for (bufferIndex in this.buffers.indices) {
if (bufferIndex < skipBuffersCount) {
continue
}

if (bufferIndex < 1) {
continue
}

val bufferSaved = this.buffers[bufferIndex]

val bufferMix = bufferIndex / bufferAmount.toFloat()

for (sampleIndex in buffer.indices) {
val delayedSample = bufferSaved[sampleIndex] * bufferMix
buffer[sampleIndex] = (buffer[sampleIndex] * dry) + (delayedSample * wet)
}
}

for (bufferIndex in 0 until this.buffers.lastIndex) {
this.buffers[bufferIndex] = this.buffers[bufferIndex + 1]
}

this.buffers[this.buffers.lastIndex] = lastBuffer
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.danand.juicynoise.interfaces

interface Effect {
fun process(buffer: FloatArray, bufferSize: Int)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package com.danand.juicynoise.interfaces

interface SignalProcessor {
fun process(time: Float): Float
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ package com.danand.juicynoise.signalprocessors
import androidx.compose.runtime.MutableState

import com.danand.juicynoise.Sensors
import com.danand.juicynoise.WeightedRandomChoice
import com.danand.juicynoise.exoticWave
import com.danand.juicynoise.interfaces.SignalProcessor
import com.danand.juicynoise.magnitude
import com.danand.juicynoise.normalizeOnto
import com.danand.juicynoise.sineWave
import com.danand.juicynoise.squareWave
import com.danand.juicynoise.sawtoothWave
import kotlin.math.abs

import kotlin.math.abs
import kotlin.math.max
import kotlin.random.Random

Expand Down Expand Up @@ -58,23 +59,39 @@ class SignalProcessorSensors(private val sensorsState: MutableState<Sensors>) :
::exoticWave,
)

private var mapping: Array<Int> = Array(this.synths.count()) {
private val mapping: Array<Int> = Array(this.synths.count()) {
Random.nextInt(0, this.sensorGetters.count())
}

private val frequencyMaxToWeights = listOf(
700.0f to 0.6,
2500.0f to 0.3,
7000.0f to 0.1,
)

override fun process(time: Float): Float {
val angularSpeedXMagnitude = magnitude(
val angularSpeedMagnitude = magnitude(
this.sensorsState.value.angularSpeedX,
this.sensorsState.value.angularSpeedY,
this.sensorsState.value.angularSpeedZ,
)

if (angularSpeedXMagnitude > 10) {
if (angularSpeedMagnitude > 10) {
randomizeMapping(this.mapping) {
Random.nextInt(0, this.sensorGetters.count())
}
}

val accelerationMagnitude = magnitude(
this.sensorsState.value.accelerationX,
this.sensorsState.value.accelerationY,
this.sensorsState.value.accelerationZ,
)

var frequencyMax = 700.0f

val amplitudeMax = 0.9f

var sampleValueTotal = 0.0f

for (index in this.mapping.indices) {
Expand All @@ -83,26 +100,30 @@ class SignalProcessorSensors(private val sensorsState: MutableState<Sensors>) :

val sensorValue = sensorGetter()

if (accelerationMagnitude > 40) {
frequencyMax = WeightedRandomChoice(frequencyMaxToWeights).next()
}

val frequency = normalizeOnto(
sensorValue,
10.0f,
1000.0f,
40.0f,
700.0f,
frequencyMax,
).toInt()

val sampleValueSynth = synth(
time,
frequency,
0.9f,
amplitudeMax,
0.0f,
)

sampleValueTotal = max(sampleValueTotal, sampleValueSynth)
}

if ((this.sensorsState.value.light % this.sensorsState.value.magneticZ).toInt() % 3 == 0) {
val sampleValueRandom = Random.nextFloat()
val sampleValueRandom = Random.nextFloat() * amplitudeMax
sampleValueTotal = max(sampleValueTotal, sampleValueRandom)
}

Expand Down

0 comments on commit af4358d

Please sign in to comment.