2014-07-01 03:24:29 +02:00
|
|
|
module TestAudioIONodes
|
|
|
|
|
|
|
|
using FactCheck
|
2014-01-03 16:41:00 +01:00
|
|
|
using AudioIO
|
2014-06-23 08:10:35 +02:00
|
|
|
import AudioIO.AudioSample
|
|
|
|
import AudioIO.AudioBuf
|
|
|
|
import AudioIO.AudioRenderer
|
|
|
|
import AudioIO.AudioNode
|
|
|
|
import AudioIO.DeviceInfo
|
|
|
|
import AudioIO.render
|
2013-12-23 00:06:02 +01:00
|
|
|
|
2014-07-01 03:24:29 +02:00
|
|
|
include("testhelpers.jl")
|
|
|
|
|
2013-12-23 00:06:02 +01:00
|
|
|
# A TestNode just renders out 1:buf_size each frame
|
2014-06-26 05:01:27 +02:00
|
|
|
type TestRenderer <: AudioRenderer
|
|
|
|
buf::AudioBuf
|
|
|
|
TestRenderer(buf_size::Integer) = new(AudioSample[1:buf_size])
|
|
|
|
end
|
2014-01-06 04:50:56 +01:00
|
|
|
|
2014-06-23 08:10:35 +02:00
|
|
|
typealias TestNode AudioNode{TestRenderer}
|
2014-06-26 05:01:27 +02:00
|
|
|
TestNode(buf_size) = TestNode(TestRenderer(buf_size))
|
2013-12-23 00:06:02 +01:00
|
|
|
|
2014-06-23 08:10:35 +02:00
|
|
|
function render(node::TestRenderer,
|
|
|
|
device_input::AudioBuf,
|
|
|
|
info::DeviceInfo)
|
2014-06-26 05:01:27 +02:00
|
|
|
return node.buf
|
2013-12-23 00:06:02 +01:00
|
|
|
end
|
|
|
|
|
2014-06-26 03:51:16 +02:00
|
|
|
test_info = DeviceInfo(44100, 512)
|
|
|
|
dev_input = zeros(AudioSample, test_info.buf_size)
|
|
|
|
|
2014-07-01 03:24:29 +02:00
|
|
|
facts("Validating TestNode allocation") do
|
|
|
|
# first validate that the TestNode doesn't allocate so it doesn't mess up our
|
|
|
|
# other tests
|
|
|
|
test = TestNode(test_info.buf_size)
|
|
|
|
# JIT
|
|
|
|
render(test, dev_input, test_info)
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(test, dev_input, test_info)) => 16
|
2014-07-01 03:24:29 +02:00
|
|
|
end
|
2014-06-26 05:01:27 +02:00
|
|
|
|
2013-12-23 00:06:02 +01:00
|
|
|
#### AudioMixer Tests ####
|
|
|
|
|
|
|
|
# TODO: there should be a setup/teardown mechanism and some way to isolate
|
|
|
|
# tests
|
|
|
|
|
2014-07-01 03:24:29 +02:00
|
|
|
facts("AudioMixer") do
|
|
|
|
context("0 Input Mixer") do
|
|
|
|
mix = AudioMixer()
|
|
|
|
render_output = render(mix, dev_input, test_info)
|
|
|
|
@fact render_output => AudioSample[]
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(mix, dev_input, test_info)) => 48
|
2014-07-01 03:24:29 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
context("1 Input Mixer") do
|
|
|
|
testnode = TestNode(test_info.buf_size)
|
|
|
|
mix = AudioMixer([testnode])
|
|
|
|
render_output = render(mix, dev_input, test_info)
|
|
|
|
@fact render_output => AudioSample[1:test_info.buf_size]
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(mix, dev_input, test_info)) => 64
|
2014-07-01 03:24:29 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
context("2 Input Mixer") do
|
|
|
|
test1 = TestNode(test_info.buf_size)
|
|
|
|
test2 = TestNode(test_info.buf_size)
|
|
|
|
mix = AudioMixer([test1, test2])
|
|
|
|
render_output = render(mix, dev_input, test_info)
|
|
|
|
# make sure the two inputs are being added together
|
|
|
|
@fact render_output => 2 * AudioSample[1:test_info.buf_size]
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(mix, dev_input, test_info)) => 96
|
2014-07-01 03:24:29 +02:00
|
|
|
# now we'll stop one of the inputs and make sure it gets removed
|
|
|
|
stop(test1)
|
|
|
|
render_output = render(mix, dev_input, test_info)
|
|
|
|
# make sure the two inputs are being added together
|
|
|
|
@fact render_output => AudioSample[1:test_info.buf_size]
|
|
|
|
|
|
|
|
stop(mix)
|
|
|
|
render_output = render(mix, dev_input, test_info)
|
|
|
|
@fact render_output => AudioSample[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
MSE_THRESH = 1e-7
|
|
|
|
|
|
|
|
facts("SinOSC") do
|
|
|
|
freq = 440
|
|
|
|
# note that this range includes the end, which is why there are
|
|
|
|
# sample_rate+1 samples
|
|
|
|
t = linspace(0, 1, test_info.sample_rate+1)
|
|
|
|
test_vect = convert(AudioBuf, sin(2pi * t * freq))
|
|
|
|
context("Fixed Frequency") do
|
|
|
|
osc = SinOsc(freq)
|
|
|
|
render_output = render(osc, dev_input, test_info)
|
|
|
|
@fact mse(render_output, test_vect[1:test_info.buf_size]) =>
|
|
|
|
lessthan(MSE_THRESH)
|
|
|
|
render_output = render(osc, dev_input, test_info)
|
|
|
|
@fact mse(render_output,
|
|
|
|
test_vect[test_info.buf_size+1:2*test_info.buf_size]) =>
|
|
|
|
lessthan(MSE_THRESH)
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(osc, dev_input, test_info)) => 176
|
2014-07-01 03:24:29 +02:00
|
|
|
stop(osc)
|
|
|
|
render_output = render(osc, dev_input, test_info)
|
|
|
|
@fact render_output => AudioSample[]
|
|
|
|
end
|
|
|
|
|
|
|
|
context("Testing SinOsc with signal input") do
|
|
|
|
t = linspace(0, 1, test_info.sample_rate+1)
|
|
|
|
f = 440 .- t .* (440-110)
|
|
|
|
dt = 1 / test_info.sample_rate
|
|
|
|
# NOTE - this treats the phase as constant at each sample, which isn't strictly
|
|
|
|
# true. Unfortunately doing this correctly requires knowing more about the
|
|
|
|
# modulating signal and doing the real integral
|
|
|
|
phase = cumsum(2pi * dt .* f)
|
|
|
|
unshift!(phase, 0)
|
|
|
|
expected = convert(AudioBuf, sin(phase))
|
|
|
|
|
|
|
|
freq = LinRamp(440, 110, 1)
|
|
|
|
osc = SinOsc(freq)
|
|
|
|
render_output = render(osc, dev_input, test_info)
|
|
|
|
@fact mse(render_output, expected[1:test_info.buf_size]) =>
|
|
|
|
lessthan(MSE_THRESH)
|
|
|
|
render_output = render(osc, dev_input, test_info)
|
|
|
|
@fact mse(render_output,
|
|
|
|
expected[test_info.buf_size+1:2*test_info.buf_size]) =>
|
|
|
|
lessthan(MSE_THRESH)
|
|
|
|
# give a bigger budget here because we're rendering 2 nodes
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(osc, dev_input, test_info)) => 448
|
2014-07-01 03:24:29 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
facts("ArrayPlayer") do
|
|
|
|
context("playing long sample") do
|
|
|
|
v = rand(AudioSample, 44100)
|
|
|
|
player = ArrayPlayer(v)
|
|
|
|
render_output = render(player, dev_input, test_info)
|
|
|
|
@fact render_output => v[1:test_info.buf_size]
|
|
|
|
render_output = render(player, dev_input, test_info)
|
|
|
|
@fact render_output => v[(test_info.buf_size + 1) : (2*test_info.buf_size)]
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(player, dev_input, test_info)) => 192
|
2014-07-01 03:24:29 +02:00
|
|
|
stop(player)
|
|
|
|
render_output = render(player, dev_input, test_info)
|
|
|
|
@fact render_output => AudioSample[]
|
|
|
|
end
|
|
|
|
|
|
|
|
context("testing end of vector") do
|
|
|
|
# give a vector just a bit larger than 1 buffer size
|
|
|
|
v = rand(AudioSample, test_info.buf_size + 1)
|
|
|
|
player = ArrayPlayer(v)
|
|
|
|
render(player, dev_input, test_info)
|
|
|
|
render_output = render(player, dev_input, test_info)
|
|
|
|
@fact render_output => v[test_info.buf_size+1:end]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
facts("Gain") do
|
2014-07-01 08:06:30 +02:00
|
|
|
context("Constant Gain") do
|
|
|
|
gained = TestNode(test_info.buf_size) * 0.75
|
|
|
|
render_output = render(gained, dev_input, test_info)
|
|
|
|
@fact render_output => 0.75 * AudioSample[1:test_info.buf_size]
|
|
|
|
@fact (@allocated render(gained, dev_input, test_info)) => 32
|
|
|
|
end
|
|
|
|
context("Gain by a Signal") do
|
|
|
|
gained = TestNode(test_info.buf_size) * TestNode(test_info.buf_size)
|
|
|
|
render_output = render(gained, dev_input, test_info)
|
|
|
|
@fact render_output => AudioSample[1:test_info.buf_size] .* AudioSample[1:test_info.buf_size]
|
|
|
|
@fact (@allocated render(gained, dev_input, test_info)) => 48
|
|
|
|
end
|
2014-07-01 03:24:29 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
facts("LinRamp") do
|
|
|
|
ramp = LinRamp(0.25, 0.80, 1)
|
|
|
|
expected = convert(AudioBuf, linspace(0.25, 0.80, test_info.sample_rate+1))
|
|
|
|
render_output = render(ramp, dev_input, test_info)
|
|
|
|
@fact mse(render_output, expected[1:test_info.buf_size]) =>
|
|
|
|
lessthan(MSE_THRESH)
|
|
|
|
render_output = render(ramp, dev_input, test_info)
|
|
|
|
@fact mse(render_output,
|
|
|
|
expected[(test_info.buf_size+1):(2*test_info.buf_size)]) =>
|
|
|
|
lessthan(MSE_THRESH)
|
2014-07-01 08:06:30 +02:00
|
|
|
@fact (@allocated render(ramp, dev_input, test_info)) => 256
|
2014-07-01 03:24:29 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
end # module TestAudioIONodes
|