Interface

Performance note

Presumably due to the limits of inference, scheduling 16 or more synthesizers simultaneously will lead you off a performance cliff. Hopefully this limitation will go away in future versions of Julia.

AudioSchedules.AudioScheduleMethod
AudioSchedule(; sample_rate = 44100Hz)

Create an empty AudioSchedule. You can add! new synthesizers to the schedule. Specify a sample_rate with units per time, like 1/s or Hz.

julia> using AudioSchedules


julia> using Unitful: s, Hz


julia> a_schedule = AudioSchedule()
0.0 s 44100.0 Hz AudioSchedule

julia> add!(a_schedule, Map(sin, Cycles(440Hz)), 0s, 0, Line => 1s, 0.05, Line => 1s, 1, Line => 1s, 0)

julia> a_schedule
3.0 s 44100.0 Hz AudioSchedule

You can use write the schedule directly to a PortAudioStream. However, to do so, the PortAudioStream must have a Weaver writer. The PortAudioStream must have exactly 0 input channels, 1 output channel, and a matching sample rate.

julia> using PortAudio: PortAudioStream

julia> PortAudioStream(0, 1, writer = Weaver()) do stream
            write(stream, a_schedule)
        end

julia> a_schedule
0.0 s 44100.0 Hz AudioSchedule

After you play an AudioSchedule, it will be empty again. You can save it as a SampledSignals.SampleBuf if you want to play it again.

julia> using SampledSignals: SampleBuf

julia> new_schedule = AudioSchedule();

julia> add!(new_schedule, Map(sin, Cycles(440Hz)), 0s, 0, Line => 1s, 0.05, Line => 1s, 1, Line => 1s, 0)

julia> saved = SampleBuf(new_schedule)
132300-frame, 1-channel SampleBuf{Float64, 2}
3.0s sampled at 44100.0Hz
▁▂▂▃▃▃▃▃▃▃▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▅▅▅▆▆▆▆▆▆▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▆▆▆▆▆▆▅▅▄

julia> PortAudioStream(0, 1, warn_xruns = false) do stream
            write(stream, saved)
        end
132300
source
AudioSchedules.CyclesType
Cycles(frequency)

Cycles from 0 to 2π to repeat at a frequency (with frequency units, like Hz). Supports make_iterator.

julia> using AudioSchedules


julia> using Unitful: Hz


julia> first(make_iterator(Cycles(440Hz), 44100Hz))
0.0
source
AudioSchedules.GrowType
Grow(start_level, rate)

Exponentially grow or decay from start_level (unitless), at a continuous rate (with units per time like 1/s). Supports make_iterator and segments.

julia> using AudioSchedules


julia> using Unitful: Hz, s


julia> first(make_iterator(Grow(1, 1 / s), 44100Hz))
1.0
source
AudioSchedules.HookType
Hook(rate, slope)

Make a hook shape, with an exponential curve growing at a continuous rate (with units per time like 1/s), followed by a line with slope (with units per time like 1/s). Use with add!. Supports segments. Not all hooks are solvable.

julia> using AudioSchedules


julia> using Unitful: Hz, s


julia> a_schedule = AudioSchedule();


julia> add!(a_schedule, Map(sin, Cycles(440Hz)), 0s, 1, Hook(1 / s, 1 / s) => 2s, ℯ + 1)


julia> add!(a_schedule, Map(sin, Cycles(440Hz)), 0s, 1, Hook(1 / s, 1 / s) => 2s, 0)
ERROR: Unsolvable hook
[...]
source
AudioSchedules.LineType
Line(start_level, slope)

A line from start_level (unitless) with slope (with units per time like 1/s). Supports make_iterator and segments.

julia> using AudioSchedules


julia> using Unitful: Hz, s


julia> first(make_iterator(Line(0, 1 / s), 44100Hz))
0.0
source
AudioSchedules.MapType
Map(a_function, synthesizers...)

Map a_function over synthesizers. Supports make_iterator.

julia> using AudioSchedules


julia> using Unitful: Hz


julia> first(make_iterator(Map(sin, Cycles(440Hz)), 44100Hz))
0.0
source
AudioSchedules.SawToothMethod
SawTooth(overtones)

Build a saw-tooth wave from its partials, starting with the fundamental (1), up to overtones.

To increase richness but also buziness, increase overtones.

julia> using AudioSchedules


julia> SawTooth(3)(π / 4)
0.9185207636218614
source
AudioSchedules.ScaleType
function Scale(ratio)

A simple wrapper that will multiply inputs by the ratio.

julia> using AudioSchedules


julia> Scale(3)(2)
6
source
AudioSchedules.add!Method
add!(schedule::AudioSchedule, synthesizer, start_time,
    start_level, shape => duration, end_level, more_segments...
)

Add a synthesizer to a AudioSchedule, where synthesizer is anything that supports make_iterator, start_time has units of time (like s), and the rest of the arguments specify the shape of the envelope.

For all envelope segments, call

segments(shape, start_level, duration, end_level)

duration should have units of time (like s). For example,

add!(schedule, synthesizer, start_time, 0, Line => 1s, 1, Line => 1s, 0)

will call segments twice:

segments(Line, 0, 1s, 1)
segments(Line, 1, 1s, 0)
source
AudioSchedules.equal_loudnessMethod
equal_loudness(synthesizer::Map{<:Any, Tuple{Cycles}})

Change the volume of a synthesizer so that sounds played at different frequencies will have the same perceived volume. Assumes that the map function has a period of 2π.

julia> using AudioSchedules


julia> using Unitful: Hz


julia> soft = equal_loudness(Map(cos, Cycles(10000Hz)));


julia> first(make_iterator(soft, 44100Hz)) ≈ 0.0053035474
true

Technical details: uses the ISO 226:2003 curve for 40 phons. Scales output by a ratio of the equivalent sound pressure at the current frequency to the equivalent sound pressure at 20Hz (about as low as humans can hear).

source
AudioSchedules.make_iteratorMethod
make_iterator(synthesizer, sample_rate)

Return an iterator that will the play the synthesizer at sample_rate (with frequency units, like Hz). The iterator should yield Float64s between -1 and 1. Assumes that iterators will never end while they are scheduled.

source
AudioSchedules.segmentsMethod
segments(shape, start_level, duration, end_level)

Called for each envelope segment passed to add!. Return a tuple of pairs in the form (segment, duration), where duration has units of time (like s), for a segment of shape shape.

julia> using AudioSchedules


julia> using Unitful: s


julia> segments(Grow, 1, 1s, ℯ)
((Grow(1.0, 1.0 s⁻¹), 1 s),)
source
AudioSchedules.@q_strMacro
q"interval"

Create a musical interval. You can specify a numerator (which defaults to 1), denominator (which defaults to 1), and an octave shift (which defaults to 0).

julia> using AudioSchedules


julia> q"1"
1//1

julia> q"3/2"
3//2

julia> q"2/3o1"
4//3

julia> q"2/3o-1"
1//3

julia> q"o2"
4//1

julia> q"1 + 1"
ERROR: LoadError: Base.Meta.ParseError("Can't parse interval 1 + 1")
[...]
source