added tetris theme
This commit is contained in:
parent
e02878f0d7
commit
e127450404
@ -1,38 +0,0 @@
|
||||
package music.Filter
|
||||
|
||||
import music.Params.SAMPLES
|
||||
import music.Signal
|
||||
|
||||
typealias Filter = (Signal) -> (Signal)
|
||||
|
||||
object Filters {
|
||||
fun easeIn(signal: Signal): Signal {
|
||||
val len = kotlin.math.min(SAMPLES / 64, signal.size)
|
||||
val start = signal.slice(0 until len)
|
||||
val end = signal.slice(len until signal.size)
|
||||
|
||||
return (start.withIndex().map { i -> (i.value * (i.index / len.toFloat())).toInt() } + end).toTypedArray();
|
||||
}
|
||||
|
||||
fun easeOut(signal: Signal): Signal {
|
||||
val len = kotlin.math.min(SAMPLES / 64, signal.size)
|
||||
val sep = signal.size - len - 1;
|
||||
val start = signal.slice(0 until sep)
|
||||
val end = signal.slice(sep until signal.size)
|
||||
|
||||
return (start + end.withIndex().map { i -> (i.value * ((len - i.index) / len.toFloat())).toInt() }).toTypedArray();
|
||||
}
|
||||
|
||||
fun ease(signal: Signal): Signal {
|
||||
val len = kotlin.math.min(SAMPLES / 64, signal.size / 2)
|
||||
val sep = signal.size - len - 1;
|
||||
val start = signal.slice(0 until len)
|
||||
val mid = signal.slice(len until sep)
|
||||
val end = signal.slice(sep until signal.size)
|
||||
|
||||
return (start.withIndex().map { i -> (i.value * (i.index / len.toFloat())).toInt() }
|
||||
+ mid
|
||||
+ end.withIndex().map { i -> (i.value * ((len - i.index) / len.toFloat())).toInt() }
|
||||
).toTypedArray();
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package music
|
||||
|
||||
import music.Filter.Filters
|
||||
import music.Params.SAMPLES
|
||||
import music.generators.ToneGenerator
|
||||
import music.filter.EchoFilter
|
||||
import music.filter.FilterQueue
|
||||
import music.filter.Limiter
|
||||
import music.generators.EasedToneGenerator
|
||||
import javax.sound.sampled.AudioFormat
|
||||
import javax.sound.sampled.AudioSystem
|
||||
import javax.sound.sampled.DataLine
|
||||
@ -11,6 +13,7 @@ import javax.sound.sampled.SourceDataLine
|
||||
object Params {
|
||||
const val SAMPLES = 44100
|
||||
const val PI = kotlin.math.PI.toFloat()
|
||||
const val MAX_VAL = 32767 // 2**15 - 1
|
||||
val CORES = Runtime.getRuntime().availableProcessors()
|
||||
}
|
||||
|
||||
@ -23,9 +26,115 @@ fun main() {
|
||||
line.open(format)
|
||||
line.start()
|
||||
|
||||
val gen = ToneGenerator(.2f, -24, WaveformTransformations::sawtooth)
|
||||
//val gen = ToneGenerator(.2f, -24, WaveformTransformation.SAWTOOTH)
|
||||
|
||||
val bpm = 60;
|
||||
val volume = 0.1f;
|
||||
val tetrisSound = { volume: Float, tone: Int -> EasedToneGenerator(volume, tone, WaveformTransformation.SQUARE) }
|
||||
|
||||
|
||||
// C C# D D# E F F# G G# A A# B C
|
||||
// C Db D Eb E F Gb G Ab A Bb B C
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12
|
||||
|
||||
// TETRIS
|
||||
// base notes
|
||||
val bar1 = Bar(8, bpm);
|
||||
|
||||
val rightHand = Melody(tetrisSound, volume/ 2, -1);
|
||||
|
||||
rightHand.add("E", 1f)
|
||||
.add("E", 1f)
|
||||
.add("A", 1f)
|
||||
.add("A", 1f)
|
||||
.add("Ab", 1f)
|
||||
.add("E", 1f)
|
||||
.add("A", 1.5f)
|
||||
.applyTo(bar1)
|
||||
|
||||
val leftHand = Melody(tetrisSound, volume, 0)
|
||||
|
||||
leftHand.add("E", .5f)
|
||||
.add(-1, .25f)
|
||||
.add("C", .25f) // 1
|
||||
.add("D", .5f)
|
||||
.add("C", .25f)
|
||||
.add(-1, .25f) // 2
|
||||
.add(-3, .25)
|
||||
.pause(.25)
|
||||
.add(-3, .25)
|
||||
.add("C", 0.25) // 3
|
||||
.add("E", .5)
|
||||
.add(2, 0.25)
|
||||
.add(0, 0.25) // 4
|
||||
.add(-1, 0.5)
|
||||
.pause(0.25)
|
||||
.add("C", 0.25) // 5
|
||||
.add("D", 0.5)
|
||||
.add("E",0.5) // 6
|
||||
.add("C", 0.5)
|
||||
.add(-3, 0.25)
|
||||
.pause(0.25) // 7
|
||||
.add(-3, 0.5)
|
||||
.applyTo(bar1)
|
||||
|
||||
|
||||
val bar2 = Bar(8, bpm);
|
||||
|
||||
rightHand.reset()
|
||||
leftHand.reset()
|
||||
|
||||
rightHand.add("D", 1f)
|
||||
.add("D", 1f)
|
||||
.add("C", 1f)
|
||||
.add("C", 1f)
|
||||
.add("E", 1f)
|
||||
.add("E", 1f)
|
||||
.add("A", 2f)
|
||||
.applyTo(bar2)
|
||||
|
||||
leftHand.pause(0.25)
|
||||
.add("D", .5)
|
||||
.add("F", .25) // 1
|
||||
.add("A", .5)
|
||||
.add("G", .25)
|
||||
.add("F", .25) // 2
|
||||
.add("E", 0.5)
|
||||
.pause(.25)
|
||||
.add("C", .25) // 3
|
||||
.add("E", .5)
|
||||
.add("D", .25)
|
||||
.add("C", .25) // 4
|
||||
.add(-1, .25)
|
||||
.pause(.25)
|
||||
.add(-1, .25)
|
||||
.add("C",.25) // 5
|
||||
.add("D", .5)
|
||||
.add("E", .5) // 6
|
||||
.add("C", .5)
|
||||
.add("A", .25, offset = -1)
|
||||
.pause(0.25) // 7
|
||||
.add("A", 0.5, offset = -1)
|
||||
.applyTo(bar2)
|
||||
// D F A G F E
|
||||
// C E D C B
|
||||
// B C D E C A A
|
||||
|
||||
|
||||
val echo = EchoFilter(SAMPLES / 12, .9f)
|
||||
val limiter = Limiter(.9f);
|
||||
|
||||
|
||||
val filters = FilterQueue(echo, limiter)
|
||||
|
||||
|
||||
//println(output.size / SAMPLES.toFloat())
|
||||
|
||||
var output = filters.stream(bar1.getSignal())
|
||||
line.write(output.toBytes(), 0, output.size * 2)
|
||||
output = filters.stream(bar2.getSignal())
|
||||
line.write(output.toBytes(), 0, output.size * 2)
|
||||
|
||||
|
||||
val signal = Filters.ease(gen.get(Samples(2)))
|
||||
|
||||
line.write(signal.toBytes(), 0, signal.size * 2)
|
||||
}
|
||||
|
71
src/main/kotlin/music/Melody.kt
Normal file
71
src/main/kotlin/music/Melody.kt
Normal file
@ -0,0 +1,71 @@
|
||||
package music
|
||||
|
||||
import music.generators.Generator
|
||||
import music.generators.ToneGenerator
|
||||
|
||||
class Melody(val getGen: (volume: Float, tone: Int) -> (Generator), val volume: Float, val offset: Int = 0) {
|
||||
|
||||
val tones = mutableSetOf<Beat>()
|
||||
var pos = 0f
|
||||
|
||||
fun add(tone: String, len: Double, offset: Int? = null): Melody {
|
||||
return add(tone, len.toFloat(), offset)
|
||||
}
|
||||
fun add(tone: Int, len: Double, offset: Int? = null): Melody {
|
||||
return add(tone, len.toFloat(), offset)
|
||||
}
|
||||
fun add(tone: String, len: Float, offset: Int? = null): Melody {
|
||||
return add(toneFromString(tone), len, offset)
|
||||
}
|
||||
fun add(tone: Int, len: Float, offset: Int? = null): Melody {
|
||||
val ofs = offset ?: this.offset;
|
||||
tones.add(Beat(getGen(volume, tone + (ofs * 12)), len, pos))
|
||||
pos += len
|
||||
return this
|
||||
}
|
||||
|
||||
fun pause(len: Float): Melody {
|
||||
pos += len
|
||||
return this
|
||||
}
|
||||
fun pause(len: Double): Melody {
|
||||
pos += len.toFloat()
|
||||
return this
|
||||
}
|
||||
|
||||
fun applyTo(bar: Bar) {
|
||||
for (tone in tones) {
|
||||
bar.tones.add(tone)
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
pos = 0f
|
||||
tones.removeAll { true }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// C C# D D# E F F# G G# A A# B C
|
||||
// C Db D Eb E F Gb G Ab A Bb B C
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12
|
||||
private val tones = mapOf(
|
||||
"C" to 0,
|
||||
"Db" to 1, "C#" to 1,
|
||||
"D" to 2,
|
||||
"D#" to 3, "Eb" to 3,
|
||||
"E" to 4,
|
||||
"F" to 5,
|
||||
"F#" to 6, "Gb" to 6,
|
||||
"G" to 7,
|
||||
"G#" to 8, "Ab" to 8,
|
||||
"A" to 9,
|
||||
"A#" to 10, "Bb" to 10,
|
||||
"B" to 11
|
||||
)
|
||||
|
||||
fun toneFromString(tone: String, offset: Int = 0): Int {
|
||||
return Companion.tones[tone]?.plus(offset * 12)!!
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
package music
|
||||
|
||||
import music.Params.SAMPLES
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
typealias Samples = Array<Float>
|
||||
|
||||
fun Samples(len: Int, start: Int = 0): Samples =
|
||||
Samples(len * SAMPLES) { i -> start.toFloat() + (i / SAMPLES.toFloat())}
|
||||
fun Samples(len: Float, start: Float = 0f): Samples =
|
||||
Samples((len * SAMPLES).toInt()) { i -> start + (i / SAMPLES.toFloat())}
|
||||
|
||||
fun Samples(len: Int, start: Float = 0f): Samples = Samples(len.toFloat(), start)
|
||||
|
||||
fun Samples.map(mapper: (Float) -> (Float)): Samples =
|
||||
Array(size) { mapper(this[it]) }
|
||||
@ -17,7 +20,10 @@ fun Samples.sin(): Samples =
|
||||
map { kotlin.math.sin(it) }
|
||||
|
||||
fun Samples.mod(num: Int): Samples =
|
||||
map { it % num }
|
||||
map { it % num }
|
||||
|
||||
fun Samples.abs(): Samples =
|
||||
map { it.absoluteValue }
|
||||
|
||||
operator fun Samples.times(scalar: Float): Samples =
|
||||
if (scalar == 1f) this else map { it * scalar }
|
||||
|
@ -1,31 +1,58 @@
|
||||
package music
|
||||
|
||||
import music.Params.MAX_VAL
|
||||
import music.Params.PI
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
/**
|
||||
* A Signal is string of Integer values which describe a curve, they go from - MAX_VAL to MAX_VAL
|
||||
*/
|
||||
typealias Signal = Array<Int>
|
||||
|
||||
val MAX_VAL = 2f.pow(15) - 1
|
||||
|
||||
fun Signal.toBytes(): ByteArray =
|
||||
Array(size * 2) { i -> (this[i/2] shr ((i % 2) * 8)).toByte() }.toByteArray()
|
||||
|
||||
fun Signal(samples: Samples): Signal =
|
||||
Signal(samples.size) {i -> (samples[i] * MAX_VAL).toInt() }
|
||||
|
||||
object WaveformTransformations {
|
||||
fun sine(samples: Samples, volume: Float): Signal
|
||||
= Signal(((samples * (2f * PI)).sin()) * volume)
|
||||
enum class WaveformTransformation {
|
||||
SINE {
|
||||
override fun apply(samples: Samples, volume: Float): Signal
|
||||
= Signal(((samples * (2f * PI)).sin()) * volume)
|
||||
},
|
||||
SQUARE {
|
||||
override fun apply(samples: Samples, volume: Float): Signal
|
||||
= Signal(samples.times(2f).ceil().mod(2).times(2f).minus(1f) * (volume))
|
||||
},
|
||||
SAWTOOTH {
|
||||
override fun apply(samples: Samples, volume: Float): Signal
|
||||
= Signal(((samples * 2f).mod(2) - 1f) * volume)
|
||||
},
|
||||
TRIANGLE {
|
||||
override fun apply(samples: Samples, volume: Float): Signal
|
||||
= Signal(samples.times(4f).mod(4).minus(2f).abs().minus(1f) * (volume))
|
||||
};
|
||||
|
||||
fun square(samples: Samples, volume: Float): Signal
|
||||
= Signal((((samples * (2f * PI)).sin() / 1f).ceil() * 2f + 1f) * volume)
|
||||
|
||||
fun sawtooth(samples: Samples, volume: Float): Signal
|
||||
= Signal(((samples + 1f).mod(2) - 1f) * volume)
|
||||
open fun apply(samples: Samples, volume: Float): Signal = Signal(samples * volume)
|
||||
}
|
||||
|
||||
typealias WaveformTransformation = (Samples, Float) -> (Signal)
|
||||
fun Signal.map(mapper: (Int) -> (Int)): Signal =
|
||||
Array(size) { mapper(this[it]) }
|
||||
|
||||
operator fun Signal.times(scalar: Float): Signal =
|
||||
if (scalar == 1f) this else map { (it * scalar).toInt() }
|
||||
|
||||
operator fun Signal.plus(value: Int): Signal =
|
||||
map { it + value }
|
||||
|
||||
operator fun Signal.minus(value: Int): Signal =
|
||||
map { it - value }
|
||||
|
||||
operator fun Signal.div(value: Float): Signal =
|
||||
map { (it / value).toInt() }
|
||||
|
||||
|
||||
fun Signal.join(start: Int, signal: Signal): Signal
|
||||
= (this.sliceArray(0 until start)
|
||||
+ signal.withIndex().map { i -> this.elementAtOrElse(i.index + start) {0} + i.value }
|
||||
+ this.sliceArray(start + signal.size until this.size))
|
||||
|
||||
|
@ -1,5 +1,35 @@
|
||||
package music
|
||||
|
||||
import music.Params.SAMPLES
|
||||
import music.filter.EaseFilter
|
||||
import music.filter.FilterQueue
|
||||
import music.generators.Generator
|
||||
import java.util.logging.Filter
|
||||
|
||||
|
||||
class Track {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Bar(val beats: Int, val bpm: Int) {
|
||||
|
||||
val bps = bpm / 60f
|
||||
val tones = mutableSetOf<Beat>()
|
||||
|
||||
fun getSignal():Signal {
|
||||
var base = Signal((SAMPLES * beats / bps).toInt()) {0}
|
||||
for (tone in tones) {
|
||||
base = base.join((SAMPLES * tone.start / bps).toInt(), tone.getSignal(bps))
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
class Beat(private val generator: Generator, private val duration: Float, val start: Float) {
|
||||
fun getSignal(bps: Float): Signal =
|
||||
generator.get(Samples(duration / bps, start / bps))
|
||||
}
|
10
src/main/kotlin/music/filter/Compressor.kt
Normal file
10
src/main/kotlin/music/filter/Compressor.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package music.filter
|
||||
|
||||
import music.Signal
|
||||
|
||||
class Compressor(val threshold: Int, val ratio: Float, val outputGain: Float):Filter {
|
||||
|
||||
override fun stream(signal: Signal): Signal {
|
||||
return signal;
|
||||
}
|
||||
}
|
24
src/main/kotlin/music/filter/EchoFilter.kt
Normal file
24
src/main/kotlin/music/filter/EchoFilter.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package music.filter
|
||||
|
||||
import music.Signal
|
||||
import music.map
|
||||
import music.div
|
||||
import music.times
|
||||
import java.util.*
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
|
||||
class EchoFilter(delay: Int, private val volume: Float = 0.5f): Filter {
|
||||
private var cache: Queue<Int> = LinkedList(List(delay) {0})
|
||||
|
||||
override fun stream(signal: Signal): Signal =
|
||||
signal.map(this::apply)
|
||||
|
||||
private fun apply(value: Int): Int {
|
||||
val effect = value + cache.remove()
|
||||
cache.add(effect.toFloat().absoluteValue.pow(volume).toInt() * effect.sign)
|
||||
return effect;
|
||||
}
|
||||
|
||||
}
|
53
src/main/kotlin/music/filter/Filter.kt
Normal file
53
src/main/kotlin/music/filter/Filter.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package music.filter
|
||||
|
||||
import music.Signal
|
||||
|
||||
interface Filter {
|
||||
fun stream(signal: Signal): Signal
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum class EaseFilter(val duration: Int = 32): Filter {
|
||||
IN {
|
||||
override fun stream (signal: Signal): Signal {
|
||||
val len = kotlin.math.min(duration, signal.size)
|
||||
val start = signal.slice(0 until len)
|
||||
val end = signal.slice(len until signal.size)
|
||||
|
||||
return (start.withIndex().map { i -> (i.value * (i.index / len.toFloat())).toInt() } + end).toTypedArray();
|
||||
}
|
||||
},
|
||||
|
||||
OUT {
|
||||
override fun stream(signal: Signal): Signal {
|
||||
val len = kotlin.math.min(duration, signal.size)
|
||||
val sep = signal.size - len - 1;
|
||||
val start = signal.slice(0 until sep)
|
||||
val end = signal.slice(sep until signal.size)
|
||||
|
||||
return (start + end.withIndex().map { i -> (i.value * ((len - i.index) / len.toFloat())).toInt() }).toTypedArray();
|
||||
}
|
||||
},
|
||||
|
||||
INOUT {
|
||||
override fun stream(signal: Signal): Signal {
|
||||
val len = kotlin.math.min(duration, signal.size / 2)
|
||||
val sep = signal.size - len - 1;
|
||||
val start = signal.slice(0 until len)
|
||||
val mid = signal.slice(len until sep)
|
||||
val end = signal.slice(sep until signal.size)
|
||||
|
||||
return (start.withIndex().map { i -> (i.value * (i.index / len.toFloat())).toInt() }
|
||||
+ mid
|
||||
+ end.withIndex().map { i -> (i.value * ((len - i.index) / len.toFloat())).toInt() }
|
||||
).toTypedArray();
|
||||
}
|
||||
};
|
||||
|
||||
override fun stream(signal: Signal): Signal = signal
|
||||
}
|
||||
|
8
src/main/kotlin/music/filter/FilterQueue.kt
Normal file
8
src/main/kotlin/music/filter/FilterQueue.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package music.filter
|
||||
|
||||
import music.Signal
|
||||
|
||||
class FilterQueue(private vararg val filters: Filter): Filter {
|
||||
override fun stream(signal: Signal): Signal =
|
||||
filters.fold(signal, {acc, filter -> filter.stream(acc)})
|
||||
}
|
14
src/main/kotlin/music/filter/Limiter.kt
Normal file
14
src/main/kotlin/music/filter/Limiter.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package music.filter
|
||||
|
||||
import music.Params.MAX_VAL
|
||||
import music.Signal
|
||||
import music.map
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class Limiter(val threshold: Float): Filter {
|
||||
val maxVal = (MAX_VAL * threshold).toInt()
|
||||
|
||||
override fun stream(signal: Signal): Signal =
|
||||
signal.map { max(min(it, maxVal), - maxVal) }
|
||||
}
|
22
src/main/kotlin/music/generators/Drum.kt
Normal file
22
src/main/kotlin/music/generators/Drum.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package music.generators
|
||||
|
||||
import music.*
|
||||
import music.Params.SAMPLES
|
||||
import music.filter.EchoFilter
|
||||
import music.filter.FilterQueue
|
||||
import kotlin.math.max
|
||||
|
||||
class Drum(volume: Float, tone: Int = -36): Generator{
|
||||
val kickLen = (SAMPLES * 0.08).toInt();
|
||||
val filter = FilterQueue(EchoFilter(kickLen, .7f));
|
||||
|
||||
val gen1 = EasedToneGenerator(volume, tone, WaveformTransformation.SINE)
|
||||
val gen2 = EasedToneGenerator(volume / 2, tone + 12, WaveformTransformation.SINE)
|
||||
|
||||
override fun get(samples: Samples): Signal {
|
||||
val kick1 = gen1.get(samples.sliceArray(0 until kickLen))
|
||||
val kick2 = gen2.get(samples.sliceArray(0 until (kickLen / 2)))
|
||||
|
||||
return filter.stream(kick1.join(0, kick2) + Signal(max(samples.size - kickLen, 0)) {0}) / 1.5f;
|
||||
}
|
||||
}
|
@ -4,12 +4,8 @@ import music.Samples
|
||||
import music.Signal
|
||||
import music.WaveformTransformation
|
||||
|
||||
abstract class Generator(private val volume: Float, private val waveform: WaveformTransformation) {
|
||||
|
||||
fun get(samples: Samples): Signal {
|
||||
return waveform(generate(samples), volume)
|
||||
}
|
||||
|
||||
protected abstract fun generate(samples: Samples): Samples
|
||||
interface Generator {
|
||||
fun get(samples: Samples): Signal
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,11 +3,19 @@ package music.generators
|
||||
import music.Samples
|
||||
import music.Signal
|
||||
import music.WaveformTransformation
|
||||
import music.filter.EaseFilter
|
||||
import music.times
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
|
||||
open class ToneGenerator(volume: Float, private val tone: Int, waveform: WaveformTransformation) : Generator(volume, waveform) {
|
||||
override fun generate(samples: Samples): Samples =
|
||||
open class ToneGenerator(private val volume: Float, private val tone: Int, private val waveform: WaveformTransformation) : Generator {
|
||||
override fun get(samples: Samples): Signal {
|
||||
return applyEffects(waveform.apply(generate(samples), volume))
|
||||
}
|
||||
|
||||
protected open fun applyEffects(signal: Signal): Signal = signal
|
||||
|
||||
protected open fun generate(samples: Samples): Samples =
|
||||
samples * getFreqOfTone(tone)
|
||||
|
||||
protected fun getFreqOfTone(tone: Float): Float =
|
||||
@ -19,4 +27,9 @@ open class ToneGenerator(volume: Float, private val tone: Int, waveform: Wavefor
|
||||
companion object {
|
||||
private const val F0: Float = 440f;
|
||||
}
|
||||
}
|
||||
|
||||
class EasedToneGenerator(volume: Float, tone: Int, waveform: WaveformTransformation) : ToneGenerator(volume, tone, waveform) {
|
||||
override fun applyEffects(signal: Signal): Signal =
|
||||
EaseFilter.INOUT.stream(signal)
|
||||
}
|
25
src/test/kotlin/music/filter/EaseFilterTest.kt
Normal file
25
src/test/kotlin/music/filter/EaseFilterTest.kt
Normal file
@ -0,0 +1,25 @@
|
||||
package music.filter
|
||||
|
||||
import music.Samples
|
||||
import music.WaveformTransformation
|
||||
import music.generators.ToneGenerator
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class EaseFilterTest {
|
||||
@Test
|
||||
fun testOutputLength() {
|
||||
val gen = ToneGenerator(1f, 0, WaveformTransformation.SINE);
|
||||
val samples = Samples(1)
|
||||
val signal = gen.get(samples)
|
||||
|
||||
|
||||
assertEquals(samples.size, signal.size, "Generated samples should have same size as input")
|
||||
|
||||
for (filter in EaseFilter.values()) {
|
||||
assertEquals(samples.size, filter.stream(signal).size, "Filter $filter should not increase length")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user