Wavify is a Ruby audio processing toolkit with immutable transforms, codec I/O, streaming pipelines, DSP effects, and a sequencing DSL.
Use it to:
- Read, inspect, transform, and write audio from Ruby scripts.
- Process large files with streaming pipelines and stateful DSP effects.
- Generate tones, small arrangements, and test fixtures without mandatory FFmpeg or SoX.
- Ruby
>= 3.1 - Bundler
- Optional native build environment for OGG Vorbis gems (
ogg-ruby,vorbis)
Add to your Gemfile:
gem "wavify"Then install:
bundle installOr install directly:
gem install wavifyWAV, AIFF, FLAC, and raw PCM work without OGG dependencies. Add ogg-ruby and vorbis to your Gemfile only when you need .ogg / .oga support.
require "wavify"
format = Wavify::Core::Format::CD_QUALITY
audio = Wavify::Audio.tone(
frequency: 440.0,
duration: 1.0,
waveform: :sine,
format: format
)
audio.fade_in(0.02, curve: :exp).fade_out(0.05, curve: :log).bit_depth(16, dither: true).write("tone.wav")Codec-specific write options are forwarded with codec_options::
audio.write("master.flac", codec_options: { block_size: 2048 })
audio.write("preview.ogg", codec_options: { quality: 0.5 })
audio.write("tagged.wav", codec_options: { info: { title: "Tone", artist: "Wavify" } })
audio.write("master.wav", overwrite: false)Main constructors:
Audio.read(path_or_io, format: nil, codec_options: {}, strict: false, filename: nil)Audio.metadata(path_or_io, format: nil, codec_options: {}, strict: false, filename: nil)Audio.info(path_or_io, format: nil, codec_options: {}, strict: false, filename: nil)Audio.stream(path_or_io, chunk_size: 4096, format: nil, codec_options: {}, strict: false, filename: nil)Audio.tone(frequency:, duration:, waveform:, format:)Audio.silence(duration_seconds, format:)Audio.mix(*audios, strategy: :clip, gains: nil, align: :start)
Immutable transforms (each also has ! in-place variants):
gain,normalize,trim,fade_in,fade_out,pan,reverse,loop,applyconcat,append,prepend,overlay,crossfade,slice,crop,pad_start,pad_end,insert_silenceto_mono,to_stereo,resample,bit_depth,map_samples,map_frames
Utility methods:
convert,split(at:),duration,sample_frame_count,channels,sample_rate,frames,each_framepeak_amplitude,rms_amplitude,peak_dbfs,rms_dbfs,lufs,stats,silent?,clipped?,dc_offset,zero_crossing_rate
Mix strategies are :clip (default), :normalize, :headroom, and :soft_limit. gains: accepts one dB value per source, and align: can be :start, :center, or :end.
Normalize modes are :peak, :rms, and :lufs.
Use bit_depth(16, dither: true) when reducing PCM bit depth and you want simple TPDF dither.
Duration helpers:
Wavify.ms(250)
Wavify.seconds(3)
Wavify::Core::Duration.parse("1:23.456")Wavify::Audio.stream("input.wav", chunk_size: 4096)
.pipe(Wavify::Effects::Compressor.new(threshold: -18, ratio: 3.0), name: :comp)
.map_chunks(name: :trim_preview) { |chunk| Wavify::Audio.new(chunk).trim(threshold: 0.005).buffer }
.meter { |stats| puts stats[:peak_dbfs] }
.tee("preview.wav")
.write_to("output.aiff", format: Wavify::Core::Format::CD_QUALITY)pipe accepts processors that respond to call, process, or apply.
Stateful processors may implement reset, flush(format:), and tail_duration.
Use take_duration, drop_duration, to_audio, progress, meter, tee, and pipeline_steps for common chunk workflows and inspection.
Use dry_run(format:) to validate a stream pipeline without writing output.
write_to also accepts codec-specific output options:
stream.write_to("output.flac", codec_options: { block_size_strategy: :fixed, block_size: 2048 })| Format | Read | Write | Stream Read | Stream Write | Notes |
|---|---|---|---|---|---|
| WAV | ✅ | ✅ | ✅ | ✅ | PCM + float WAV, extensible WAV, BWF metadata, RF64 read metadata |
| AIFF | ✅ | ✅ | ✅ | ✅ | PCM AIFF plus uncompressed AIFF-C NONE / sowt |
| FLAC | ✅ | ✅ | ✅ | ✅ | Pure Ruby implementation with comments, mid-side, and LPC write options |
| OGG Vorbis | ✅ | ✅ | ✅ | ✅ | Optional ogg-ruby + vorbis gems |
| Raw PCM/Float | ✅* | ✅ | ✅* | ✅ | format: is required for read/stream-read/metadata |
Raw example:
raw_format = Wavify::Core::Format.new(channels: 2, sample_rate: 44_100, bit_depth: 16, sample_format: :pcm)
audio = Wavify::Audio.read("input.pcm", format: raw_format)
audio.write("output.wav")For IO objects without magic bytes, pass filename: as a codec hint:
io = StringIO.new(raw_bytes)
audio = Wavify::Audio.read(io, filename: "input.raw", format: raw_format)Metadata example:
metadata = Wavify::Audio.metadata("input.wav")
metadata[:format].sample_rate
metadata[:duration]Codec registry helpers:
Wavify::Codecs.supported_formats
Wavify::Codecs.available_formats
Wavify::Codecs.detect("input.wav")
Wavify::Codecs.register(".custom", MyCodec)
Wavify::Adapters.knownread/stream_readsupport sequential chained streams and interleaved multi-stream OGG.- Interleaved multi-stream decode is mixed into one output stream.
- If interleaved streams have different sample rates, they are resampled to the first logical stream's sample rate before mix.
decode_mode: :strictanddecode_mode: :placeholderare accepted for API compatibility.- OGG support is optional;
wavify doctorreports whether the native gems are installed. - MP3, AAC, FFmpeg, MIDI, and spectrogram support are adapter-gem boundaries; use
Wavify::Adapters.load(:ffmpeg)after installing a matching adapter gem.
Use Wavify.build for one-shot rendering/writing, or Wavify::DSL.build_definition when you want timeline access.
song = Wavify::DSL.build_definition(format: Wavify::Core::Format::CD_QUALITY, tempo: 116, swing: 0.55, default_bars: 2) do
sample_folder "samples"
key :c, :minor
track :kick do
synth :sine
notes "C2/8. . C2/8t .", resolution: 16
envelope attack: 0.001, decay: 0.05, sustain: 0.0, release: 0.06
gain(-4)
end
track :pad do
chords ["Cmaj7/E@drop2"], voicing: :open
end
arrange do
section :intro, bars: 1, tracks: %i[kick], markers: [:start]
section :bridge, bars: 1, tracks: %i[kick pad], tempo: 92, beats_per_bar: 3, markers: [:bridge]
end
end
timeline = song.timeline
song.timeline_json
song.timeline_text
stems = song.render(stems: true)
mix = song.render
mix.write("song.wav")Pattern steps support rests (-/.), normal triggers (x, velocity 0.8), accents (X, velocity 1.0), explicit velocity suffixes (x0.5), probability metadata (x?50), and ratchets (x:3).
Note tokens support fixed durations (C4/8), dotted values (C4/8.), triplets (C4/8t), and ties (D4~ D4).
Use key :c, :minor for simple scale quantization, slash chords for inversions, and @drop2 / @open or voicing: for chord voicings.
Arrangement sections can carry tempo:, beats_per_bar:, and markers:.
Swing values start at 0.5 for straight timing; values such as 0.55 delay off-beat steps on even grids.
Sample tracks can use sample_folder, per-sample pitch: semitones, preset :lofi_drums, and Wavify::DSL.validate for pre-render checks.
Built-in modules:
- Oscillator waveforms:
:sine,:square,:sawtooth,:triangle,:pulse,:white_noise,:pink_noise - Envelope (AHDSR with optional segment curves)
- Automation and LFO modulation helpers
- Biquad filters (lowpass/highpass/bandpass/notch/peaking/shelves)
- Effects:
Delay,Reverb,Chorus,Vibrato,Flanger,Phaser,Distortion,Compressor,Limiter,SoftLimiter,NoiseGate,Expander,Tremolo,AutoPan,StereoWidener,Bitcrusher,EQ - Preset chains:
MasteringChain,PodcastChain
Register custom processors for pipelines and DSL tracks:
Wavify::Effects.register(:my_effect, MyEffect)
Wavify::DSL.effect(:my_effect, MyEffect)Envelope supports hold: and curve: :linear | :exp | :log. Reverb supports pre_delay: for delaying only the wet path and width: for stereo wet width.
Scripts in examples/:
examples/format_convert.rbexamples/audio_processing.rbexamples/synth_pad.rbexamples/drum_machine.rbexamples/chill_vibes.rbexamples/hybrid_arrangement.rbexamples/streaming_master_chain.rbexamples/cinematic_transition.rb
Run:
ruby examples/synth_pad.rbSee examples/README.md for the full list.
The gem includes a small CLI for common scripting tasks:
wavify info input.wav
wavify convert input.wav output.flac
wavify tone --freq 440 --duration 1 tone.wav
wavify normalize input.wav output.wav --target -1
wavify trim input.wav output.wav --threshold 0.01
wavify chain input.wav output.wav --gain -3 --fade-in 0.02 --fade-out 0.05
wavify render song.rb out.wav --tempo 120 --swing 0.55 --bars 4
wavify timeline song.rb --tempo 120 --bars 4
wavify formats
wavify doctordocs/getting-started.mddocs/codecs.mddocs/dsp.mddocs/streaming.mddocs/sequencer.mddocs/limitations.mddocs/performance.mdROADMAP.md- YARD docs can be generated with
bundle exec rake docs:yard.
- MP3, AAC, and M4A are not built into core.
- FFmpeg and SoX are not mandatory runtime dependencies.
- OGG Vorbis uses optional native gems (
ogg-ruby,vorbis). - Raw PCM/float requires
format:for read, stream read, and metadata. - Streaming writes for header-based formats require seekable output IO.
- Resampling defaults to linear interpolation; pass
resampler: :windowed_sincfor higher-quality offline conversion.
Install dependencies:
bundle installRun tests:
bundle exec rspec
bundle exec rake spec:coverage COVERAGE_MINIMUM=90
bundle exec rake types:validateGenerate/check docs:
bundle exec rake docs:examples
bundle exec rake docs:yard
YARD_MINIMUM=85 bundle exec rake docs:check
bundle exec rake docs:alldocs:examples smoke-runs the self-contained example scripts. docs:yard generates YARD output under doc/. docs:check enforces the configured YARD documentation percentage.
Benchmarks:
bundle exec rake bench:wav_io
bundle exec rake bench:dsp
bundle exec rake bench:flac
bundle exec rake bench:stream
bundle exec rake bench:all
bundle exec rake bench:baseline
bundle exec rake bench:compareRelease checks:
bundle exec rake release:checkWavify is released under the MIT License.