Interface
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.AudioScheduleAudioSchedules.CyclesAudioSchedules.GrowAudioSchedules.LineAudioSchedules.MapAudioSchedules.SawToothAudioSchedules.ScaleAudioSchedules.durationAudioSchedules.make_seriesAudioSchedules.@envelope
AudioSchedules.AudioSchedule — MethodAudioSchedule(; sample_rate = 44100Hz)Create an empty AudioSchedule. You can push! new synthesizers to the audio_schedule. Provide 4 arguments to push!: the schedule, the synthesizer, the start time, and an envelope that you create with @envelope.
julia> using AudioSchedules
julia> audio_schedule = AudioSchedule()
0.0 s 44100.0 Hz AudioSchedule
julia> push!(audio_schedule, Map(sin, Cycles(440Hz)), 0s, @envelope(
0,
Line => 1s,
0.2,
Line => 1s,
0,
))
julia> push!(audio_schedule, Map(sin, Cycles(660Hz)), 2s, @envelope(
0,
Line => 1s,
0.2,
Line => 1s,
0,
))
julia> audio_schedule
4.0 s 44100.0 Hz AudioScheduleYou can iterate over an AudioSchedule. Each element will just be a vector of amplitudes.
julia> length(first(audio_schedule))
44100
julia> collect(AudioSchedule())
Any[]You can use write the audio_schedule directly to a PortAudio.PortAudioStream. The PortAudioStream must have exactly 1 output channel, and a matching sample rate.
julia> using PortAudio: PortAudioStream
julia> PortAudioStream(0, 1, warn_xruns = false) do stream
write(stream, audio_schedule)
end
julia> PortAudioStream(0, 2) do stream
write(stream, audio_schedule)
end
ERROR: ArgumentError: PortAudioStream does not have 1 output channel
[...]
julia> PortAudioStream(0, 1, samplerate = 48000) do stream
write(stream, audio_schedule)
end
ERROR: ArgumentError: Sample rates of PortAudioStream (48000.0) and AudioSchedule (44100.0) do not match
[...]You can save an AudioSchedule as a SampledSignals.SampleBuf.
julia> using SampledSignals: SampleBuf
julia> saved = SampleBuf(audio_schedule)
176400-frame, 1-channel SampleBuf{Float64, 1}
4.0s sampled at 44100.0Hz
▃▄▄▄▄▅▅▅▅▅▅▅▅▆▆▆▆▆▆▆▆▆▆▆▆▆▆▅▅▅▅▅▅▅▅▄▄▄▄▃▃▄▄▄▄▅▅▅▅▅▅▅▅▆▆▆▆▆▆▆▆▆▆▆▆▆▆▅▅▅▅▅▅▅▅▄▄▄▄▃You can empty! an AudioSchedule and reuse it.
julia> empty!(audio_schedule)
julia> audio_schedule
0.0 s 44100.0 Hz AudioScheduleAudioSchedules.Cycles — TypeCycles(frequency)Cycles from 0 to 2π to repeat at a frequency (with frequency units, like Hz). Supports make_series.
julia> using AudioSchedules
julia> using Unitful: Hz
julia> first(make_series(Cycles(440Hz), 44100Hz))
0.06268937721449021AudioSchedules.Grow — TypeGrow(start_level, duration, end_level)Exponentially grow or decay from start_level to end_level over a duration in time units like s. Supports make_series.
julia> using AudioSchedules
julia> using Unitful: Hz, s
julia> first(make_series(Grow(0.1, 1s, 1), 44100Hz))
0.10000522141770128AudioSchedules.Line — TypeLine(start_level, duration, end_level)A line from start_level to end_level with a duration in time units like s. Supports make_series.
julia> using AudioSchedules
julia> first(make_series(Line(0, 1s, 1), 44100Hz))
2.2675736961451248e-5AudioSchedules.Map — TypeMap(a_function, synthesizers...)Map a_function over synthesizers. Supports make_series.
julia> using AudioSchedules
julia> first(make_series(Map(sin, Cycles(440Hz)), 44100Hz))
0.06264832417874369AudioSchedules.SawTooth — MethodSawTooth(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.9185207636218614AudioSchedules.Scale — Typefunction Scale(ratio)A simple wrapper that will multiply inputs by the ratio.
julia> using AudioSchedules
julia> Scale(3)(2)
6AudioSchedules.duration — Methodduration(audio_schedule::AudioSchedule)Find the duration of an AudioSchedule in seconds.
julia> using AudioSchedules
julia> audio_schedule = AudioSchedule();
julia> push!(audio_schedule, Map(sin, Cycles(440Hz)), 0s, @envelope(
0,
Line => 1s,
1,
Line => 1s,
0,
))
julia> duration(audio_schedule)
2.0 sAudioSchedules.make_series — Methodmake_series(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.
Base.push! — Methodpush!(audio_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_series, start_time has units of time (like s), and the rest of the arguments specify the shape of the envelope.
For all envelope segment, call
segment(shape, start_level, duration, end_level)duration should have units of time (like s). For example,
push!(audio_schedule, synthesizer, start_time, @envelope(0, Line => 1s, 1, Line => 1s, 0))will call segment twice:
segment(Line, 0, 1s, 1)
segment(Line, 1, 1s, 0)AudioSchedules.@envelope — Macro@envelope(arguments...)Create an envelope. Start with the start amplitude (a number between 0 and 1). Then, specify a pair, segment_function => duration, where duration is the duration of the segment. Then, specify the end amplitude (again, a number between 0 and 1). So for example,
For example, @envelope(0, Line => 1s, 1) will create an envelope with 1 segment. The segment is created with segment_function(start_level, duration, end_level) => duration. So, for this example, the segment will be Line(0, 1s, 1) => 1s.
After you finished your first segment, you can add as many more segments as you'd like. The end level of the previous segment will be the start level of the next segment.
For example, @envelope(0, Line => 1s, 1, Line => 1s, 0) will create an envelope with 2 segments:
Line(0, 1s, 1) => 1sLine(1, 1s, 0) => 1s
julia> using AudioSchedules
julia> @envelope(0, Line => 1s, 1, Line => 1s, 0)
(Line(0.0, 1.0 s⁻¹) => 1.0 s, Line(1.0, -1.0 s⁻¹) => 1.0 s)
julia> @envelope(1, 2, 3)
ERROR: LoadError: ArgumentError: 2 is not a pair
[...]