Compat Julia 0.7
This commit is contained in:
parent
03aefe619d
commit
5823404f1a
11 changed files with 102 additions and 80 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
*.swp
|
||||
*.o
|
||||
deps/deps.jl
|
||||
deps/build.log
|
||||
*.wav
|
||||
*.flac
|
||||
*.cov
|
||||
|
|
|
@ -6,6 +6,7 @@ os:
|
|||
sudo: required
|
||||
julia:
|
||||
- 0.6
|
||||
- nightly
|
||||
notifications:
|
||||
email: false
|
||||
script:
|
||||
|
|
9
REQUIRE
9
REQUIRE
|
@ -1,6 +1,7 @@
|
|||
julia 0.6.0-dev.2746
|
||||
BinDeps
|
||||
SampledSignals 0.3.0
|
||||
RingBuffers 1.0.0
|
||||
julia 0.6
|
||||
BinDeps 0.8.8
|
||||
SampledSignals 1.1.2
|
||||
RingBuffers 1.1.2
|
||||
Compat 0.66.0
|
||||
@osx Homebrew
|
||||
@windows WinRPM
|
||||
|
|
|
@ -2,6 +2,7 @@ environment:
|
|||
matrix:
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
|
|
3
deps/build.jl
vendored
3
deps/build.jl
vendored
|
@ -1,8 +1,9 @@
|
|||
using BinDeps
|
||||
using Compat
|
||||
|
||||
@BinDeps.setup
|
||||
|
||||
ENV["JULIA_ROOT"] = abspath(JULIA_HOME, "../../")
|
||||
ENV["JULIA_ROOT"] = abspath(Compat.Sys.BINDIR, "../../")
|
||||
|
||||
# include alias for WinRPM library
|
||||
libportaudio = library_dependency("libportaudio", aliases=["libportaudio-2"])
|
||||
|
|
|
@ -1,30 +1,26 @@
|
|||
__precompile__()
|
||||
__precompile__(true)
|
||||
|
||||
module PortAudio
|
||||
|
||||
using SampledSignals
|
||||
using RingBuffers
|
||||
#using Suppressor
|
||||
using Compat
|
||||
import Compat: undef, fetch, @compat
|
||||
import Compat.LinearAlgebra: transpose!
|
||||
|
||||
import Base: eltype, show
|
||||
import Base: close, isopen
|
||||
import Base: read, read!, write, flush
|
||||
|
||||
export PortAudioStream
|
||||
|
||||
|
||||
# Get binary dependencies loaded from BinDeps
|
||||
include("../deps/deps.jl")
|
||||
include("suppressor.jl")
|
||||
include("pa_shim.jl")
|
||||
include("libportaudio.jl")
|
||||
|
||||
function __init__()
|
||||
init_pa_shim()
|
||||
global const notifycb_c = cfunction(notifycb, Cint, (Ptr{Void}, ))
|
||||
# initialize PortAudio on module load
|
||||
@suppress_err Pa_Initialize()
|
||||
end
|
||||
|
||||
export PortAudioStream
|
||||
|
||||
# These sizes are all in frames
|
||||
|
||||
# the block size is what we request from portaudio if no blocksize is given.
|
||||
|
@ -46,7 +42,7 @@ function versioninfo(io::IO=STDOUT)
|
|||
println(io, "Shim Source Hash: ", shimhash()[1:10])
|
||||
end
|
||||
|
||||
type PortAudioDevice
|
||||
mutable struct PortAudioDevice
|
||||
name::String
|
||||
hostapi::String
|
||||
maxinchans::Int
|
||||
|
@ -76,7 +72,7 @@ devnames() = join(["\"$(dev.name)\"" for dev in devices()], "\n")
|
|||
# PortAudioStream
|
||||
##################
|
||||
|
||||
type PortAudioStream{T}
|
||||
mutable struct PortAudioStream{T}
|
||||
samplerate::Float64
|
||||
blocksize::Int
|
||||
stream::PaStream
|
||||
|
@ -98,7 +94,7 @@ type PortAudioStream{T}
|
|||
Ptr{Pa_StreamParameters}(0) :
|
||||
Ref(Pa_StreamParameters(outdev.idx, outchans, type_to_fmt[T], 0.0, C_NULL))
|
||||
this = new(sr, blocksize, C_NULL)
|
||||
finalizer(this, close)
|
||||
@compat finalizer(close, this)
|
||||
this.sink = PortAudioSink{T}(outdev.name, this, outchans, blocksize*2)
|
||||
this.source = PortAudioSource{T}(indev.name, this, inchans, blocksize*2)
|
||||
this.errbuf = RingBuffer{pa_shim_errmsg_t}(1, ERR_BUFSIZE)
|
||||
|
@ -127,6 +123,7 @@ type PortAudioStream{T}
|
|||
end
|
||||
end
|
||||
|
||||
# this is the top-level outer constructor that all the other outer constructors end up calling
|
||||
"""
|
||||
PortAudioStream(inchannels=2, outchannels=2; options...)
|
||||
PortAudioStream(duplexdevice, inchannels=2, outchannels=2; options...)
|
||||
|
@ -150,8 +147,6 @@ Options:
|
|||
`false`, you are free to read and write separately, but
|
||||
overflow or underflow can affect the round-trip latency.
|
||||
"""
|
||||
# this is the top-level outer constructor that all the other outer constructors
|
||||
# end up calling
|
||||
function PortAudioStream(indev::PortAudioDevice, outdev::PortAudioDevice,
|
||||
inchans=2, outchans=2; eltype=Float32, samplerate=-1, blocksize=DEFAULT_BLOCKSIZE, synced=false)
|
||||
if samplerate == -1
|
||||
|
@ -226,7 +221,7 @@ end
|
|||
isopen(stream::PortAudioStream) = stream.stream != C_NULL
|
||||
|
||||
SampledSignals.samplerate(stream::PortAudioStream) = stream.samplerate
|
||||
eltype{T}(stream::PortAudioStream{T}) = T
|
||||
eltype(stream::PortAudioStream{T}) where T = T
|
||||
|
||||
read(stream::PortAudioStream, args...) = read(stream.source, args...)
|
||||
read!(stream::PortAudioStream, args...) = read!(stream.source, args...)
|
||||
|
@ -253,7 +248,7 @@ Handle errors coming over the error stream from PortAudio. This is run as an
|
|||
independent task while the stream is active.
|
||||
"""
|
||||
function handle_errors(stream::PortAudioStream)
|
||||
err = Vector{pa_shim_errmsg_t}(1)
|
||||
err = Vector{pa_shim_errmsg_t}(undef, 1)
|
||||
while true
|
||||
nread = read!(stream.errbuf, err)
|
||||
nread == 1 || break
|
||||
|
@ -279,7 +274,7 @@ end
|
|||
# Define our source and sink types
|
||||
for (TypeName, Super) in ((:PortAudioSink, :SampleSink),
|
||||
(:PortAudioSource, :SampleSource))
|
||||
@eval type $TypeName{T} <: $Super
|
||||
@eval mutable struct $TypeName{T} <: $Super
|
||||
name::String
|
||||
stream::PortAudioStream{T}
|
||||
chunkbuf::Array{T, 2}
|
||||
|
@ -319,7 +314,7 @@ function SampledSignals.unsafe_write(sink::PortAudioSink, buf::Array, frameoffse
|
|||
towrite = min(framecount-nwritten, CHUNKSIZE)
|
||||
# make a buffer of interleaved samples
|
||||
transpose!(view(sink.chunkbuf, :, 1:towrite),
|
||||
view(buf, (1:towrite)+nwritten+frameoffset, :))
|
||||
view(buf, (1:towrite) .+ nwritten .+ frameoffset, :))
|
||||
n = write(sink.ringbuf, sink.chunkbuf, towrite)
|
||||
nwritten += n
|
||||
# break early if the stream is closed
|
||||
|
@ -335,7 +330,7 @@ function SampledSignals.unsafe_read!(source::PortAudioSource, buf::Array, frameo
|
|||
toread = min(framecount-nread, CHUNKSIZE)
|
||||
n = read!(source.ringbuf, source.chunkbuf, toread)
|
||||
# de-interleave the samples
|
||||
transpose!(view(buf, (1:toread)+nread+frameoffset, :),
|
||||
transpose!(view(buf, (1:toread) .+ nread .+ frameoffset, :),
|
||||
view(source.chunkbuf, :, 1:toread))
|
||||
|
||||
nread += toread
|
||||
|
@ -346,9 +341,43 @@ function SampledSignals.unsafe_read!(source::PortAudioSource, buf::Array, frameo
|
|||
nread
|
||||
end
|
||||
|
||||
|
||||
const libpa_shim = find_pa_shim()
|
||||
|
||||
"""
|
||||
PortAudio.shimhash()
|
||||
|
||||
Return the sha256 hash(as a string) of the source file used to build the shim.
|
||||
We may use this sometime to verify that the distributed binary stays in sync
|
||||
with the rest of the package.
|
||||
"""
|
||||
shimhash() = unsafe_string(ccall((:pa_shim_getsourcehash, libpa_shim), Cstring, ()))
|
||||
|
||||
|
||||
# this is called by the shim process callback to notify that there is new data.
|
||||
# it's run in the audio context so don't do anything besides wake up the
|
||||
# AsyncCondition handle associated with that ring buffer
|
||||
notifycb(handle) = ccall(:uv_async_send, Cint, (Ptr{Void}, ), handle)
|
||||
notifycb(handle) = ccall(:uv_async_send, Cint, (Ptr{Cvoid},), handle)
|
||||
|
||||
global shim_processcb_c, notifycb_c
|
||||
|
||||
function set_global_callbacks()
|
||||
shim_dlib = Libdl.dlopen(libpa_shim)
|
||||
|
||||
# pointer to the shim's process callback
|
||||
global shim_processcb_c = Libdl.dlsym(shim_dlib, :pa_shim_processcb)
|
||||
if shim_processcb_c == C_NULL
|
||||
error("Got NULL pointer loading `pa_shim_processcb`")
|
||||
end
|
||||
|
||||
global notifycb_c = @cfunction notifycb Cint (Ptr{Cvoid},)
|
||||
end
|
||||
|
||||
function __init__()
|
||||
set_global_callbacks()
|
||||
|
||||
# initialize PortAudio on module load
|
||||
@suppress_err Pa_Initialize()
|
||||
end
|
||||
|
||||
end # module PortAudio
|
||||
|
|
|
@ -9,8 +9,8 @@ const PaHostApiIndex = Cint
|
|||
const PaHostApiTypeId = Cint
|
||||
# PaStream is always used as an opaque type, so we're always dealing
|
||||
# with the pointer
|
||||
const PaStream = Ptr{Void}
|
||||
const PaStreamCallback = Void
|
||||
const PaStream = Ptr{Cvoid}
|
||||
const PaStreamCallback = Cvoid
|
||||
const PaStreamFlags = Culong
|
||||
|
||||
const paNoFlag = PaStreamFlags(0x00)
|
||||
|
@ -86,7 +86,7 @@ const pa_host_api_names = Dict{PaHostApiTypeId, String}(
|
|||
14 => "AudioScience HPI"
|
||||
)
|
||||
|
||||
type PaHostApiInfo
|
||||
mutable struct PaHostApiInfo
|
||||
struct_version::Cint
|
||||
api_type::PaHostApiTypeId
|
||||
name::Ptr{Cchar}
|
||||
|
@ -100,7 +100,7 @@ Pa_GetHostApiInfo(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, libportaudio),
|
|||
|
||||
# Device Functions
|
||||
|
||||
type PaDeviceInfo
|
||||
mutable struct PaDeviceInfo
|
||||
struct_version::Cint
|
||||
name::Ptr{Cchar}
|
||||
host_api::PaHostApiIndex
|
||||
|
@ -126,15 +126,15 @@ Pa_GetDefaultOutputDevice() = ccall((:Pa_GetDefaultOutputDevice, libportaudio),
|
|||
|
||||
# Stream Functions
|
||||
|
||||
type Pa_StreamParameters
|
||||
mutable struct Pa_StreamParameters
|
||||
device::PaDeviceIndex
|
||||
channelCount::Cint
|
||||
sampleFormat::PaSampleFormat
|
||||
suggestedLatency::PaTime
|
||||
hostAPISpecificStreamInfo::Ptr{Void}
|
||||
hostAPISpecificStreamInfo::Ptr{Cvoid}
|
||||
end
|
||||
|
||||
type PaStreamInfo
|
||||
mutable struct PaStreamInfo
|
||||
structVersion::Cint
|
||||
inputLatency::PaTime
|
||||
outputLatency::PaTime
|
||||
|
@ -148,7 +148,7 @@ end
|
|||
# err = ccall((:Pa_OpenDefaultStream, libportaudio),
|
||||
# PaError, (Ref{PaStream}, Cint, Cint,
|
||||
# PaSampleFormat, Cdouble, Culong,
|
||||
# Ref{Void}, Ref{Void}),
|
||||
# Ref{Cvoid}, Ref{Cvoid}),
|
||||
# streamPtr, inChannels, outChannels, sampleFormat, sampleRate,
|
||||
# framesPerBuffer, C_NULL, C_NULL)
|
||||
# handle_status(err)
|
||||
|
@ -166,7 +166,7 @@ function Pa_OpenStream(inParams, outParams,
|
|||
Ptr{Pa_StreamParameters},
|
||||
Ptr{Pa_StreamParameters},
|
||||
Cdouble, Culong, PaStreamFlags,
|
||||
Ptr{Void}, Ptr{Void}),
|
||||
Ptr{Cvoid}, Ptr{Cvoid}),
|
||||
streamPtr,
|
||||
inParams, outParams,
|
||||
sampleRate, framesPerBuffer, flags,
|
||||
|
@ -211,7 +211,7 @@ function Pa_ReadStream(stream::PaStream, buf::Array, frames::Integer=length(buf)
|
|||
show_warnings::Bool=true)
|
||||
frames <= length(buf) || error("Need a buffer at least $frames long")
|
||||
err = ccall((:Pa_ReadStream, libportaudio), PaError,
|
||||
(PaStream, Ptr{Void}, Culong),
|
||||
(PaStream, Ptr{Cvoid}, Culong),
|
||||
stream, buf, frames)
|
||||
handle_status(err, show_warnings)
|
||||
buf
|
||||
|
@ -221,7 +221,7 @@ function Pa_WriteStream(stream::PaStream, buf::Array, frames::Integer=length(buf
|
|||
show_warnings::Bool=true)
|
||||
frames <= length(buf) || error("Need a buffer at least $frames long")
|
||||
err = ccall((:Pa_WriteStream, libportaudio), PaError,
|
||||
(PaStream, Ptr{Void}, Culong),
|
||||
(PaStream, Ptr{Cvoid}, Culong),
|
||||
stream, buf, frames)
|
||||
handle_status(err, show_warnings)
|
||||
nothing
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
function init_pa_shim()
|
||||
function find_pa_shim()
|
||||
libdir = joinpath(dirname(@__FILE__), "..", "deps", "usr", "lib")
|
||||
libsuffix = ""
|
||||
basename = "pa_shim"
|
||||
@static if is_linux() && Sys.ARCH == :x86_64
|
||||
@static if Compat.Sys.islinux() && Sys.ARCH == :x86_64
|
||||
libsuffix = "x86_64-linux-gnu"
|
||||
elseif is_linux() && Sys.ARCH == :i686
|
||||
elseif Compat.Sys.islinux() && Sys.ARCH == :i686
|
||||
libsuffix = "i686-linux-gnu"
|
||||
elseif is_apple() && Sys.ARCH == :x86_64
|
||||
elseif Compat.Sys.isapple() && Sys.ARCH == :x86_64
|
||||
libsuffix = "x86_64-apple-darwin14"
|
||||
elseif is_windows() && Sys.ARCH == :x86_64
|
||||
elseif Compat.Sys.iswindows() && Sys.ARCH == :x86_64
|
||||
libsuffix = "x86_64-w64-mingw32"
|
||||
elseif is_windows() && Sys.ARCH == :i686
|
||||
elseif Compat.Sys.iswindows() && Sys.ARCH == :i686
|
||||
libsuffix = "i686-w64-mingw32"
|
||||
elseif !any(
|
||||
(sfx) -> isfile(joinpath(libdir, "$basename.$sfx")),
|
||||
|
@ -19,16 +19,11 @@ function init_pa_shim()
|
|||
end
|
||||
# if there's a suffix-less library, it was built natively on this machine,
|
||||
# so load that one first, otherwise load the pre-built one
|
||||
global const libpa_shim = Base.Libdl.find_library(
|
||||
libpa_shim = Libdl.find_library(
|
||||
[basename, "$(basename)_$libsuffix"],
|
||||
[libdir])
|
||||
libpa_shim == "" && error("Could not load $basename library, please file an issue at https://github.com/JuliaAudio/RingBuffers.jl/issues with your `versioninfo()` output")
|
||||
shim_dlib = Libdl.dlopen(libpa_shim)
|
||||
# pointer to the shim's process callback
|
||||
global const shim_processcb_c = Libdl.dlsym(shim_dlib, :pa_shim_processcb)
|
||||
if shim_processcb_c == C_NULL
|
||||
error("Got NULL pointer loading `pa_shim_processcb`")
|
||||
end
|
||||
return libpa_shim
|
||||
end
|
||||
|
||||
const pa_shim_errmsg_t = Cint
|
||||
|
@ -43,19 +38,10 @@ mutable struct pa_shim_info_t
|
|||
outputbuf::Ptr{PaUtilRingBuffer} # ringbuffer for output
|
||||
errorbuf::Ptr{PaUtilRingBuffer} # ringbuffer to send error notifications
|
||||
sync::Cint # keep input/output ring buffers synchronized (0/1)
|
||||
notifycb::Ptr{Void} # Julia callback to notify on updates (called from audio thread)
|
||||
inputhandle::Ptr{Void} # condition to notify on new input data
|
||||
outputhandle::Ptr{Void} # condition to notify when ready for output
|
||||
errorhandle::Ptr{Void} # condition to notify on new errors
|
||||
notifycb::Ptr{Cvoid} # Julia callback to notify on updates (called from audio thread)
|
||||
inputhandle::Ptr{Cvoid} # condition to notify on new input data
|
||||
outputhandle::Ptr{Cvoid} # condition to notify when ready for output
|
||||
errorhandle::Ptr{Cvoid} # condition to notify on new errors
|
||||
end
|
||||
|
||||
"""
|
||||
PortAudio.shimhash()
|
||||
|
||||
Return the sha256 hash(as a string) of the source file used to build the shim.
|
||||
We may use this sometime to verify that the distributed binary stays in sync
|
||||
with the rest of the package.
|
||||
"""
|
||||
shimhash() = unsafe_string(
|
||||
ccall((:pa_shim_getsourcehash, libpa_shim), Cstring, ()))
|
||||
Base.unsafe_convert(::Type{Ptr{Void}}, info::pa_shim_info_t) = pointer_from_objref(info)
|
||||
Base.unsafe_convert(::Type{Ptr{Cvoid}}, info::pa_shim_info_t) = pointer_from_objref(info)
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
macro suppress_err(block)
|
||||
quote
|
||||
if ccall(:jl_generating_output, Cint, ()) == 0
|
||||
ORIGINAL_STDERR = STDERR
|
||||
ORIGINAL_STDERR = stderr
|
||||
err_rd, err_wr = redirect_stderr()
|
||||
err_reader = @async readstring(err_rd)
|
||||
err_reader = @async read(err_rd, String)
|
||||
end
|
||||
|
||||
value = $(esc(block))
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env julia
|
||||
|
||||
using Base.Test
|
||||
using Compat
|
||||
using Compat.Test
|
||||
import Compat: Cvoid
|
||||
using TestSetExtensions
|
||||
using PortAudio
|
||||
using SampledSignals
|
||||
|
@ -34,7 +36,7 @@ function setup_callback(inchans, outchans, nframes, synced)
|
|||
|
||||
function processfunc()
|
||||
ccall(shim_processcb_c, Cint,
|
||||
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{Void}),
|
||||
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Cvoid}, Culong, Ptr{Cvoid}),
|
||||
cb_input, cb_output, nframes, C_NULL, flags, pointer_from_objref(info))
|
||||
end
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
include("runtests.jl")
|
||||
|
||||
# these default values are specific to my machines
|
||||
if is_windows()
|
||||
if Compat.Sys.iswindows()
|
||||
default_indev = "Microphone Array (Realtek High "
|
||||
default_outdev = "Speaker/Headphone (Realtek High"
|
||||
elseif is_apple()
|
||||
default_indev = "Built-in Microph"
|
||||
elseif Compat.Sys.isapple()
|
||||
default_indev = "Built-in Microphone"
|
||||
default_outdev = "Built-in Output"
|
||||
elseif is_linux()
|
||||
elseif Compat.Sys.islinux()
|
||||
default_indev = "default"
|
||||
default_outdev = "default"
|
||||
end
|
||||
|
@ -50,12 +50,12 @@ end
|
|||
write(stream, buf)
|
||||
io = IOBuffer()
|
||||
show(io, stream)
|
||||
@test String(take!(io)) == """
|
||||
PortAudio.PortAudioStream{Float32}
|
||||
@test Compat.occursin("""
|
||||
PortAudioStream{Float32}
|
||||
Samplerate: 44100.0Hz
|
||||
Buffer Size: 4096 frames
|
||||
2 channel sink: "$default_outdev"
|
||||
2 channel source: "$default_indev\""""
|
||||
2 channel source: "$default_indev\"""", String(take!(io)))
|
||||
close(stream)
|
||||
end
|
||||
@testset "Error on wrong name" begin
|
||||
|
@ -68,8 +68,8 @@ end
|
|||
buf = SampleBuf(rand(eltype(stream), 48000, nchannels(stream.sink))*0.1, samplerate(stream))
|
||||
t1 = @async write(stream, buf)
|
||||
t2 = @async write(stream, buf)
|
||||
@test wait(t1) == 48000
|
||||
@test wait(t2) == 48000
|
||||
@test fetch(t1) == 48000
|
||||
@test fetch(t2) == 48000
|
||||
flush(stream)
|
||||
close(stream)
|
||||
end
|
||||
|
@ -78,8 +78,8 @@ end
|
|||
buf = SampleBuf(rand(eltype(stream), 48000, nchannels(stream.source))*0.1, samplerate(stream))
|
||||
t1 = @async read!(stream, buf)
|
||||
t2 = @async read!(stream, buf)
|
||||
@test wait(t1) == 48000
|
||||
@test wait(t2) == 48000
|
||||
@test fetch(t1) == 48000
|
||||
@test fetch(t2) == 48000
|
||||
close(stream)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue