User Tools

Site Tools


dfpwm

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

dfpwm [2015/06/25 11:36] (current)
Line 1: Line 1:
 +DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec created by Ben "​GreaseMonkey"​ Russell in 2012, originally to be used as a voice codec for asiekierka'​s pixmess, a C remake of 64pixels. It is a 1-bit-per-sample codec which uses a dynamic-strength one-pole low-pass filter as a predictor. Due to the fact that a raw DPFWM decoding creates a high-pitched whine, it is often followed by some post-processing filters to make the stream more listenable.
 +
 +DFPWM is recognisable,​ but also creates a fair bit of noise. It is suitable for brostep, however.
 +
 +It depends on the implementation as to what all the parameters are. Testing has shown that 8 bits per sample works better than 16 bits. The codec is not frequency-dependent,​ and can work at any frequency. It is a mono codec, but stereo can be achieved by running two streams in parallel.
 +
 +If necessary, you can get away with converting your streams to raw 1-bit, or you could possibly assume a static-strength low-pass filter.
 +
 +The implementation in Computronics handles streams using Ri=7, Rd=20, 8 bits per sample, and LSB stored first.
 +
 +==== Specification ====
 +
 +DFPWM, at its simplest, works like this:
 +  Decoding:
 +    smp(t) <- Predictor(bit(t))
 +  Encoding:
 +    if smp(t) > LastPredictor OR (smp(t) = LastPredictor = 127):
 +      bit(t) <- Predictor(1)
 +    else:
 +      bit(t) <- Predictor(0)
 +
 +where bit is either LOW or HIGH, and smp ∈ [LOW, HIGH]. (Apologies for using the ∈, it's just that if I try to do "less than or equal",​ dokuwiki gives me an "is implied by" symbol instead. --GM)
 +
 +=== State ===
 +
 +Firstly we need to give some types:
 +* Let q,s be signed integers.
 +* Let b' be a single bit, either 0 or 1.
 +* Let Ri,Rd be chosen constant signed integers. ((7,20) is reasonable for 8 bits per sample.)
 +
 +Then we need to assign meanings:
 +* Let q be the "​charge",​ initialised to 0.
 +* Let s be the "​strength",​ initialised to 1.
 +* Let Ri be the strength increase.
 +* Let Rd be the strength decrease.
 +
 +Ri and Rd are constant (and, until someone discovers a better set of values, will always be 7 and 20 respectively). q and s vary.
 +
 +From here we can define the predictor.
 +
 +=== Predictor ===
 +
 +To simplify this, we will define this in terms of signed 8-bit samples.
 +
 +== Input comprehension ==
 +Let b' be the previous instance of b, initialised to 0 to simplify implementation.
 +
 +Let t be the "​target",​ -128 if b is 0, and 127 if b is 1. (In other words, LOW and HIGH respectively.)
 +
 +== Charge adjustment ==
 +Let q' be an integer such that
 +> q' <- q + (s*(t - q) + 128)/256
 +
 +If q == q', and q != t, then:
 +> If t < q: q' <- q' - 1
 +> If t > q: q' <- q' + 1
 +
 +This is done to ensure that the "​charge"​ can always reach its target.
 +
 +Then set q <- q'.
 +
 +== Strength adjustment ==
 +Let r,z be integers such that:
 +> If b == b', then r = Ri, z = 255
 +> If b != b', then r = Rd, z = 0
 +
 +Let s' be an integer such that
 +> s' <- s + (r*(z - s) + 128)/256
 +
 +If s == s', and s != z, then:
 +> If z < s: s' <- s' - 1
 +> If z > s: s' <- s' + 1
 +
 +Then set s <- s'.
 +
 +=== Filtering ===
 +
 +You can do anything here, within reason. These methods are by no means the best way to deal with the noise you get.
 +
 +These notes are based on the C implementation.
 +
 +== Antijerk ==
 +
 +If the current target and the previous target are different, output the average of the current and previous results from the predictor; otherwise, output the value directly.
 +
 +== Low-pass filter ==
 +
 +outQ <- outQ + (expectedOutput - outQ) * 100/256, essentially.
  
dfpwm.txt · Last modified: 2015/06/25 11:36 (external edit)