This library is a cross-platform and header only library (except for 3rd-party libs necessary).
This library is for synthesizing, loading and saving sound and also has a chip-tune engine which allow you full flexibility to design instruments and to use these instruments when composing your chip-tune tune.
The library depends on an implementation of the interface AudioLibSwitcher
. See below for more info.
Documentation is a work in progress for the moment being. For example on how to use this lib, please clone repo Pilot Episode
and follow the instructions there.
WaveformIO.h
uses libsndfile
which is under the LGPL-2.1 license. There is no derivative work of the library whatsoever. libsndfile
is only a dependency.
In order to be able to use WaveformIO.h
you need to supply the libsnd binaries (libs or dlls) and headers yourself. These are not included.
Since libsndfile
is under LGPL, this means that whenever a release build is created, its source code has to be included as well. So that is important to remember.
See this online discussion for more info: https://softwareengineering.stackexchange.com/questions/141847/how-does-using-a-lgpl-gem-affect-my-mit-licensed-application
This library (which is under the MIT license) heavily relies on the OpenAL Soft license which is under the LGPL license (see https://www.openal-soft.org/).
Here OpenAL Soft is also only used as a dependency and 8Beat
is not to be considered as derived work. There are no binaries (libs or dlls), nor headers from OpenAL Soft included in this repository. The user has to supply those him/her-self.
One issue however is that the newer OpenAL Soft repository on github (https://github.com/kcat/openal-soft) seems to be under GPL-v2 license. This could be a potential issue!!!
Edit: See this discussion with the author of the lib: kcat/openal-soft#187:
kcat:
Hi.
Generally speaking, as long as OpenAL Soft is unmodified and distributed as a shared library, you can use it in any project you wish. Unless your code is also (L)GPL, that's typically the easiest way to deal with LGPL libraries. Otherwise, if OpenAL Soft itself is static-linked into a binary, then any distribution must also offer the source or object files of the binary so a user can relink with another version of OpenAL Soft.
We'll often use the term waveform
as meaning an audio signal here.
8Beat is a header-only library that contains the following header files:
-
Waveform.h
contains the structWaveform
that contains an audio buffer, and variablesfrequency
,sample_rate
andduration
. This struct is very central as it holds the PCM audio waveform representation itself. -
WaveformIO.h
contains classWaveformIO
that has two static public functions:load()
andsave()
. These functions rely on thesndfile
library which allows you to import and export aWaveform
object to many different types and formats. -
WaveformGeneration.h
contains classWaveformGeneration
with a single public functiongenerate_waveform()
.- You can control the created waveform via these optional parameters in struct
WaveformGenerationParams
:std::optional<float> sample_range_min = std::nullopt; // default: -1 std::optional<float> sample_range_max = std::nullopt; // default: +1 // duty_cycle applies to SQUARE, TRIANGLE and SAWTOOTH. // In the case of TRIANGLE, duty_cycle = 1 is a SAWTOOTH and 0 is reverse SAWTOOTH. // In the case of SAWTOOTH, when duty_cycle goes from 1 to 0, the SAWTOOTH teeth get thinner towards 0. // default: 0.5 for SQUARE and TRIANGLE and 1 for SAWTOOTH. std::optional<float> duty_cycle = std::nullopt; std::optional<float> duty_cycle_sweep = std::nullopt; // unit/s. std::optional<float> min_frequency_limit = std::nullopt; std::optional<float> max_frequency_limit = std::nullopt; std::optional<float> freq_slide_vel = std::nullopt; // 8va/s std::optional<float> freq_slide_acc = std::nullopt; // 8va/s^2 std::optional<float> vibrato_depth = std::nullopt; std::optional<float> vibrato_freq = std::nullopt; std::optional<float> vibrato_freq_vel = std::nullopt; std::optional<float> vibrato_freq_acc = std::nullopt; std::optional<float> vibrato_freq_acc_max_vel_limit = std::nullopt; int noise_filter_order = 2; float noise_filter_rel_bw = 0.2f; float noise_filter_slot_dur_s = 1e-2f; std::vector<ArpeggioPair> arpeggio;
- You can control the created waveform via these optional parameters in struct
-
Spectrum.h
contains structSpectrum
which is used in conjunction with functions such as public functionsfft()
andifft()
in classWaveformHelper
. -
WaveformHelper.h
contains classWaveformHelper
which has the following public static functions:subset()
allows you to retrieve a portion of a waveform.mix()
mixes two waveforms by lerping them or multiple waveforms by weighted average.ring_modulation()
multiplies two waveforms.reverb()
does reverb between a waveform and an impulse response waveform of an environment (response sound from a dirac pulse-like "trigger" sound) to create a reverb effect.reverb_fast()
same asreverb()
but is very fast because it uses the fast Fourier transform.complex2real()
lets you choose if you want the real part, imag part or absolute value of both from a given complex value.fft()
this is the fast Fourier transform using the Cooley-Tukey algorithm.ifft()
this is the fast inverse Fourier transform using a variant of the Cooley-Tukey algorithm.find_min_max()
finds the min and max values of a given audio signal.normalize_over()
only normalize if the amplitude is larger than a certain limit. If so then normalized to that limit. This is a kind of a normalized amplitude limiter.normalize()
normalizes the waveform so that the max amplitude is always (nearly) 1.normalize_scale()
same asnormalize()
but is followed by a scaling operation so that the max amlitude = scaling_factor.scale()
simply just scale the waveform with a scale factor.clamp()
clamps the samples of a waveform within a specified range.fir_moving_average()
a moving average filter of sorts.fir_sinc_window_low_pass()
a kind of a low-pass filter.flanger()
applies a flanger filter to the input waveform.karplus_strong()
generates guitar-like string sounds.envelope_adsr()
applies an adsr envelope to a specified waveform.resample()
resamples a waveform to a specified sample-rate.filter(const Waveform&, const FilterArgs&)
filters a waveform according to theFilterArgs
argument. Calls the function signature below:filter(const Waveform& wave, FilterType type, FilterOpType op_type, int filter_order, float freq_cutoff_hz, std::optional<float> freq_bandwidth_hz, float ripple = 0.1f, // ripple: For Chebychev filters. bool normalize_filtered_wave = false)
where:type
isNONE
,Butterworth
,ChebyshevTypeI
orChebyshevTypeII
.op_type
isNONE
,LowPass
,HighPass
,BandPass
orBandStop
.
filter(const Waveform&, const Filter&)
filters a general FIR or IIR filter with coeffsa
andb
. Used internally by above filter functions.filter(const std::vector<float>&, const Filter&)
used by the function in the previous point.print_waveform_graph_idx()
andprint_waveform_graph_t()
prints the waveform shape in the terminal.calc_time_from_num_cycles()
utility function for waveform objects.calc_dt()
utility function for waveform objects.calc_duration()
utility function for waveform objects.calc_num_samples()
utility function for waveform objects.
-
ADSR.h
contains data structures to represent various aspects of an ADSR envelope. Also contains the namespaceadsr_presets
with a number of ADSR presets that will give you anADSR
struct. -
Synthesizer.h
contains classSynthesizer
which allows you to produce a synthesized instrument sound via the static public functionssynthesize()
. These are the supported instruments at the moment:PIANO
(Not really piano-like, but it's a start).VIOLIN
(Can perhaps serve as a violin-ish sound if you squint with your ears).ORGAN
.TRUMPET
(Not quite there yet, but not too shabby though).FLUTE
(Sounds kind of flute-ish).GUITAR
(Need to rework the Karplus-Strong algorithm to make it sound as it's supposed to).KICKDRUM
.SNAREDRUM
.HIHAT
.ANVIL
(Well, it kind of sounds like an anvil doesn't it?).
-
AudioSourceHandler.h
contains classesAudioSourceHandler
,AudioSource
andAudioStreamSource
.AudioSourceHandler
produces instances ofAudioSource
andAudioStreamSource
. -
ChipTuneEngine.h
contains classChipTuneEngine
which allows you to play a chiptune from a text-file (file ending*.ct
) in a threaded manner so that you can use it in games and what-not. In the beginning of the tune file you define the instruments, adsr envelopes, low-pass filters etc. Then after that you define the score where each column is a voice or channel if you will, and each column is a beat (bars are made up of beats, you could say). Refer to this wiki page about the file format.
This header-only library depends on the following header-only libraries:
Core
TrainOfThrought
AudioLibSwitcher_OpenAL
- Windows:
3rdparty_OpenAL
- Windows:
3rdparty_libsndfile
(only necessary forWaveformIO.h
)
and uses the header-only library Termin8or
for the demos.
These libs are expected to be located in checkout dirs with the same names and next to each other. Like this:
<my_source_code_dir>/lib/Core/
<my_source_code_dir>/lib/TrainOfThought/ ; Used by `WaveformHelper.h`.
<my_source_code_dir>/lib/AudioLibSwitcher_OpenAL/ ; Needed for now, but in the future you will be able to choose.
<my_source_code_dir>/lib/3rdparty_OpenAL/ ; Windows only. The OpenAL-Soft libs/dlls necessary for the **Windows** build.
<my_source_code_dir>/lib/3rdparty_libsndfile/ ; Windows only. The libsndfile libs/dlls necessary for `WaveformIO.h` to work on windows.
<my_source_code_dir>/lib/Termin8or/ ; Only for the demos.
<my_source_code_dir>/lib/8Beat/
where <source>
is where you normally put your git repos and <lib>
is recommended to be "lib
" but can be named something different or left out all-together. However, the following programs requires them to be located in a sub-folder called "lib
" or else these programs will not build:
The header only libs use relative include paths (which is mayhaps a bit suboptimal), but I'll see if I can find a better solution for this in the future.
There are currently ten demos under the demos
folder that you can build and run under linux / macos.
First cd to folder demos
. To build demo_1
type ./build_demo_1.sh
. Then run by typing ./bin/demo_1
. The same applies for the other demos. You can build all demos by running the script ./build_all_demos.sh
or build_all_demos.bat
.
An easier way to get started is to use any of the scripts setup_and_build_demos_debian.sh
, setup_and_build_demos_macos.sh
or setup_and_build_demos.bat
. If you use these, you can then skip the section below. These scripts automatically clones the repos necessary for your platform.
You'll need OpenAL Soft
for all demos and sndfile
for some of the demos.
However, the libs and dlls from these are mirrored in 3rdparty_OpenAL
and 3rdparty_libsndfile
respectively and 8Beat
expects the folder structure used in these repos, so the most easy way to get started is to use any of the setup_and_build_demos
scripts in the root folder. See the Getting Started
section for the folder layout that is expected by 8Beat
.
On Windows no installation of OpenAL Soft or libsndfile is necessary. Only the 3rdparty_OpenAL
(and optionally 3rdparty_libsndfile
) repos are necessary. See above.
Install OpenAL Soft using brew install openal-soft
. The build script(s) takes care of the rest (adjust script if paths differ).
Install libsndfile using brew install libsndfile
. The build script(s) takes care of the rest (adjust script if paths differ).
Install OpenAL Soft using sudo apt install libopenal-dev
.
Install libsndfile using sudo apt install libsndfile-dev
.