For music

A "Gen" has a method to implement which has the signature -

genproc(::Gen, ::Bus, t::Float64, rt::Float64) -> Tuple{Float64,Gen}

The genproc method is expected to schedule any signals to the bus using sched(::Bus,t,::Signal) and return info about the next gen to trigger later. The t argument to genproc is "clock time" and not real time - i.e. it is determined by the clock which the bus runs. The rt argument is the real time.

Missing docstring.

Missing docstring for Synth.Gen. Check Documenter's build log for details.

Synth.seqFunction
seq(ga :: GA, gb :: GB) :: Gen where {GA <: Gen, GB <: Gen}

Sequences the given two gens to occur one after the other. When the first gen ga results in a Cont, it will switch to gb and after that will continue onwards.

source
Synth.trackFunction
track(gs :: AbstractVector) :: Gen

A "track" is a heterogeneous sequence of Gens. When each gen finishes, its genproc call will produce a Cont which indicates it is time to switch to the next Gen in the track. This is like a generalization of seq except that seq has types known at construction time.

source
Synth.durnFunction
durn(d :: Real, gen :: Gen) :: Gen

Forces the duration of the given gen to be the given d, even if it was something else. Possibly useful with chord.

source
Synth.chordFunction
chord(gens :: AbstractVector) :: Gen

A Gen which runs all the given heterogeneous gens in lock step. The duration of the chord is given by the maximum of the durations of all the gens every time they run. Whenever a gen produces a Cont or Stop, it is taken out of the set of gens and the other continue on.

Broadly, chord is useful when all the gens have the same duration - very much like a chord in music. If you're thinking of whether you should use this to start off multiple musical processes in parallel with each determining its own duration, you probably want par.

source
Synth.parFunction
par(gens :: AbstractVector) :: Par

For parallel composition of gens given as a vector. The Par itself has effectively zero duration and when sequenced as part of a track will result in the following gen being immediately processed without delay. The individual gens part of each of the "threads" will continue to be processed by the bus over time until they finish.

source
Synth.loopFunction
loop(n :: Int, g :: Gen) :: Gen

Will loop the given gen n times before ending.

source
Synth.dynFunction
dyn(fn :: Function, n :: Int, i :: Int) :: Gen

A "dyn" gen calls the function with each i to determine the gen that should be performed. The function will be called with two arguments i and n where i will range from 1 to n.

source
Synth.recFunction
rec(fn :: Function, c :: C, s :: S) :: Gen where {C,S}

Constructs a recursive process using the given function. The function takes the current state and is expected to return a tuple of a Gen and the next state. If the returned gen is Cont, then the gen is considered finished and will move on to the next gen in whatever context it was invoked.

Iterating an octave

For example, if you want a gen which iterates through the pitches of an octave you can do it this way (though you can accomplish it using track as well).

function octave(p :: Real, i :: Int)
    if i <= 12
        (tone(p + i, 0.25), i+1)
    else
        (Cont(), i+1)
    end
end
b = bus()
sched(b, rec(octave, 60, 0::Int))
play(b, 4.0)

rec is therefore a general mechanism to implement stateful recursive time evolution at a level higher than signals. Note that the function called by rec can itself return another recursive process as a part of its evolution.

source
Synth.pingFunction
ping(pitch :: Real, dur :: Real, vel :: Real = 0.5f0, decay :: Real = dur) :: Gen
ping(pitch :: AbstractVector{R}, dur :: Real, vel :: Real = 0.5f0, decay :: Real = dur) :: Gen where {R <: Real}
ping(pitch :: AbstractVector{R}, dur :: AbstractVector{RD}, vel :: Real = 0.5f0, decay :: Real = dur) :: Gen where {R <: Real, RD <: Real}
ping(pch :: PitchChord, dur :: Real, vel :: Real = 0.5f0, decay :: Real = dur) :: Gen

A "ping" is a simple decaying sine tone for illustrating how to create a Gen. If you use the array versions, they're made into corresponding tracks for ease of use. So ping([60,67,72], 0.5) would be a three-note track. If the duration is also an array, it will be cycled through for each of the pitch values. So the number of pings is determined only by the number of pitches given.

source
Synth.toneFunction
tone(pitch :: Real, dur :: Real, vel :: Real = 0.5f0; release_secs :: Real = 0.05) :: Gen
tone(pitch :: AbstractVector{R}, dur :: Real, vel :: Real = 0.5f0; release_secs :: Real = 0.05) :: Gen where {R <: Real}
tone(pitch :: AbstractVector{R}, dur :: AbstractVector{RD}, vel :: Real = 0.5f0; release_secs :: Real = 0.05) :: Gen where {R <: Real, RD <: Real}
tone(pch :: PitchChord, dur :: Real, vel :: Real = 0.5f0, release_secs :: Real = 0.05) :: Gen

tone is related to ping in that it will sustain a tone for the given duration as opposed to ping which will immediately start releasing the tone. In other words, a ping is a note with zero duration. However, the note is configured with a default short release time where the default release of a ping is determined by its duration.

Note that you can force the duration of a gen to be whatever you want using durn.

source
tone(wt :: WaveTone, pitch, dur, vel = 0.5f0; release_secs = 0.05)

With this, you can take an existing configured wavetable tone and assign a different pitch/dur/vel characteristic to it.

source
Synth.chFunction
ch(pitches :: AbstractVector{R}) :: PitchChord where {R <: Real}

Represents a set of chorded pitch values. Usable with ping.

source
Synth.snippetFunction
snippet(filename::AbstractString, selstart :: Float64 = 0.0, selend :: Float64 = Inf) :: Gen

A Gen that plays a fragment of the given audio file. It uses sample under the hood and therefore relies on its caching mechanism for speedy schedule of sample fragment playback.

Note that both snippet and sample do not support resampling or playing back to clocks that vary their speeds from real time. So you need to be careful with duration computation. For example, when scheduling on to a bus running at a tempo of 120bpm, you'll need to double your durations using durn in order to synchronize with the end of the snippet. Otherwise, the next Gen will start playing half way through the snippet.

source

MIDI specific

Note that MIDI sequencing is unfinished. Particularly it requires accurate scheduling.

Synth.midimsgFunction
midimsg(dev::PortMidiDest, msg::MIDIMsg)

Wraps sending an instantaneous MIDI short message to the given output device.

source
Synth.midinoteFunction
midinote(chan::Int, note::Int, vel::Real, dur::Real, logicaldur::Real = dur; port=:midi)

Produces a MIDI note of given duration. The note off will be scheduled appropriately and the note will be set to last the given duration. The logicaldur parameter gives the logical duration of the note which is taken to be the same as the time interval between noteon and noteoff by default.

source
Synth.miditriggerFunction
miditrigger(dev::PortMidiDest, chan::Int, note::Int, vel::Real, logicaldur::Real)

A triggered MIDI message only has a noteon - such as for a drum hit, which does not require a note off since the drum voices have a natural cut off point.

source
Synth.midiseqFunction
midiseq(seq::AbstractVector{Tuple{Float64, MIDIMsg}}) :: MidiSeq

A simple sequence of MIDI messages to be sent at given times relative to start of the Gen. This could technically be constructed as a track as well, but permits a more general sequence of short messages to be sent.

source