BoxLang 🚀 A New JVM Dynamic Language Learn More...
|:------------------------------------------------------: |
| ⚡︎ B o x L a n g ⚡︎
| Dynamic : Modular : Productive
|:------------------------------------------------------: |
Copyright Since 2023 by Ortus Solutions, Corp
www.boxlang.io | www.ortussolutions.com
BoxLang Audio makes sound from code. Synthesize instruments and drums, arrange them on a beat grid, mix tracks with ducking, add effects and save the result as WAV, MP3 or FLAC. It runs entirely on the JVM, so there is no Python or native tool to install, and a track made on your laptop renders the same on a server. WAV, MP3 and FLAC need nothing else; OGG, Opus, AAC and video files use FFmpeg, which the module finds on your computer or downloads for you.
Key Features:
Audio scripts are usually Python and numpy glued to ffmpeg. Here the whole pipeline is BoxLang functions that take a buffer and return a new one, so a song reads like a recipe:
t = audioTimeline( bpm=128 );
kick = audioDrum( "kick" );
bass = audioFilter( audioEnvelope( audioOsc( "saw", audioFreq( "A1" ), 0.4 ), 0.003, 0.12 ), "lowpass", 900, 2 );
for( at in audioBeats( t, 0, 16 ) ){
audioAdd( timeline=t, sound=kick, at=at, track="drums" );
}
audioAdd( timeline=t, sound=bass, at=t.bar( 1 ), track="bass", gainDb=-6 );
audioPump( t, "bass", audioBeats( t, 0, 16 ), -5, 0.18 );
audioSave( audioSaturate( audioRender( t ) ), "groove.mp3" );
java.desktop module)libs/ folder (see Licensing)# Install into your BoxLang home
install-bx-module bx-audio
# Or in a CommandBox project
box install bx-audio
// Make a one second A440 tone, fade it and save it
tone = audioOsc( "sine", 440, 1 );
tone = audioFade( audio=tone, fadeIn=0.01, fadeOut=0.2, curve="smooth" );
audioSave( tone, "tone.wav" );
// Read it back and inspect it
println( audioInfo( "tone.wav" ) );
Every function returns a new buffer and leaves its input untouched. BoxLang does not allow mixing positional and named arguments in one call, so use all of one style.
| Function | Purpose |
|---|---|
audioSilence( seconds, channels=2, sampleRate=44100 )
| Create a blank audio buffer |
audioLoad( path )
| Read a WAV, MP3 or FLAC file, or any other format through FFmpeg, detected by content. An MP3 gives back the length it was saved with |
audioSave( audio, path, bitDepth=16, bitrate=0 )
| Write by the extension: WAV (16, 24 or 32 bit float), MP3
(bitrate in kbps, default 192 or 128 below 32 kHz,
mono or stereo), FLAC, or OGG, Opus, M4A and AAC through FFmpeg |
audioInfo( source, levels=false )
| Channels, sample rate, frames and seconds of a buffer or
a file, and with levels=true the peak and RMS |
audioWaveform( audio, points=800 )
| The loudest level in each of points equal
slices, as an array for drawing a waveform |
audioGain( audio, gainDb )
| Change the volume in decibels |
audioPan( audio, pan )
| Place a sound from -1 (left) to 1 (right) with a constant power law. Mono becomes stereo, stereo acts as a balance |
audioFade( audio, fadeIn=0, fadeOut=0,
curve="linear" )
| Fade in and out, curve is
linear or smooth
|
audioNormalize( audio, targetDb=-1 )
| Scale the peak to a target level |
audioTrim( audio, start=0, end )
| Cut a section, in seconds |
audioConcat( audios )
| Join buffers end to end, formats must match |
audioReverse( audio )
| Play it backwards |
audioMix( audios )
| Add buffers together so they play at the same time, as long as the longest |
audioChannels( audio, channels )
| Convert between mono and stereo |
audioResample( audio, sampleRate )
| Change the sample rate with a windowed sinc filter, with anti-aliasing when lowering it |
audioTags( path, tags )
| Read the tags of a WAV, MP3 or FLAC file, with the cover and chapters, or write them. Changes are atomic |
audioEncode( audio, format="wav", bitDepth=16,
bitrate=0 )
| Audio as the bytes of a file, without touching the disk |
audioDecode( data, format="" )
| Audio from bytes in memory, such as an upload, the format found from the content |
Sounds are made from code, with no samples to download or license. Generators return mono buffers at a known level, so they mix predictably.
| Function | Purpose |
|---|---|
audioFreq( note )
| A note name ("C4",
"F#3", "Bb2") or
MIDI number to Hz, A4 = 440. 60.1 is ten cents
sharp of C4 |
audioOsc( shape, freq, seconds, sampleRate=44100,
harmonics=16, partials, width=0.5, phase=0 )
| sine, saw, square,
triangle (band limited, peak about 1),
pulse (naive digital, width 0.125 /
0.25 / 0.5 / 0.75 for NES style) or additive from
[ multiple, amplitude ] pairs |
audioSweep( startHz, endHz, seconds, speed=28,
sampleRate=44100 )
| A sine that glides between pitches, the core of a kick or a laser |
audioNoise( seconds, sampleRate=44100, seed=1,
color="white" )
| Repeatable noise: white, pink,
brown or blue, at RMS 0.25 |
audioEnvelope( audio, attack=0.005, decay=0 )
| Linear attack then exponential decay, decay
is the time constant |
audioDrum( type, sampleRate=44100, seed=1, pitch=1,
decay=1, level=1, seconds )
| kick, snare, hat,
openhat, clap, tom,
crash, riser, peak-normalized to level
|
audioFilter( audio, type, cutoff, q=0.7071 )
| lowpass, highpass,
bandpass or notch
|
audioSampler( audio, rootNote="C4",
loop=false, loopStart=0, loopEnd=0, crossfade=0.02 )
| An instrument made from a recording, optionally with a looped sustain |
audioNote( sampler, note, seconds=0, velocity=1,
attack=0.003, release=0.05 )
| One note, or a chord from an array of notes, played from a sampler |
The same seed always gives the same sound on any machine. It does not match numpy's random stream, so a port of a numpy script sounds the same but is not bit identical.
// A plucked bass note and a kick, mixed on a beat grid
t = audioTimeline( bpm=128 );
pluck = audioEnvelope( audioOsc( "saw", audioFreq( "A1" ), 0.4 ), 0.003, 0.12 );
pluck = audioFilter( pluck, "lowpass", 900, 2 );
kick = audioDrum( "kick" );
for( at in audioBeats( t, 0, 16 ) ){
audioAdd( timeline=t, sound=kick, at=at, track="drums" );
}
audioAdd( timeline=t, sound=pluck, at=t.bar( 1 ), track="bass", gainDb=-6 );
audioSave( audioSaturate( audioRender( t ) ), "out/groove.wav" );
Echo and reverb return a longer buffer so the tail rings out instead
of being cut off. mix is a dry/wet crossfade from 0 to 1.
| Function | Purpose |
|---|---|
audioDelay( audio, time=0.35, feedback=0.4, mix=0.35,
pingPong=false, tail )
| Echo. Repeats run until 60 dB down (at most 10 s) unless
tail is given. pingPong bounces them
between stereo sides |
audioReverb( audio, room=0.5, damp=0.5, mix=0.3, tail )
| Freeverb style reverb: 8 damped combs into 4 allpass filters, stereo spread. Large rooms build up level on sustained sounds, so lower the mix |
audioChorus( audio, rate=0.8, depth=0.003, mix=0.5 )
| A slowly swinging short delay for width and thickness |
audioConvolve( audio, impulse, mix=0.3 )
| Convolution reverb: the sound played through the recorded echo of a real space (an impulse response up to 20 seconds) |
A curve is a value that changes over time. Point it at the volume, position, filter or pitch of a sound, or of a track on a timeline.
| Function | Purpose |
|---|---|
audioLfo( shape="sine", rate=1, depth=1,
center=0, phase=0, fadeIn=0, seed=0 )
| A curve that swings around center
(sine, triangle, saw,
square or random) |
audioCurve( points, interpolation="linear" )
| A curve through points ({ at, value }),
linear, smooth or step
|
audioCurveAt( curve, seconds )
| What a curve says at a time |
audioModulate( audio, param, curve,
filter="lowpass", q=0.7071 )
| Drive gainDb, pan,
cutoff or pitch of a sound with a
curve: tremolo, auto pan, filter sweeps, vibrato |
audioAutomate( timeline, track, param, curve )
| Move a track's gainDb or pan
during the render |
// A snare with a dotted eighth ping pong echo and a hall
t = audioTimeline( bpm=100 );
snare = audioChannels( audioDrum( type="snare" ), 2 );
echoed = audioDelay( audio=snare, time=t.beat( 0.75 ), feedback=0.45, mix=0.3, pingPong=true );
hall = audioReverb( audio=echoed, room=0.7, damp=0.4, mix=0.25 );
audioSave( hall, "out/snare-hall.wav" );
A timeline holds named tracks at one sample rate and a tempo grid. Sounds are placed on tracks, ducking and track volumes are applied when it is rendered, so the stored tracks are never altered.
| Function | Purpose |
|---|---|
audioTimeline( bpm=120, beatsPerBar=4, sampleRate=44100,
channels=2 )
| Create a timeline. t.beat( n ) and
t.bar( n ) turn beats and bars into seconds |
audioAdd( timeline, sound, at=0, track="main",
gainDb=0, pan=0 )
| Place a sound at a time. Other sample rates are converted automatically, mono sounds are panned |
audioBeats( timeline, start=0, end, step=1 )
| List moments on the beat grid in seconds, ready for
audioAdd( at= ) or audioPump( times= )
|
audioTrackGain( timeline, track, gainDb )
| Set the volume of a whole track |
audioPump( timeline, track, times, amountDb=-5,
recovery=0.18 )
| Dip a track at given moments, like a kick drum sidechain |
audioDuck( timeline, track, trigger, amountDb=-12,
thresholdDb=-40, attackMs=10, releaseMs=300 )
| Lower a track while another is loud, like music under a voice-over |
audioRender( timeline, tail=0 )
| Mix all tracks down to one buffer. The timeline can be changed and rendered again |
audioSaturate( audio, drive=2.2, level=0.9 )
| Peak-normalize and round off peaks with a tanh curve, a warm limiter for the final mix |
BoxLang does not allow mixing positional and named arguments in one call. Use all positional (
audioPump( t, "music", times, -4, 0.1 )) or all named (audioAdd( timeline=t, sound=kick, at=t.beat( 4 ), track="drums" )).
t = audioTimeline( bpm=128 );
voice = audioLoad( "voice.wav" ); // any sample rate, it is converted for you
audioAdd( timeline=t, sound=music, at=0, track="music", gainDb=-10 );
audioAdd( timeline=t, sound=voice, at=t.bar( 2 ), track="voice" );
audioPump( t, "music", audioBeats( t, 0, 64 ), -4, 0.15 ); // pump on every beat
audioDuck( t, "music", "voice", -9 ); // and duck under the voice
mix = audioFade( audioRender( timeline=t, tail=1 ), 0, 1, "smooth" );
audioSave( audioSaturate( mix ), "out/trailer-mix.wav" );
An audio buffer also exposes getChannels(),
getFrames(), getSampleRate(),
getDurationSeconds(), getPeak(),
getRms() and copy().
// Bring a mono 22.05kHz voice-over up to a 44.1kHz stereo mixdown format
voice = audioLoad( "voice.wav" );
voice = audioResample( voice, 44100 );
voice = audioChannels( voice, 2 );
voice = audioNormalize( voice, -3 );
voice = audioFade( audio=voice, fadeIn=0.05, fadeOut=0.3, curve="smooth" );
audioSave( voice, "out/voice-stereo.wav", 24 );
println( audioInfo( "out/voice-stereo.wav" ) );
Loudness is measured as ITU-R BS.1770 and EBU R128 define it
(K-weighting, gated blocks, true peak), the numbers streaming services
and broadcasters use. Checked against FFmpeg's ebur128 on
rendered music: within 0.1 LU.
| Function | Purpose |
|---|---|
audioLoudness( audio )
| integrated LUFS, range LU,
truePeakDb and samplePeakDb dBFS,
momentaryMax and shortTermMax LUFS.
Silence reads -120 |
audioNormalizeLoudness( audio, targetLufs=-14,
truePeakDb=-1 )
| Set the loudness, with a limiter keeping peaks under the ceiling |
audioMaster( audio, target="streaming",
truePeakDb=-1, glue=false )
| The same with presets: streaming -14,
podcast -16, game -16,
broadcast -23 (EBU R128), cinema -24,
or a number. glue compresses lightly first |
audioCompress( audio, thresholdDb=-18, ratio=4,
attackMs=10, releaseMs=100, kneeDb=6,
makeupDb="auto" )
| A compressor. auto make-up matches the
loudness of the input |
audioLimit( audio, ceilingDb=-1, releaseMs=100,
lookAheadMs=5 )
| A look-ahead limiter: no sample ends above the ceiling |
audioEq( audio, bands )
| Parametric EQ. Each band is { type, freq, gainDb, q
} with type
peak, lowshelf,
highshelf, lowpass,
highpass or notch
|
mix = audioRender( timeline=t );
println( audioLoudness( mix ).integrated ); // -19.5
final = audioMaster( audio=mix, target="streaming" ); // -14 LUFS, true peak under -1 dBFS
| Function | Purpose |
|---|---|
audioStretch( audio, rate )
| Change the speed without changing the pitch.
1.25 is faster and shorter, between 0.25 and 4 |
audioFitTo( audio, seconds )
| Change the length to exactly this many seconds, keeping the pitch. Keep the change within about 25 percent for natural results |
audioPitch( audio, semitones )
| Change the pitch without changing the length, within 24 semitones |
This is WSOLA (waveform similarity overlap-add), written for this module: good on music and speech, with the usual limits of the method on dense chords at large changes. Pitch shifting does not keep the character of a voice (formants move with the pitch).
| Function | Purpose |
|---|---|
audioBpm( audio, minBpm=60, maxBpm=200 )
| bpm (0 when there is no clear pulse),
confidence and the beats in seconds |
audioSpectrum( audio, bands=16, minHz=40, maxHz=16000,
start=0, seconds=0 )
| Level in dB of log spaced bands |
audioSpectrogram( audio, bands=16, frames=64, minHz=40,
maxHz=16000 )
| Band levels over time, for drawing |
audioFft( audio, size=2048, start=0 )
| The raw frequencies and
magnitudes of one window |
audioDetectSilence( audio, thresholdDb=-50,
minSeconds=0.3 )
| The silent stretches as { start, end, duration }
|
audioTrimSilence( audio, thresholdDb=-50, pad=0 )
| Cut the silence off both ends |
audioWaveformImage( audio, width=800, height=160,
format="", path="",
color="#3b82f6", background="",
style="filled", split=false )
| The waveform as an SVG or PNG picture, written to a path or returned |
audioSpectrogramImage( audio, width=800, height=256,
format="", path="", minHz=40, maxHz=16000,
rangeDb=80, scale="magma" )
| The spectrogram as a PNG, with magma,
viridis or gray colors |
WAV, MP3 and FLAC are read and written by the module itself (FLAC
lossless, in plain Java, verified against FFmpeg). OGG Vorbis, Opus,
AAC and M4A, and reading the audio of any video or other format, use
FFmpeg as a separate program. The module looks for it in this order:
the ffmpeg module setting, the
BX_AUDIO_FFMPEG variable, ~/.bxaudio/ffmpeg,
then the PATH. If there is none it downloads one for your computer
(about 66 MB on Linux and macOS, 110 MB on Windows) into
~/.bxaudio, checked against a pinned checksum, unless
autoInstall is false.
| Function | Purpose |
|---|---|
audioFfmpeg()
| Is FFmpeg available, where from, which version and encoders, and what a download would be |
audioFfmpegInstall( onProgress )
| Download it now |
videoInfo( path )
| Length, size, video and audio streams of any media file |
audioMux( video, audio, output, keepOriginal=false,
originalDb=-12, offset=0, bitrate=0 )
| Put audio on a video, copying the picture untouched; never longer than the video |
audioMarkers( markers, format="json",
path="", fps=30, total=0 )
| Markers as json, csv, a Resolve
edl, srt, vtt, FFmpeg
chapters (ffmetadata) or a youtube
chapter list |
audioHttpResponse( source, request, format="",
bitDepth=16, bitrate=0, maxAgeSeconds=3600,
filename="", download=false )
| What to send for a request for audio: status, headers and body for Range, ETag and 304 |
audioSave( audio, "out.ogg", 16, 96 ) and
audioLoad( "clip.mp4" ) just work once FFmpeg
is there. FFmpeg builds are third party programs with their own
licenses (see docs); the module never bundles or
links them.
| Function | Purpose |
|---|---|
audioMixer( sampleRate=44100, channels=2,
output="speakers", buses )
| A real time mixer. output is
speakers, null (keeps time, no sound,
for servers) or memory (call render( seconds
) to get the audio) |
mixer.play( sound, { bus, volume, pan, pitch, loop,
loopStart, loopEnd, start, fadeIn } )
| Play a sound, get a playback to stop( fade
), pause(), resume(),
setVolume(), setPan(),
setPitch(), fadeTo( level, seconds )
|
mixer.crossfade( playback, sound, seconds, options )
| Swap music smoothly |
mixer.setVolume( bus, volume ) /
mixer.setVolume( volume )
| Bus and master volume |
audioPlay( audio, wait=true, volume=1,
output="speakers" )
| Play one sound through the speakers, or with
output="null" where there is no sound
card. To get the audio itself use audioMixer(
output="memory" ) and render()
|
audioLoopify( audio, crossfade=0.25 )
| Blend the end into the start so a loop cannot click |
audioVariations( audio, count=5, pitchRange=1,
gainRangeDb=2, seed=1 )
| Slightly different copies, so repeated effects do not sound alike |
audioSprite( sounds, gap=0.1, path )
| Join named sounds into one file with a Howler.js map |
Playing to the speakers needs a sound card and the JVM's
java.desktop module. The game audio example shows a
scripted session, rendered to a file or played live. MIDI files are
not rendered: the JDK's software synthesizer cannot be driven offline
from outside the JDK.
audioSpeak turns text into a mono audio buffer with a Piper voice, on the JVM
and offline once the voices are downloaded. The engine and the voices
are not in the module, they are far too big. The first call downloads
them, with every file checked against a checksum, into
~/.bxaudio, shared by every BoxLang install.
bx-audio where shows what is stored and bx-audio
clean frees it.
| Function | Purpose |
|---|---|
audioSpeak( text, voice, autoInstall, onProgress,
speed=1, pauses, pronunciations )
| Speak a text, speed 1 is the voice's pace,
pauses ({ sentence, clause, paragraph
} in seconds) speaks it piece by piece.
voice is an id such as
en_US-joe-medium or a short name such as
joe. Downloads the engine (about 148 MB, once) and
the voice (63 to 114 MB, once) if they are missing, unless
autoInstall is false |
audioSpeakCues( text, voice, autoInstall, onProgress,
speed=1, pauses, pronunciations, maxChars=80,
split="sentence" )
| Speak piece by piece and get audio,
cues (start, end,
text) and seconds. Captions land on
the words within about 10 ms |
audioSubtitles( cues, format="srt",
path="", offset=0 )
| SRT or WebVTT text from cues, written to a file if you give a path |
audioVoices( language="", installed=false,
status="offered", license="",
usage="" )
| The voices: id, name, language, quality, license,
usage (what the license means for a product you
sell), commercial, credit, size,
status (installed,
downloadable, or excluded with
reason). status="excluded"
or "all" shows the voices that are not
offered and why |
audioLicenses()
| The licenses of the offered voices and what each asks of you |
audioCredits( voices )
| The credits the licenses of some voices ask for, as text for an about page |
audioVoiceInstall( voice, onProgress )
| Download a voice ahead of time, or
"engine" for the speech engine every
voice needs |
audioVoiceRemove( voice )
| Delete a voice, or "engine", from disk |
audioSpeechStatus()
| Where it all lives, whether the engine is installed, and which voices are |
audioStorage()
| Where ~/.bxaudio keeps the engine, voices,
FFmpeg and caches and how big each is |
audioStorageClear( kind="all" )
| Delete one kind of download (engine,
voices, ffmpeg, cache) or all
|
// A voice-over, ducking music underneath it
voice = audioSpeak( text="Meet BoxLang. Fast, friendly and made for the JVM.", voice="joe" );
t = audioTimeline( bpm=100 );
audioAdd( timeline=t, sound=music, at=0, track="music", gainDb=-10 );
audioAdd( timeline=t, sound=voice, at=t.bar( 1 ), track="voice" );
audioDuck( t, "music", "voice", -9 );
audioSave( audioSaturate( audioRender( timeline=t, tail=1 ) ), "promo.mp3" );
Only voices whose model card states an open license are on offer, each with its license shown, and you choose which to use. The full list, and the voices that were left out and why, is in docs/voices.md. Only US English voices are available for now.
Three module settings (in boxlang.json under
modules.bxaudio.settings, or in
ModuleConfig): home is where the engine, the
voices and FFmpeg are downloaded to, empty for
~/.bxaudio; autoInstall is true
unless you want the module to refuse to download anything;
ffmpeg is the path to an FFmpeg you want it to use. The
environment variables BX_AUDIO_HOME and
BX_AUDIO_FFMPEG set the same.
The examples catalog holds complete pieces of music made from code, with no samples or licensing concerns. Start with Pop Drive, a 55 second pop track that was checked against an original numpy script, then Lofi, House, Ambient, Techno, Synthwave, Cinematic, Chiptune, five original 8 bit tracks, Game sound effects, ten sounds you can export as a pack, and Jazz, Rock, a Voice-over with music that ducks under a spoken voice, a complete Trailer soundtrack (music cut to the video, voice-over, captions, mastering, mux) Game audio (a mixer session, loops, variations, a sprite), Sampler and The BxAudio theme.
boxlang examples/01-pop-drive/pop-drive.bxs --out=out/pop-drive.wav
To listen in a browser, start the explorer with the MiniServer: cd
examples/explorer && boxlang-miniserver --webroot ..
Installing the module with install-bx-module also
installs a bx-audio command and its bash tab completion
(bx-audio completions zsh, fish and
powershell print the scripts for those shells), so you
can work with audio from a shell without writing a script. Without the
installer, the same commands run as boxlang module:bxaudio <command>.
bx-audio render techno --bars=16 --out=techno.mp3 # synthesize an example piece
bx-audio info techno.mp3 # format, length, peak and RMS
bx-audio convert song.wav song.mp3 --bitrate=160 # WAV to MP3, with --sampleRate and --channels
bx-audio normalize voice.wav loud.wav --targetDb=-3
bx-audio trim song.wav intro.wav --start=0 --end=12
bx-audio waveform song.wav # one line of loudness, add --unicode for blocks
bx-audio voices # the open license voices you can use
bx-audio speak "Hello there" --voice=joe --out=hi.mp3 # installs the engine and voice the first time
bx-audio explorer # the web explorer, on the BoxLang MiniServer
bx-audio doctor # checks MP3, the examples and the MiniServer
| Command | Purpose |
|---|---|
info <file>
| Format, channels, sample rate, length, peak and RMS of an audio file |
convert <in> <out>
| Convert between WAV, MP3, FLAC and (with FFmpeg) OGG,
Opus and M4A. Options: --bitrate,
--bitDepth, --sampleRate, --channels
|
loudness <file>
| LUFS, loudness range and true peak |
master <in> <out>
| Reach a loudness target: --target=streaming
(or podcast, broadcast, a number),
--truePeakDb, --glue
|
stretch <in> <out> / pitch
<in> <out>
| --rate or --seconds for the
speed, --semitones for the pitch |
bpm <file> / silence <file>
| Tempo (with --beats), and the silent
stretches or --trim=OUT
|
loopify <in> <out> / sprite
<out> <files...> / play <file>
| Game audio: seamless loops, a sprite with a Howler map, playing to the speakers |
ffmpeg [install] / probe
<file> / mux <video> <audio> <out>
| FFmpeg status and download, media information, audio onto a video |
where / clean [kind] --yes
| Where ~/.bxaudio keeps its downloads and how
big they are, and deleting them |
licenses
| The voice licenses and what each asks of you |
credits <voice>...
| The credits the licenses of some voices ask for, to put on an about page |
completions <shell>
| Print the tab completion script for bash,
zsh, fish or powershell
|
version
| The module version |
normalize <in> <out>
| Scale the peak to --targetDb (default -1) |
trim <in> <out>
| Keep the section from --start to
--end, in seconds |
waveform <file>
| Draw the loudness on one line, --width
characters wide |
speak <text>
| Say a text with --voice, into
--out (default speech.wav). Installs
what is missing, unless --no-install.
--file reads the text from a file,
--speed, --captions=FILE.srt,
--by-clause and the pause options time it for video |
voices [voice]
| List the voices you can use, with
--installed, --downloadable,
--excluded (not offered, and why),
--all, --lang and
--license. A voice name shows its details |
install <voice> / remove <voice>
| Download or delete a voice, or engine for
the speech engine |
pieces
| List the example pieces |
render <piece>
| Synthesize an example piece. Takes --out and
the settings of that piece, such as --bars and --bpm
|
explorer
| Start the web explorer on --port (default
8080) and --host (default 127.0.0.1,
this machine only). Needs the BoxLang MiniServer on your PATH |
doctor
| Check MP3, FFmpeg, audio output, the examples and the explorer on this machine |
Paths are relative to the folder you run the command in. Run
bx-audio help <command> for the options of one command.
The module is Apache 2.0. Text to speech downloads the Voices engine
(Apache 2 and MIT) and the voices you choose, each with its own
license, see docs/voices.md. FLAC is
encoded and decoded by code in this module. FFmpeg, when used, is a
separate program found on your computer or downloaded from its
provider (macOS and Linux builds by martin-riedl.de, Windows builds by
gyan.dev), started as a process and never bundled; its builds carry
their own licenses, see https://ffmpeg.org/legal.html.
MP3 uses JLayer and jump3r (both LGPL), shipped as separate,
replaceable jars in the module's libs/ folder and never
bundled into the module jar. Without them WAV still works and MP3
calls raise a clear error. MP3 encoding needs the
java.desktop module in the JVM. Details and license
texts: licenses/THIRD-PARTY.md.
The documentation site is at
bxaudio.boxlang.io,
built from docs/
with BX Sites: guides for video,
speech, mastering and games, the reference of every function and
command, every voice with its license, and a player for each example.
The function and command reference, the voices page and the examples
page are generated from the code, see docs/contribute.md.
./gradlew downloadBoxLang # once, fetches the BoxLang jar used for compiling and tests
./gradlew spotlessApply test
BoxLang is a professional open-source project and it is completely funded by the community and Ortus Solutions, Corp. Ortus Patreons get many benefits like a cfcasts account, a FORGEBOX Pro account and so much more. If you are interested in becoming a sponsor, please visit our patronage page: https://patreon.com/ortussolutions
"I am the way, and the truth, and the life; no one comes to the Father, but by me (JESUS)" Jn 14:1-12
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
15-theme, a grid anthem at 132 BPM with an original adventure fanfare (bx-audio render theme, and --sting for a 3 second jingle). BxAudio logos and brand colors (teal to blue) across the docs, the explorer and the readmedocs/.theme/home.bxm) with press-to-play examples, a browser playground and the ecosystem, and a "Try it" page with a run-tested recipe for every feature and a clone-and-run guide13-trailer (music fitted to a video, a voice-over with captions, ducking, mastering, chapters and mux) and 14-game-audio (a mixer session with buses, loops, variations, a boss theme from stretch and pitch, and a sprite)audioMixer (real time, buses, volume, pan, pitch, loops, crossfade; output to the speakers, a clock with no sound, or memory), audioPlay, audioLoopify, audioVariations, audioSprite (with a Howler.js map). CLI play, loopify, spriteaudioSpeak takes speed and pauses; audioSpeakCues returns the audio with timed caption lines; audioSubtitles writes SRT and WebVTT. CLI speak --speed --captions --by-clauseaudioFfmpeg, audioFfmpegInstall (pinned, SHA-256 checked downloads for Linux and macOS on amd64 and arm64, and Windows), OGG, Opus and AAC output through audioSave, reading any format and the audio of any video through audioLoad, videoInfo, audioMux and audioMarkers. CLI ffmpeg, probe, mux. Settings ffmpeg, BX_AUDIO_FFMPEGaudioLoudness (EBU R128 / BS.1770, within 0.1 LU of FFmpeg's ebur128), audioNormalizeLoudness, audioMaster with presets, audioCompress, audioLimit, audioEq. CLI loudness, masteraudioStretch, audioFitTo, audioPitch (WSOLA). CLI stretch, pitchaudioBpm (tempo and beats), audioSpectrum, audioSpectrogram, audioFft, audioDetectSilence, audioTrimSilence. CLI bpm, silence~/.bxaudio (settings home, BX_AUDIO_HOME), shared by every BoxLang install. audioStorage, audioStorageClear and the CLI where and clean show and free them. The speech engine loads through the module's own class loaderaudioVoices( status=, license= ), audioLicenses and CLI voices --excluded --downloadable --all, voices <voice> and licenses show what is offered, what is not and why12-voiceover: a spoken announcement over music that ducks under the voice, using audioSpeak and audioDuck, or a recording of your own. Command line only, since it needs the speech engine10-jazz (a twelve bar blues with a walking bass and a solo improvised by rules that follow the chords) and 11-rock (distorted power chords, bass, drums and a seeded solo), measured, tested and in the explorer09-sfx: ten synthesized game sound effects (jump, coin, laser, explosion, power up, hit, blip, alarm, game over, footstep), as a sampler, one sound, or a folder of files08-chiptune: five original 8 bit tracks (overworld, cave, boss, title and victory) written as text in a small notation, for two pulse channels, a triangle bass and noise drums. In the explorer as wellaudioSpeak, audioVoices, audioVoiceInstall, audioVoiceRemove and audioSpeechStatus, and the bx-audio commands speak, voices, install and remove. The Voices engine (about 148 MB, pinned by SHA-1) and the voices are downloaded on first use, verified and resumable, into the bx-audio folder of the BoxLang home (settings home and autoInstall). The catalog lists only single speaker US English voices whose model card states an open license, generated by tools/update-voices.bxs, with the list and the voices left out in docs/voices.mdaudioSave writes MP3 without a bitrate at 192 kbps, or 128 kbps below 32 kHz where 192 does not existbx-audio command line: info, convert, normalize, trim, waveform, pieces, render, explorer and doctor. It runs as boxlang module:bxaudio <command>, and install-bx-module installs a bx-audio executable and bash completion from the executable and completions settings in box.json. The module now ships the examples and the exploreroverrides) win over the command line05-techno, 06-synthwave and 07-cinematic (trailer music with a true silent beat before the boom), measured and covered by ExamplesGenresTest, and added to the explorerexamples/explorer: a BoxLang MiniServer app (index.bxm, render.bxm) that plays every catalog piece in the browser with adjustable settings, a waveform, seeking and a render cache. BoxLang branded with the official logos and palette, light and dark. Covered by ExplorerCatalogTestaudioWaveform( audio, points ) returns per-slice peak levels for drawing a waveformaudioLoad and audioSave (new bitrate argument). Uses LGPL JLayer and jump3r as separate optional jars in libs/, with license texts in licenses/; files are detected by content, not extensionAudioBuffer type and a dependency free WAV codec (read PCM 8/16/24/32 and float 32/64, write 16/24 bit PCM and 32 bit float)audioSilence, audioLoad, audioSave and audioInfoaudioDelay (with feedback, ping pong and an automatic tail), audioReverb (Freeverb design) and audioChorusaudioMix to sum buffers02-lofi, 03-house and 04-ambient, original compositions checked by measurement (chord tones, brightness, beat pulse, transient strength, a silent ending) and covered by tests01-pop-drive, a BoxLang port of a numpy script checked against the original (same length and peak, octave bands within 0.5 dB, kick and bass envelope correlation 1.000), covered by a testshadowJar now depends on serviceLoaderBuild, and build/module is cleared before it is assembled, so a jar built on its own still registers its BIFs and no removed file lingersaudioFreq, audioOsc (sine, saw, square, triangle, pulse, additive), audioSweep, audioNoise (white, pink, brown, blue), audioEnvelope, audioDrum (kick, snare, hat, openhat, clap, tom, crash, riser) and audioFilter (lowpass, highpass, bandpass, notch)audioTimeline, audioAdd, audioBeats, audioTrackGain, audioPump, audioDuck and audioRender, with a tempo grid, automatic sample rate and channel conversion, kick-style pump ducking and voice-over sidechain duckingaudioSaturate for peak-normalize plus tanh limitingaudioGain, audioPan, audioFade, audioNormalize, audioTrim, audioConcat, audioReverse, audioChannels and audioResample
$
box install bx-audio