Skip to content

Commit

Permalink
Add ba.mulaw_bitcrusher function.
Browse files Browse the repository at this point in the history
  • Loading branch information
sletz committed Dec 9, 2024
1 parent 6cbb3c0 commit a222717
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 21 deletions.
64 changes: 63 additions & 1 deletion basics.lib
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ it = library("interpolators.lib");
si = library("signals.lib");

declare name "Faust Basic Element Library";
declare version "1.20.0";
declare version "1.21.0";

//=============================Conversion Tools===========================================
//========================================================================================
Expand Down Expand Up @@ -2373,6 +2373,68 @@ with {
scaler = float(2^nbits - 1);
};

//----------------------------`(ba.)mulaw_bitcrusher`------------------------------------------
// Produce distortion by reducing the signal resolution using μ-law compression.
//
// #### Usage
//
// ```
// _ : mulaw_bitcrusher(mu,nbits) : _
// ```
//
// Where:
//
// * `mu`: controls the degree of μ-law compression, larger values result in stronger compression
// * `nbits`: the number of bits of the wanted resolution
//
// #### Description
//
// The `mulaw_bitcrusher` applies a combination of μ-law compression, quantization, and expansion
// to create a non-linear bitcrushed effect. This method retains finer detail in lower-amplitude signals
// compared to linear bitcrushing, making it suitable for creative sound design.
//
// #### Theory
//
// 1. **μ-law Compression**:
// emphasizes lower-amplitude signals by applying a logarithmic curve to the signal.
// The formula used is:
// ```
// F(x) = ma.signum(x) * log(1 + mu * abs(x)) / log(1 + mu);
// ```
// 2. **Quantization**:
// reduces the signal resolution to `nbits` by rounding values to the nearest step within the specified bit depth.
//
// 3. **μ-law Expansion**:
// reverses the compression applied earlier to restore the signal to its original dynamic range:
// ```
// F⁻¹(y) = ma.signum(y) * (pow(1 + mu, abs(y)) - 1) / mu;
// ```
//
// #### Example test program
//
// ```
// process = os.osc(440) : mulaw_bitcrusher(255, 8);
// ```
//
// In this example, a sine wave at 440 Hz is passed through the μ-law bitcrusher, with a compression
// parameter `mu` of 255 and 8-bit quantization. This creates a distorted, "lo-fi" effect.
//
// #### References
//
// * <https://en.wikipedia.org/wiki/Μ-law_algorithm>

//------------------------------------------------------------------------------
declare mulaw_bitcrusher author "Stephane Letz";

mulaw_bitcrusher(mu,nbits,x) = x : muLawCompress(mu) : bitcrusher(nbits) : muLawExpand(mu)
with {
// μ-law compression
muLawCompress(mu, x) = ma.signum(x) * log(1 + mu * abs(x)) / log(1 + mu);

// μ-law expansion (decompression)
muLawExpand(mu, x) = ma.signum(x) * (pow(1 + mu, abs(x)) - 1) / mu;
};


//=================================Sliding Reduce=========================================
// Provides various operations on the last n samples using a high order
Expand Down
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -389,5 +389,5 @@ <h4 class="modal-title" id="keyboardModalLabel">Keyboard Shortcuts</h4>

<!--
MkDocs version : 1.5.3
Build Date UTC : 2024-12-03 16:05:11.274276+00:00
Build Date UTC : 2024-12-09 15:34:04.253892+00:00
-->
72 changes: 58 additions & 14 deletions docs/libs/basics/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,10 @@
<li class="nav-item" data-level="3"><a href="#babitcrusher" class="nav-link">(ba.)bitcrusher</a>
<ul class="nav flex-column">
</ul>
</li>
<li class="nav-item" data-level="3"><a href="#bamulaw_bitcrusher" class="nav-link">(ba.)mulaw_bitcrusher</a>
<ul class="nav flex-column">
</ul>
</li>
</ul>
</li>
Expand Down Expand Up @@ -1791,6 +1795,46 @@ <h4 id="usage_68">Usage</h4>
<ul>
<li><code>nbits</code>: the number of bits of the wanted resolution</li>
</ul>
<hr />
<h3 id="bamulaw_bitcrusher"><code>(ba.)mulaw_bitcrusher</code></h3>
<p>Produce distortion by reducing the signal resolution using μ-law compression.</p>
<h4 id="usage_69">Usage</h4>
<pre><code>_ : mulaw_bitcrusher(mu,nbits) : _
</code></pre>
<p>Where:</p>
<ul>
<li><code>mu</code>: controls the degree of μ-law compression, larger values result in stronger compression</li>
<li><code>nbits</code>: the number of bits of the wanted resolution</li>
</ul>
<h4 id="description">Description</h4>
<p>The <code>mulaw_bitcrusher</code> applies a combination of μ-law compression, quantization, and expansion
to create a non-linear bitcrushed effect. This method retains finer detail in lower-amplitude signals
compared to linear bitcrushing, making it suitable for creative sound design.</p>
<h4 id="theory">Theory</h4>
<ol>
<li><strong>μ-law Compression</strong>:
emphasizes lower-amplitude signals by applying a logarithmic curve to the signal.
The formula used is:
<code>F(x) = ma.signum(x) * log(1 + mu * abs(x)) / log(1 + mu);</code></li>
<li>
<p><strong>Quantization</strong>:
reduces the signal resolution to <code>nbits</code> by rounding values to the nearest step within the specified bit depth.</p>
</li>
<li>
<p><strong>μ-law Expansion</strong>:
reverses the compression applied earlier to restore the signal to its original dynamic range:
<code>F⁻¹(y) = ma.signum(y) * (pow(1 + mu, abs(y)) - 1) / mu;</code></p>
</li>
</ol>
<h4 id="example-test-program_11">Example test program</h4>
<pre><code>process = os.osc(440) : mulaw_bitcrusher(255, 8);
</code></pre>
<p>In this example, a sine wave at 440 Hz is passed through the μ-law bitcrusher, with a compression
parameter <code>mu</code> of 255 and 8-bit quantization. This creates a distorted, "lo-fi" effect.</p>
<h4 id="references_1">References</h4>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Μ-law_algorithm">https://en.wikipedia.org/wiki/Μ-law_algorithm</a></li>
</ul>
<h2 id="sliding-reduce">Sliding Reduce</h2>
<p>Provides various operations on the last n samples using a high order
<code>slidingReduce(op,n,maxN,disabledVal,x)</code> fold-like function:</p>
Expand Down Expand Up @@ -1926,7 +1970,7 @@ <h3 id="baslidingreduce"><code>(ba.)slidingReduce</code></h3>
<code>slidingReduce(max,128,128,0-(ma.MAX))</code> will compute the maximum of the last
128 samples. The output is updated each sample, unlike reduce, where the
output is constant for the duration of a block.</p>
<h4 id="usage_69">Usage</h4>
<h4 id="usage_70">Usage</h4>
<pre><code>_ : slidingReduce(op,n,maxN,disabledVal) : _
</code></pre>
<p>Where:</p>
Expand All @@ -1945,7 +1989,7 @@ <h3 id="baslidingsum"><code>(ba.)slidingSum</code></h3>
<p>The sliding sum of the last n input samples.</p>
<p>It will eventually run into numerical trouble when there is a persistent dc component.
If that matters in your application, use the more CPU-intensive <code>ba.slidingSump</code>.</p>
<h4 id="usage_70">Usage</h4>
<h4 id="usage_71">Usage</h4>
<pre><code>_ : slidingSum(n) : _
</code></pre>
<p>Where:</p>
Expand All @@ -1956,7 +2000,7 @@ <h4 id="usage_70">Usage</h4>
<h3 id="baslidingsump"><code>(ba.)slidingSump</code></h3>
<p>The sliding sum of the last n input samples.</p>
<p>It uses a lot more CPU than <code>ba.slidingSum</code>, but is numerically stable "forever" in return.</p>
<h4 id="usage_71">Usage</h4>
<h4 id="usage_72">Usage</h4>
<pre><code>_ : slidingSump(n,maxN) : _
</code></pre>
<p>Where:</p>
Expand All @@ -1967,7 +2011,7 @@ <h4 id="usage_71">Usage</h4>
<hr />
<h3 id="baslidingmax"><code>(ba.)slidingMax</code></h3>
<p>The sliding maximum of the last n input samples.</p>
<h4 id="usage_72">Usage</h4>
<h4 id="usage_73">Usage</h4>
<pre><code>_ : slidingMax(n,maxN) : _
</code></pre>
<p>Where:</p>
Expand All @@ -1978,7 +2022,7 @@ <h4 id="usage_72">Usage</h4>
<hr />
<h3 id="baslidingmin"><code>(ba.)slidingMin</code></h3>
<p>The sliding minimum of the last n input samples.</p>
<h4 id="usage_73">Usage</h4>
<h4 id="usage_74">Usage</h4>
<pre><code>_ : slidingMin(n,maxN) : _
</code></pre>
<p>Where:</p>
Expand All @@ -1991,7 +2035,7 @@ <h3 id="baslidingmean"><code>(ba.)slidingMean</code></h3>
<p>The sliding mean of the last n input samples.</p>
<p>It will eventually run into numerical trouble when there is a persistent dc component.
If that matters in your application, use the more CPU-intensive <code>ba.slidingMeanp</code>.</p>
<h4 id="usage_74">Usage</h4>
<h4 id="usage_75">Usage</h4>
<pre><code>_ : slidingMean(n) : _
</code></pre>
<p>Where:</p>
Expand All @@ -2002,7 +2046,7 @@ <h4 id="usage_74">Usage</h4>
<h3 id="baslidingmeanp"><code>(ba.)slidingMeanp</code></h3>
<p>The sliding mean of the last n input samples.</p>
<p>It uses a lot more CPU than <code>ba.slidingMean</code>, but is numerically stable "forever" in return.</p>
<h4 id="usage_75">Usage</h4>
<h4 id="usage_76">Usage</h4>
<pre><code>_ : slidingMeanp(n,maxN) : _
</code></pre>
<p>Where:</p>
Expand All @@ -2015,7 +2059,7 @@ <h3 id="baslidingrms"><code>(ba.)slidingRMS</code></h3>
<p>The root mean square of the last n input samples.</p>
<p>It will eventually run into numerical trouble when there is a persistent dc component.
If that matters in your application, use the more CPU-intensive <code>ba.slidingRMSp</code>.</p>
<h4 id="usage_76">Usage</h4>
<h4 id="usage_77">Usage</h4>
<pre><code>_ : slidingRMS(n) : _
</code></pre>
<p>Where:</p>
Expand All @@ -2026,7 +2070,7 @@ <h4 id="usage_76">Usage</h4>
<h3 id="baslidingrmsp"><code>(ba.)slidingRMSp</code></h3>
<p>The root mean square of the last n input samples.</p>
<p>It uses a lot more CPU than <code>ba.slidingRMS</code>, but is numerically stable "forever" in return.</p>
<h4 id="usage_77">Usage</h4>
<h4 id="usage_78">Usage</h4>
<pre><code>_ : slidingRMSp(n,maxN) : _
</code></pre>
<p>Where:</p>
Expand All @@ -2046,7 +2090,7 @@ <h2 id="parallel-operators">Parallel Operators</h2>
<hr />
<h3 id="baparallelop"><code>(ba.)parallelOp</code></h3>
<p>Apply a commutative binary operation <code>op</code> to N parallel inputs.</p>
<h4 id="usage_78">usage</h4>
<h4 id="usage_79">usage</h4>
<pre><code>si.bus(N) : parallelOp(op,N) : _
</code></pre>
<p>where:</p>
Expand All @@ -2057,7 +2101,7 @@ <h4 id="usage_78">usage</h4>
<hr />
<h3 id="baparallelmax"><code>(ba.)parallelMax</code></h3>
<p>The maximum of N parallel inputs.</p>
<h4 id="usage_79">Usage</h4>
<h4 id="usage_80">Usage</h4>
<pre><code>si.bus(N) : parallelMax(N) : _
</code></pre>
<p>Where:</p>
Expand All @@ -2067,7 +2111,7 @@ <h4 id="usage_79">Usage</h4>
<hr />
<h3 id="baparallelmin"><code>(ba.)parallelMin</code></h3>
<p>The minimum of N parallel inputs.</p>
<h4 id="usage_80">Usage</h4>
<h4 id="usage_81">Usage</h4>
<pre><code>si.bus(N) : parallelMin(N) : _
</code></pre>
<p>Where:</p>
Expand All @@ -2077,7 +2121,7 @@ <h4 id="usage_80">Usage</h4>
<hr />
<h3 id="baparallelmean"><code>(ba.)parallelMean</code></h3>
<p>The mean of N parallel inputs.</p>
<h4 id="usage_81">Usage</h4>
<h4 id="usage_82">Usage</h4>
<pre><code>si.bus(N) : parallelMean(N) : _
</code></pre>
<p>Where:</p>
Expand All @@ -2087,7 +2131,7 @@ <h4 id="usage_81">Usage</h4>
<hr />
<h3 id="baparallelrms"><code>(ba.)parallelRMS</code></h3>
<p>The RMS of N parallel inputs.</p>
<h4 id="usage_82">Usage</h4>
<h4 id="usage_83">Usage</h4>
<pre><code>si.bus(N) : parallelRMS(N) : _
</code></pre>
<p>Where:</p>
Expand Down
1 change: 1 addition & 0 deletions docs/libs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ <h2 id="basics">basics</h2>
<a href="basics/#batoggle">(ba.)toggle</a> &nbsp; &nbsp;
<a href="basics/#baon_and_off">(ba.)on_and_off</a> &nbsp; &nbsp;
<a href="basics/#babitcrusher">(ba.)bitcrusher</a> &nbsp; &nbsp;
<a href="basics/#bamulaw_bitcrusher">(ba.)mulaw_bitcrusher</a> &nbsp; &nbsp;
<a href="basics/#baslidingreduce">(ba.)slidingReduce</a> &nbsp; &nbsp;
<a href="basics/#baslidingsum">(ba.)slidingSum</a> &nbsp; &nbsp;
<a href="basics/#baslidingsump">(ba.)slidingSump</a> &nbsp; &nbsp;
Expand Down
2 changes: 1 addition & 1 deletion docs/search/search_index.json

Large diffs are not rendered by default.

Binary file modified docs/sitemap.xml.gz
Binary file not shown.
4 changes: 2 additions & 2 deletions noises.lib
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ lfnoise(freq) = lfnoise0(freq) : seq(i,5,fi.lowpass(1,freq)); // non-overshootin
//
// Where:
//
// * ` f0`: average frequency of noise impulses per second
// * `f0`: average frequency of noise impulses per second
//
// Random impulses in the amplitude range -1 to 1 are generated
// at an average rate of f0 impulses per second.
Expand Down Expand Up @@ -384,7 +384,7 @@ with {
// Where:
//
// * `amp`: amplitude of noise impulses (positive and negative)
// * ` f0`: average frequency of noise impulses per second
// * `f0`: average frequency of noise impulses per second
//
// #### Reference
//
Expand Down
4 changes: 2 additions & 2 deletions version.lib
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//
//------------------------------------------------------------
version = 2, // MAJOR version when we make incompatible API changes,
45, // MINOR version when we add functionality in a backwards compatible manner,
1; // PATCH version when we make backwards compatible bug fixes.
46, // MINOR version when we add functionality in a backwards compatible manner,
0; // PATCH version when we make backwards compatible bug fixes.


0 comments on commit a222717

Please sign in to comment.