removes flac test as it was broken on osx, adds get_audio_devices

This commit is contained in:
Spencer Russell 2014-05-02 16:10:54 -04:00
parent 81a8503c1d
commit 0d84cb409b
5 changed files with 147 additions and 94 deletions

37
deps/src/shim.c vendored
View file

@ -3,11 +3,11 @@
#include <stdio.h>
#include <unistd.h>
static int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
static PaStream *AudioStream;
static int JuliaPipeReadFD = 0;
@ -61,33 +61,16 @@ PaError open_stream(unsigned int sampleRate, unsigned int bufSize)
return paNoError;
}
//PaError stop_sin(void)
//{
// PaError err;
// err = Pa_StopStream(sin_stream);
// if(err != paNoError)
// {
// return err;
// }
//
// err = Pa_CloseStream(sin_stream);
// if( err != paNoError )
// {
// return err;
// }
// return paNoError;
//}
/*
* This routine will be called by the PortAudio engine when audio is needed.
* It may called at interrupt level on some machines so don't do anything that
* could mess up the system like calling malloc() or free().
*/
static int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
unsigned int i;
unsigned char fd_data = 0;

View file

@ -1,7 +1,7 @@
module AudioIO
# export the basic API
export play, stop
export play, stop, get_audio_devices
# default stream used when none is given
_stream = nothing
@ -47,34 +47,6 @@ function play(node::AudioNode)
play(node, _stream)
end
# Allow users to play a raw array by wrapping it in an ArrayPlayer
function play(arr::AudioBuf, args...)
player = ArrayPlayer(arr)
play(player, args...)
end
# If the array is the wrong floating type, convert it
function play{T <: FloatingPoint}(arr::Array{T}, args...)
arr = convert(AudioBuf, arr)
play(arr, args...)
end
# If the array is an integer type, scale to [-1, 1] floating point
# integer audio can be slightly (by 1) more negative than positive,
# so we just scale so that +/- typemax(T) becomes +/- 1
function play{T <: Signed}(arr::Array{T}, args...)
arr = arr / typemax(T)
play(arr, args...)
end
function play{T <: Unsigned}(arr::Array{T}, args...)
zero = (typemax(T) + 1) / 2
range = floor(typemax(T) / 2)
arr = (arr - zero) / range
play(arr, args...)
end
function stop(node::AudioNode)
deactivate(node)
node
@ -99,4 +71,8 @@ function Base.wait(node::AudioNode)
end
end
function get_audio_devices()
return get_portaudio_devices()
end
end # module AudioIO

View file

@ -17,7 +17,7 @@ end
function render(node::SinOsc, device_input::AudioBuf, info::DeviceInfo)
phase = AudioSample[1:info.buf_size] * 2pi * node.freq / info.sample_rate
phase += node.phase
phase .+= node.phase
node.phase = phase[end]
return sin(phase), is_active(node)
end
@ -122,6 +122,34 @@ function render(node::ArrayPlayer, device_input::AudioBuf, info::DeviceInfo)
return output, is_active(node)
end
# Allow users to play a raw array by wrapping it in an ArrayPlayer
function play(arr::AudioBuf, args...)
player = ArrayPlayer(arr)
play(player, args...)
end
# If the array is the wrong floating type, convert it
function play{T <: FloatingPoint}(arr::Array{T}, args...)
arr = convert(AudioBuf, arr)
play(arr, args...)
end
# If the array is an integer type, scale to [-1, 1] floating point
# integer audio can be slightly (by 1) more negative than positive,
# so we just scale so that +/- typemax(T) becomes +/- 1
function play{T <: Signed}(arr::Array{T}, args...)
arr = arr / typemax(T)
play(arr, args...)
end
function play{T <: Unsigned}(arr::Array{T}, args...)
zero = (typemax(T) + 1) / 2
range = floor(typemax(T) / 2)
arr = (arr .- zero) / range
play(arr, args...)
end
#### AudioInput ####
# Renders incoming audio input from the hardware

View file

@ -2,11 +2,33 @@ typealias PaTime Cdouble
typealias PaError Cint
typealias PaSampleFormat Culong
typealias PaStream Void
typealias PaDeviceIndex Cint
typealias PaHostApiIndex Cint
typealias PaTime Cdouble
typealias PaHostApiTypeId Cint
const PA_NO_ERROR = 0
const libportaudio_shim = find_library(["libportaudio_shim",],
[Pkg.dir("AudioIO", "deps", "usr", "lib"),])
# PaHostApiTypeId values
const pa_host_api_names = {
0 => "In Development", # use while developing support for a new host API
1 => "Direct Sound",
2 => "MME",
3 => "ASIO",
4 => "Sound Manager",
5 => "Core Audio",
7 => "OSS",
8 => "ALSA",
9 => "AL",
10 => "BeOS",
11 => "WDMKS",
12 => "Jack",
13 => "WASAPI",
14 => "AudioScience HPI"
}
# track whether we've already inited PortAudio
portaudio_inited = false
@ -17,15 +39,7 @@ type PortAudioStream <: AudioStream
info::DeviceInfo
function PortAudioStream(sample_rate::Int=44100, buf_size::Int=1024)
global portaudio_inited
if !portaudio_inited
@assert(libportaudio_shim != "", "Failed to find required library libportaudio_shim. Try re-running the package script using Pkg.build(\"AudioIO\"), then reloading with reload(\"AudioIO\")")
init_portaudio()
portaudio_inited = true
else
error("Currently only 1 stream is supported at a time")
end
init_portaudio()
mixer = AudioMixer()
stream = new(mixer, DeviceInfo(sample_rate, buf_size))
# we need to start up the stream with the portaudio library
@ -40,12 +54,6 @@ function synchronize_buffer(buffer)
ccall((:synchronize_buffer, libportaudio_shim), Void, (Ptr{Void},), buffer)
end
function init_portaudio()
info("Initializing PortAudio. Expect errors as we scan devices")
err = ccall((:Pa_Initialize, "libportaudio"), PaError, ())
handle_status(err)
end
function open_portaudio_stream(stream::PortAudioStream)
# starts up a stream with the portaudio library and associates it with the
# given AudioIO PortAudioStream
@ -55,10 +63,7 @@ function open_portaudio_stream(stream::PortAudioStream)
fd = ccall((:make_pipe, libportaudio_shim), Cint, ())
info("Launching PortAudio Task...")
function task_wrapper()
portaudio_task(fd, stream)
end
schedule(Task(task_wrapper))
schedule(Task(() -> portaudio_task(fd, stream)))
# TODO: test not yielding here
yield()
info("Audio Task Yielded, starting the stream...")
@ -108,6 +113,65 @@ function portaudio_task(jl_filedesc::Integer, stream::PortAudioStream)
end
end
type PaDeviceInfo
struct_version::Cint
name::Ptr{Cchar}
host_api::PaHostApiIndex
max_input_channels::Cint
max_output_channels::Cint
default_low_input_latency::PaTime
default_low_output_latency::PaTime
default_high_input_latency::PaTime
default_high_output_latency::PaTime
default_sample_rate::Cdouble
end
type PaHostApiInfo
struct_version::Cint
api_type::PaHostApiTypeId
name::Ptr{Cchar}
deviceCount::Cint
defaultInputDevice::PaDeviceIndex
defaultOutputDevice::PaDeviceIndex
end
type AudioDevice
name::String
host_api::String
max_input_channels::Int
max_output_channels::Int
end
# some thin wrappers to portaudio calls
get_device_info(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, "libportaudio"),
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
get_host_api_info(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, "libportaudio"),
Ptr{PaHostApiInfo}, (PaHostApiIndex,), i))
function get_portaudio_devices()
init_portaudio()
device_count = ccall((:Pa_GetDeviceCount, "libportaudio"), PaDeviceIndex, ())
pa_devices = [get_device_info(i) for i in 0:(device_count - 1)]
[AudioDevice(bytestring(d.name),
bytestring(get_host_api_info(d.host_api).name),
d.max_input_channels,
d.max_output_channels)
for d in pa_devices]
end
function init_portaudio()
# can be called multiple times with no effect
global portaudio_inited
if !portaudio_inited
@assert(libportaudio_shim != "", "Failed to find required library libportaudio_shim. Try re-running the package script using Pkg.build(\"AudioIO\"), then reloading with reload(\"AudioIO\")")
info("Initializing PortAudio. Expect errors as we scan devices")
err = ccall((:Pa_Initialize, "libportaudio"), PaError, ())
handle_status(err)
portaudio_inited = true
end
end
# Old code for reference during initial development. We can get rid of this
# once the library is a little more mature

View file

@ -73,25 +73,27 @@ stop(node)
process(test_stream)
@test process(test_stream) == zeros(AudioIO.AudioSample, TEST_BUF_SIZE)
for ext in ["wav", "flac"]
info("Testing $ext file write/read")
info("Testing wav file write/read")
fname = "test/sinwave.$ext"
fname = "test/sinwave.wav"
samplerate = 44100
freq = 440
t = [0 : 2 * samplerate - 1] / samplerate
phase = 2 * pi * freq * t
reference = int16((2 ^ 15 - 1) * sin(phase))
samplerate = 44100
freq = 440
t = [0 : 2 * samplerate - 1] / samplerate
phase = 2 * pi * freq * t
reference = int16((2 ^ 15 - 1) * sin(phase))
af_open(fname, "w") do f
write(f, reference)
end
af_open(fname) do f
@test f.sfinfo.channels == 1
@test f.sfinfo.frames == 2 * samplerate
actual = read(f, 2 * samplerate)
@test_approx_eq(reference, actual)
end
af_open(fname, "w") do f
write(f, reference)
end
af_open(fname) do f
@test f.sfinfo.channels == 1
@test f.sfinfo.frames == 2 * samplerate
actual = read(f, 2 * samplerate)
@test_approx_eq(reference, actual)
end
info("Testing Audio Device Listing...")
d_list = get_audio_devices()
@test length(d_list) > 0