Pulse Architect
In order to apply effects or chains of effects to an audio signal, we first have to create an audio signal. One way to do so is the generate_melody function. To use it, specify a melody as a Vector of Tuples. Each Tuple contains three elements: the note name (e.g., "C4"), its duration in beats, and its amplitude. Generate melody can use this to generate an initial audio signal, using optional parameters, such as a waveform generator, the sample rate, portamento time (smoothly transitioning between notes), a decay function (for plucked note sound) and more.
PulseArchitect.generate_melody
— Functiongeneratemelody(notes, bpm; waveform, portamentotime, fs, decayfunc, endpause_mult)
Generates a raw audio signal (melody) that transitions smoothly between notes, inserting silence as specified.
Arguments
notes
: A vector where each element is either:- A
String
representing the note (e.g., "C5")—for which default values are assumed, or - A tuple
(note, duration, loudness)
:note
: String (e.g., "C5"); special strings like"--"
,"-"
, or" "
denote a pause.duration
: Note length multiplier (in beats).loudness
: Amplitude scaling factor.
- A
bpm
: Beats per minute.
Keyword Arguments
waveform
: Either a functionphase -> sample
(default issine_wave
) or an array of weights used bydefault_waveform
.portamento_time
: Crossfade duration in seconds between adjacent notes (if no pause is inserted). If too large, it will be capped so that crossfade indices remain within bounds.fs
: Sample rate (default 44100 Hz).decay_func
: A functiont -> amplitude
applied to each note (default returns 1.0).end_pause_mult
: Multiplier (in beats) for a final appended pause if none was explicitly provided (default 0.2).
Behavior
- For each note, computes its duration in seconds (using the beat multiplier and bpm) and generates a signal.
- For non-pause notes, the note's frequency is determined via
note_to_freq(note)
, and the waveform is generated accordingly. - A fade-in and fade-out is applied over a crossfade length (portamento), which is automatically limited so that indices never fall outside the signal boundaries.
- When consecutive non-pause notes are adjacent, they are crossfaded over an effective crossfade window.
- A final pause is appended at the end if none was selected.
Returns a tuple (overall, fs)
where overall
is the generated audio signal.
Every audio object is a tuple of the form (audio_signal, fs)
where audio_signal
is an array of floats representing the audio waveform and fs
is the sample rate in Hz. This allows us to manipulate the audio signal using various functions provided by PulseArchitect.
We can play an audio Tuple via the play
function. This does not block the execution of other commands, as it is started in a separate thread. Audio Signals can be saved via the save
function.
PulseArchitect.play
— Functionplay(audio_tuple::Tuple{Vector{Float64}, Int}; volume=1.0)
Plays the given audio signal asynchronously without blocking the main Thread. The audio samples are scaled by the given volume factor before playback.
audio_tuple
: A tuple(samples, fs)
where:samples
is a vector of Float64 audio samples (normalized, e.g., in [-1, 1]).fs
is the sample rate (in Hz).
volume
: A multiplier for the audio amplitude (default is 1.0).
PulseArchitect.save
— Functionsave(audio_tuple::Tuple{Vector{Float64}, Int}, name::AbstractString)
Saves the audio tuple (samples, fs) as a WAV file. If the provided name does not end with ".wav", it appends the extension. Returns the full filename.
Arguments
audio_tuple
: A tuple(samples, fs)
wheresamples
is a vector of Float64 andfs
is the sample rate.name
: The desired base name for the file.