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_melodyFunction

generatemelody(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.
  • bpm: Beats per minute.

Keyword Arguments

  • waveform: Either a function phase -> sample (default is sine_wave) or an array of weights used by default_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 function t -> 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.

source

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.playFunction
play(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).
source
PulseArchitect.saveFunction
save(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) where samples is a vector of Float64 and fs is the sample rate.
  • name: The desired base name for the file.
source