Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name = "JACKAudio"
uuid = "e15094ee-46e5-4090-b60c-bc5c382a8256"
version = "0.1.0"

[deps]
SampledSignals = "bd7594eb-a658-542f-9e75-4c4d8908c167"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]

3 changes: 0 additions & 3 deletions REQUIRE

This file was deleted.

78 changes: 37 additions & 41 deletions src/JACKAudio.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
__precompile__()

module JACKAudio

using Compat
import Compat.ASCIIString

using SampledSignals
import SampledSignals: nchannels, samplerate, nframes
using Base.Libc: malloc, free
using Sockets

export JACKClient, sources, sinks, seekavailable

Expand All @@ -25,19 +21,6 @@ const RINGBUF_SAMPLES = 131072
# detect an overflow.
const OVERFLOW_ADVANCE = 8192

function __init__()
global const process_cb = cfunction(process, Cint, (NFrames, Ptr{Ptr{Void}}))
global const shutdown_cb = cfunction(shutdown, Void, (Ptr{JACKClient}, ))
global const info_handler_cb = cfunction(info_handler, Void, (Cstring, ))
global const error_handler_cb = cfunction(error_handler, Void, (Cstring, ))


ccall((:jack_set_info_function, :libjack), Void, (Ptr{Void},),
info_handler_cb)
ccall((:jack_set_error_function, :libjack), Void, (Ptr{Void},),
error_handler_cb)
end

function error_handler(msg)
println("libjack: ERROR: $(unsafe_string(msg))")

Expand All @@ -55,8 +38,8 @@ ringbuffer to get data in and out of the process callback and a julia RingBuffer
object that can be used for additional buffering. One important difference is
that if the jack ringbuffer fills up it can't be written to, but the Julia
RingBuffer maintains the last N samples"""
immutable JACKPort
name::ASCIIString
struct JACKPort
name::String
ptr::PortPtr
jackbuf::RingBufPtr

Expand Down Expand Up @@ -86,10 +69,10 @@ for (T, Super, porttype) in
as a group. There can be multiple JACKSources and JACKSinks in a JACKClient,
and all the sources and sinks in a client get updated by the same `process`
method."""
@eval immutable $T <: $Super
name::ASCIIString
@eval struct $T <: $Super
name::String
client::ClientPtr
clientname::ASCIIString
clientname::String
ports::Vector{JACKPort}
ringcondition::Condition # used to synchronize any in-progress transations

Expand Down Expand Up @@ -136,22 +119,22 @@ end
multiple `JACKSource`s and `JACKSink`s. It is automatically activated when it is
constructed, so the sources and sinks are available for reading and writing,
respectively."""
type JACKClient
name::ASCIIString
mutable struct JACKClient
name::String
ptr::ClientPtr
sources::Vector{JACKSource}
sinks::Vector{JACKSink}
# this is memory allocated separately with malloc that is used to give the
# process callback all the pointers it needs for the source/sink ports and
# ringbuffers
portptrs::Ptr{Ptr{Void}}
callback::Base.SingleAsyncWork
portptrs::Ptr{Ptr{Nothing}}
callback::Base.AsyncCondition

# this constructor takes a list of name, channelcount pairs
function JACKClient{T1 <: Tuple, T2 <: Tuple}(
function JACKClient(
name::AbstractString="Julia",
sources::Vector{T1}=[("In", 2)],
sinks::Vector{T2}=[("Out", 2)];
sources::Vector{<:Tuple}=[("In", 2)],
sinks::Vector{<:Tuple}=[("Out", 2)];
connect=true, active=true)
status = Ref{Cint}(Failure)
clientptr = jack_client_open(name, 0, status)
Expand All @@ -175,7 +158,7 @@ type JACKClient
nptrs = 2nsources + 2nsinks + 3
# TODO: we can switch this malloc and unsafe_store business
# to an array we push! to
portptrs = Ptr{Ptr{Void}}(malloc(nptrs*sizeof(Ptr{Void})))
portptrs = Ptr{Ptr{Nothing}}(malloc(nptrs*sizeof(Ptr{Nothing})))
if isnullptr(portptrs)
jack_client_close(clientptr)
error("Failure allocating memory for JACK client \"$name\"")
Expand Down Expand Up @@ -226,7 +209,7 @@ type JACKClient
unsafe_store!(portptrs, C_NULL, ptridx)
ptridx += 1

client.callback = Base.SingleAsyncWork(data -> managebuffers(client))
client.callback = Base.AsyncCondition(data -> managebuffers(client))

# and finally we store the callback handle so the JACK process callback
# can trigger the managebuffers function to run in the julia context
Expand Down Expand Up @@ -264,8 +247,8 @@ JACKClient(sourcecount::Integer, sinkcount::Integer; kwargs...) =

function Base.show(io::IO, client::JACKClient)
print(io, "JACKClient(\"$(client.name)\", [")
sources = ASCIIString["(\"$(source.name)\", $(nchannels(source)))" for source in client.sources]
sinks = ASCIIString["(\"$(sink.name)\", $(nchannels(sink)))" for sink in client.sinks]
sources = String["(\"$(source.name)\", $(nchannels(source)))" for source in client.sources]
sinks = String["(\"$(sink.name)\", $(nchannels(sink)))" for sink in client.sinks]
join(io, sources, ", ")
print(io, "], [")
join(io, sinks, ", ")
Expand Down Expand Up @@ -309,10 +292,10 @@ sinks(client::JACKClient) = client.sinks
Base.read!(client::JACKClient, args...) = read!(client.sources[1], args...)
Base.read(client::JACKClient, args...) = read(client.sources[1], args...)
Base.write(client::JACKClient, args...) = write(client.sinks[1], args...)
Base.connect(c1::JACKClient, c2::JACKClient) = connect(c1.sinks[1], c2.sources[1])
Sockets.connect(c1::JACKClient, c2::JACKClient) = connect(c1.sinks[1], c2.sources[1])

# TODO: julia PR to extend Base.isnull rather than using isnullptr
isnullptr(ptr::Ptr) = Ptr{Void}(ptr) == C_NULL
isnullptr(ptr::Ptr) = Ptr{Nothing}(ptr) == C_NULL
isnullptr(ptr::Cstring) = Ptr{Cchar}(ptr) == C_NULL

"""Connect the given client to the physical input/output ports, by just matching
Expand Down Expand Up @@ -356,7 +339,7 @@ function autoconnect(client::JACKClient)
end
end

function Base.connect(sink::JACKSink, source::JACKSource)
function Sockets.connect(sink::JACKSink, source::JACKSource)
for (sinkport, sourceport) in zip(sink.ports, source.ports)
sinkportname = string(sink.clientname, ":", sinkport.name)
sourceportname = string(source.clientname, ":", sourceport.name)
Expand Down Expand Up @@ -570,13 +553,13 @@ function process(nframes, portptrs)

# notify the managebuffers, which will get called with a reference to
# this client
ccall(:uv_async_send, Void, (Ptr{Void},), handle)
ccall(:uv_async_send, Nothing, (Ptr{Nothing},), handle)

Cint(0)
end

"""Returns the largest x <= value s.t. x has the given alignment (in bytes)"""
align{T<:Unsigned}(value::T, alignment::Integer) = value & ~(T(alignment-1))
align(value::T, alignment::Integer) where T<:Unsigned = value & ~(T(alignment-1))
sampalign(value) = align(value, sizeof(JACKSample))

# this callback gets called from within the Julia event loop, but is triggered
Expand All @@ -603,9 +586,22 @@ function shutdown(arg)
nothing
end

memset(buf, val, count) = ccall(:memset, Ptr{Void},
(Ptr{Void}, Cint, Csize_t),
memset(buf, val, count) = ccall(:memset, Ptr{Nothing},
(Ptr{Nothing}, Cint, Csize_t),
buf, 0, count)

function __init__()
global process_cb = @cfunction(process, Cint, (NFrames, Ptr{Ptr{Nothing}}))
global shutdown_cb = @cfunction(shutdown, Nothing, (Ptr{JACKClient}, ))
global info_handler_cb = @cfunction(info_handler, Nothing, (Cstring, ))
global error_handler_cb = @cfunction(error_handler, Nothing, (Cstring, ))


ccall((:jack_set_info_function, :libjack), Nothing, (Ptr{Nothing},),
info_handler_cb)
ccall((:jack_set_error_function, :libjack), Nothing, (Ptr{Nothing},),
error_handler_cb)
end


end # module
34 changes: 17 additions & 17 deletions src/libjack.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
typealias ClientPtr Ptr{Void}
typealias PortPtr Ptr{Void}
typealias CFunPtr Ptr{Void}
typealias NFrames UInt32
typealias JACKSample Cfloat
const ClientPtr = Ptr{Nothing}
const PortPtr = Ptr{Nothing}
const CFunPtr = Ptr{Nothing}
const NFrames = UInt32
const JACKSample = Cfloat

const JACK_DEFAULT_AUDIO_TYPE = "32 bit float mono audio"

# this mirrors the struct defined in ringbuffer.h
type RingBuf
mutable struct RingBuf
buf::Ptr{Cchar}
write_ptr::Csize_t
read_ptr::Csize_t
Expand All @@ -16,7 +16,7 @@ type RingBuf
mlocked::Cint
end
# add typealias for consistency with other *Ptr types
typealias RingBufPtr Ptr{RingBuf}
const RingBufPtr = Ptr{RingBuf}

@enum(Option,
# Null value to use when no option bits are needed.
Expand All @@ -41,7 +41,7 @@ typealias RingBufPtr Ptr{RingBuf}
SessionID = 0x20)

# useful for OR'ing options together
@compat Base.:|(l::Option, r::Option) = UInt(l) | UInt(r)
Base.:|(l::Option, r::Option) = UInt(l) | UInt(r)

@enum(PortFlag,
PortIsInput = 0x01,
Expand All @@ -50,7 +50,7 @@ typealias RingBufPtr Ptr{RingBuf}
PortCanMonitor = 0x08,
PortIsTerminal = 0x10)

@compat Base.:|(l::PortFlag, r::PortFlag) = UInt(l) | UInt(r)
Base.:|(l::PortFlag, r::PortFlag) = UInt(l) | UInt(r)

# some functions also return a -1 on failure
@enum(Status,
Expand Down Expand Up @@ -94,7 +94,7 @@ typealias RingBufPtr Ptr{RingBuf}
status_str(status::Status) = string(status)

# use & syntax for checking a flag, but return a boolean
@compat Base.:&{T <: Integer}(val::T, status::Status) = val & T(status) != 0
Base.:&(val::Integer, status::Status) = val & T(status) != 0

function status_str(status::Integer)
if status == -1
Expand Down Expand Up @@ -125,12 +125,12 @@ jack_get_sample_rate(client) =

jack_set_process_callback(client, callback, userdata) =
ccall((:jack_set_process_callback, :libjack), Cint,
(ClientPtr, CFunPtr, Ptr{Void}),
(ClientPtr, CFunPtr, Ptr{Nothing}),
client, callback, userdata)

jack_on_shutdown(client, callback, userdata) =
ccall((:jack_on_shutdown, :libjack), Cint,
(ClientPtr, CFunPtr, Ptr{Void}),
(ClientPtr, CFunPtr, Ptr{Nothing}),
client, callback, userdata)

jack_get_ports(client, portname, typename, flags) =
Expand All @@ -142,7 +142,7 @@ jack_connect(client, src, dest) =
ccall((:jack_connect, :libjack), Cint, (ClientPtr, Cstring, Cstring),
client, src, dest)

jack_free(ptr) = ccall((:jack_free, :libjack), Void, (Ptr{Void}, ), ptr)
jack_free(ptr) = ccall((:jack_free, :libjack), Nothing, (Ptr{Nothing}, ), ptr)

jack_port_register(client, portname, porttype, flags, bufsize) =
ccall((:jack_port_register, :libjack), PortPtr,
Expand All @@ -162,14 +162,14 @@ jack_ringbuffer_create(bytes) =
ccall((:jack_ringbuffer_create, :libjack), Ptr{RingBuf}, (Csize_t, ), bytes)

jack_ringbuffer_free(buf) =
ccall((:jack_ringbuffer_free, :libjack), Void, (Ptr{RingBuf}, ), buf)
ccall((:jack_ringbuffer_free, :libjack), Nothing, (Ptr{RingBuf}, ), buf)

jack_ringbuffer_read(ringbuf, dest, bytes) =
ccall((:jack_ringbuffer_read, :libjack), Csize_t,
(Ptr{RingBuf}, Ptr{Void}, Csize_t), ringbuf, dest, bytes)
(Ptr{RingBuf}, Ptr{Nothing}, Csize_t), ringbuf, dest, bytes)

jack_ringbuffer_read_advance(ringbuf, bytes) =
ccall((:jack_ringbuffer_read_advance, :libjack), Void,
ccall((:jack_ringbuffer_read_advance, :libjack), Nothing,
(Ptr{RingBuf}, Csize_t), ringbuf, bytes)

jack_ringbuffer_read_space(ringbuf) =
Expand All @@ -178,7 +178,7 @@ jack_ringbuffer_read_space(ringbuf) =

jack_ringbuffer_write(ringbuf, src, bytes) =
ccall((:jack_ringbuffer_write, :libjack), Csize_t,
(Ptr{RingBuf}, Ptr{Void}, Csize_t), ringbuf, src, bytes)
(Ptr{RingBuf}, Ptr{Nothing}, Csize_t), ringbuf, src, bytes)

jack_ringbuffer_write_space(ringbuf) =
ccall((:jack_ringbuffer_write_space, :libjack), Csize_t,
Expand Down
2 changes: 0 additions & 2 deletions test/REQUIRE

This file was deleted.

9 changes: 1 addition & 8 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
module JACKAudioTests

using Compat
import Compat.ASCIIString

if VERSION >= v"0.5.0-"
using Base.Test
else
using BaseTestNext
end
using Test
using JACKAudio
using SampledSignals

Expand Down