From 65165379859ba2faf2c5e6462115f873fc8a256b Mon Sep 17 00:00:00 2001 From: Spencer Russell Date: Mon, 30 Dec 2013 03:29:43 -0800 Subject: [PATCH] adds test for SinOsc and updates README --- README.md | 64 ++++++++++++++++++++++++++++++++++++++---- src/PortAudio.jl | 2 +- src/nodes.jl | 2 ++ test/test_PortAudio.jl | 9 ++++++ test/test_nodes.jl | 9 ++++++ 5 files changed, 79 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4c58932..8e556dc 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,68 @@ PortAudio.jl [![Build Status](https://travis-ci.org/ssfrr/PortAudio.jl.png)](https://travis-ci.org/ssfrr/PortAudio.jl) -This is a Julia interface to PortAudio. It is currently under heavy development -and certainly not even close to being useful. +This is a Julia interface to PortAudio. It is currently under heavy +development. The API could change, there will be bugs, there are important +missing features. If you want to try it anyways, from your julia console: julia> Pkg.clone("https://github.com/ssfrr/PortAudio.jl.git") julia> Pkg.build("PortAudio") -Right now you can just play and stop some sin tones in your speakers: +Basic Array Playback +-------------------- - julia> using PortAudio - julia> play_sin() - julia> stop_sin() +Arrays in various formats can be played through your soundcard. Currently the +native format that is delivered to the PortAudio backend is `Float32` in the +range of `[-1, 1]`. Arrays in other sizes of float are `convert`ed. Arrays +in Signed or Unsigned Integer types are scaled so that the full range is +mapped to `[-1, 1]` floating point values. + +To play a 1-second burst of noise: + + julia> v = rand(44100) * 0.1 + julia> play(v) + +AudioNodes +---------- + +In addition to the basic `play` function you can create more complex networks +of `AudioNode`s in a render chain. In fact, when using the basic `play` to play +an Array, behind the scenes an instance of the `ArrayPlayer` type is created +and added to the master `AudioMixer` inputs. + +To explictly do the same as above: + + julia> v = rand(44100) * 0.1 + julia> player = ArrayPlayer(v) + julia> play(player) + +To generate 2 sin tones: + + julia> osc1 = SinOsc(440) + julia> osc2 = SinOsc(660) + julia> play(osc1) + julia> play(osc2) + +All AudioNodes should implement a `render` function that can be called to +retreive the next block of audio. + +AudioStreams +------------ + +AudioStreams represent a destination for audio, such as the sound card. The +`play` function attaches AudioNodes to the default stream unless a stream is +given as the 2nd argument. + +AudioStream is an abstract type, which currently has a PortAudioStream subtype +that writes to the sound card, and a TestAudioStream that is used in the unit +tests. + +Currently only 1 stream at a time is supported so there's no reason to provide +an explicit stream to the `play` function. The stream has a root mixer field +which is an instance of the `AudioMixer` type, so that multiple `AudioNode`s +can be heard at the same time. Whenever a new frame of audio is needed by the +sound card, the stream calls the `render` method on the root audio mixer, which +will in turn call the `render` methods on any input `AudioNode`s that are set +up as inputs. diff --git a/src/PortAudio.jl b/src/PortAudio.jl index e670212..dd1420a 100644 --- a/src/PortAudio.jl +++ b/src/PortAudio.jl @@ -1,7 +1,7 @@ module PortAudio +# export the basic API export play -export SinOsc, AudioMixer, ArrayPlayer, AudioInput typealias PaTime Cdouble typealias PaError Cint diff --git a/src/nodes.jl b/src/nodes.jl index 3bf41cd..7706dd7 100644 --- a/src/nodes.jl +++ b/src/nodes.jl @@ -1,3 +1,5 @@ +export SinOsc, AudioMixer, ArrayPlayer, AudioInput + #### SinOsc #### # Generates a sin tone at the given frequency diff --git a/test/test_PortAudio.jl b/test/test_PortAudio.jl index c6031bc..ff20b07 100644 --- a/test/test_PortAudio.jl +++ b/test/test_PortAudio.jl @@ -59,3 +59,12 @@ test_stream = TestAudioStream() player = play(ui8, test_stream) @test_approx_eq(process(test_stream)[1:255], convert(PortAudio.AudioBuf, linspace(-1.0, 1.0, 255))) + + +#info("Testing AudioNode Stopping...") +#test_stream = TestAudioStream() +#node = SinOsc(440) +#play(node, test_stream) +#process(test_stream) +#stop(node) +#@test process(test_stream) == zeros(PortAudio.AudioSample, TEST_BUF_SIZE) diff --git a/test/test_nodes.jl b/test/test_nodes.jl index 6fe55e5..3339761 100644 --- a/test/test_nodes.jl +++ b/test/test_nodes.jl @@ -34,3 +34,12 @@ test2 = TestNode() mix = AudioMixer([test1, test2]) @test PortAudio.render(mix, dev_input, test_info) == 2 * PortAudio.AudioSample[1:test_info.buf_size] +info("Testing SinOSC...") +freq = 440 +t = linspace(1 / test_info.sample_rate, + test_info.buf_size / test_info.sample_rate, + test_info.buf_size) +test_vect = convert(PortAudio.AudioBuf, sin(2pi * t * freq)) +osc = SinOsc(freq) +rendered = PortAudio.render(osc, dev_input, test_info) +@test_approx_eq(rendered, test_vect)