Clipfix (see waveform pic) - plugins available?

My first version of Clipfix (with inversion correction) is available here :

This code is hopelessly inefficient - basically it is just the original code with “proof of concept” inverted signal correction bodged on at the front.

I have a slightly more efficient version somewhere which I will try to search out for you. In that version, all the sample corrections take place within the same program loop (the original ClipFix runs one loop to correct positive values, and a second loop for negative values - and my first version ran yet another loop for inversion correction).

After that version, I gave up trying to modify the original code as there were quite a few issues that needed fixing and started writing the code from scratch. That is where I am at now. Hopefully have a bit more free time this month to get it finished.

I just came across this thread, after having encountered the problem everyone has described. I was recording live and had the level set just a little too high so got some sections with digital overflow distortion.
So I went and wrote a Nyquist plug-in which I think is very similar to what Arnie and Stevethefiddle were talking about. Sorry that we’re all duplicating effort!
This plug-in checks sample by sample: if sample magnitude exceeds a user threshold, it starts looking for distortion; then, if it detects a big jump going the opposite direction of the waveform trend, it assumes it is overflow, and it patches in a half cycle of a sine wave between the threshold points.
I have tested it on some short segments and it looks ok. I suppose it is a processor and memory hog on longer segments.
I am trying to figure out how to upload a plug-in. Is there some repository? Until then, here is the whole thing inline, including debug code.
Please let me know if this helps, or if it has some horrible defect.
I sure there were primitives to convert sound to list and back!

;nyquist plug-in
;version 1
;type process
;name "Digital Distortion Post-Limiter..."
; rationale
; detects digital distortion = DAC overflow
; and replaces distorted intervals 
; with interpolated half cycle of a sine wave
; control input
; sets the threshold level for interpolation
; if distortion is detected, all samples between
; threshold levels will be replaced by the interpolation.

;action "Detect and clean up Digital Distortion..."
;info "Digital Distortion Post-Limiternby Lex Lindsey May 2009nGNU Copyleft"
;control thresh "Limiting threshold" real "Peak" .8 0 1

; DEBUG get output stream
; (setq os (make-string-output-stream))

; REBUILD sound array after 
; processing each channel separately 
(defun process-s (a s)
; (prin1 a)(prin1 s)(terpri)
	(if (arrayp s)
		(vector (apply a (list (aref s 0))) (apply a (list (aref s 1))))
		(apply a (list s))

; CONVERT list of sound samples into a sound 
; whew wish there were some primitive to do this!
; must first convert the list into an array,
; then store all the list elements into the array,
; then convert the array into a sound.
; need sound rate from master 
(defun snd-from-list (sr lst)
	(let* ((nl (length lst)) (ar (make-array nl)))
		(do ((n 0 (1+ n)))
				((= n nl) (snd-from-array 0 sr ar))
			(setf (aref ar n) (car lst))
			(setq lst (cdr lst))

; CONVERT a sound into a list
; again, whew, wish there were a primitive!
; pass the count of samples because
; the duration argument to the sine function
; apparently does not work when called from inside Audacity
; (though it works fine when tested directly from ny shell)
(defun list-from-snd (s ns) 
	; (format os "list-from-snd len(s)=~S ns=~S~%" (snd-length s 2000) ns)
	(let (lst (v1 (snd-fetch s)))
		(while (> ns 0)
			(setq lst (append lst (list v1)))
			(setq v1 (snd-fetch s))
			(setq ns (1- ns))

; save samples while looking for possible digital distortion
(setq fi ()) 	; save list
(setq nf 0)		; count
(setq v1prev nil)
(setq ddf nil)	; flag digital distortion

; DEBUG - track sample index
(setq sk 0)

; DEBUG - store values when fi is accessed
; (setq filook nil)
; (defun showfi () (print "sk,fun,v,v1prev,nf,ddf=") (printlist filook))
(defun defi (v fun) 
	; (setq filook (append filook (list (list sk fun v v1prev nf ddf))))

; threshold is a constant, should be input from user
; DEBUG (setq thresh .8)
; the limiting range starts at the first saved prior value
; which crosses the threshold in either direction;
; remember all values between that position and the 
; next position where the signal crosses the threshold 
; going in the opposite direction

; SAVE sample
; return nil to flag defer output 
; check for big jump in sample value => digital distortion
; then save previous sample value for test 
(defun savefi (v1) 
	(setq fi (append fi (list v1)))
	(setq nf (1+ nf))
	(setq ddf (or ddf (> (abs (- v1 v1prev)) thresh)))
	(defi v1 "savefi")
	(setq v1prev v1)

; START saving possible overflow samples
; must return nil so caller defers saving 
; the list of sounds which may include digital distortion
; 2do: check to make sure this is not a false peak
; due to start of selection being at a high level
; could be logical loophole if selection actually starts over thresh
(defun startfi (v1) 
	(setq fi (list v1))
	(setq nf 1)
	(setq v1prev v1)
	(setq ddf nil)
	(defi v1 "startfi")

; leftovers - if end of sample list while
; 	in the midst of defering output,
;	must append saved list to output
; since appending nil is a no-op, not much to do
; but must call crossback since could be tracking possible DD
(defun leftover (s2)
	(append s2 (cond (fi (crossback nil))))

; fixup the overflow by post-limiting 
; generate a positive-going sine envelope of 
; frequency fe = fs / 2 / nf
; where fe = envelope frequency, fs = sample frequency, 
; and nf = num of samples in the overflow range
; peak magnitude of envelope is difference between peak and threshold,
; which is constant

; NB: duration does not work when called from Audacity
; so just generate an *infinite* sine wave, but only use nf samples
; DEBUG: the big let* statement is to make debug display easier

(defun limit-envelope (fi nf)
	(let* (ev (efreq (/ *sound-srate* (* nf 2)))
			(ethresh (/ (+ v1prev (car fi)) 2))
			(emagn (- 1 (abs ethresh)))
			(ephase (if (> (car fi) 0) 1 -1))
			(escale (* emagn ephase))
			(epitch (hz-to-step efreq))
			; generate half a cycle of a sine wave
			; scaled by limited magnitude and phase
			(esine (sine epitch))
			(ess (scale escale esine))
			(eoff (snd-offset ess ethresh))
; (format os "limit-envelope efreq=~S,emagn=~S,ephase=~S~%" efreq emagn ephase)
; (format os "limit-envelope ethresh=~S,epitch=~S~%" ethresh epitch)
; (format os "limit-envelope len:esine=~S~%" (snd-length esine 2000))
		(setq ev (list-from-snd eoff nf))
		; (format os "length:fi=~S,ev=~S~%" nf (length ev))

; CROSSED BACK under threshold,
; check to see if dd was detected
; if dd, then return limited envelope
; else just return the saved sound list
(defun crossback (v1)
	; check for nil - do not save if ending
	(cond (v1 (savefi v1)))
	(defi v1 "crossback")
	; (format os "crossback:nf=~S~%" nf)
	(cond 	(ddf (limit-envelope fi nf))
			(T fi)

; check for threshold
; if already saving, check for threshold crossing
;	if crossed, did we find any DD?
;	if DD, output the limited envelope
;	else output the saved samples
; else not saving, so if threshold crossed,
; 	start saving

; if checks returns non-nil, 
; it is a list to be output as sound
(defun checks (v1)
	; (prin1 (list 'v1= v1 'fi= fi))
			; already saving - check to see if crossed back
			; however, could cross under thresh while still
			; in the overflow region, so must test to see
			; that sign of v1 is same as that of first saved fi
				((and (< (abs v1) thresh) (plusp (* v1 (car fi))))
					; crossed back, check whether dd found
					; returns fixup or saved sound list
					; must clear the save list
					(let ((f (crossback v1))) (setq fi nil) f))
				(T (savefi v1)) ; also check for overflow
		(T 	; not in a potential overflow 
				((> (abs v1) thresh) 
					(startfi v1) ; crossed threshold
				(T (list v1)) ; return sample to be appended to output
; must define s2 global to use
; this single-step debug function
'(setq s2 ())
'(defun check1 (s1)
	(let ((v (snd-fetch s1)))

		; the main workhorse
		; if checks returns non-nil, 
		;	it is a sound list to append to s2
		(let ((v2 (checks v)))
			(cond (v2 (setq s2 (append s2 v2))))

		; debug track index  globally
		(setq sk (1+ sk))

; CHECK CHANNEL Sample list
; mono - so needs to have process-s split and rebuild the channels
; needs to build the output sound list s2
(defun checkchannel (s1)
	(let (s2 (srate (snd-srate s1)))
		(do ( (v (snd-fetch s1) (setq v (snd-fetch s1))))
			; remember to tack on any leftovers to s2 
			; if in the midst of an overflow check, 
			; there will be a sound list being saved
			((null v) (snd-from-list srate (leftover s2)))

			; the main workhorse
			; if checks returns non-nil, 
			;	it is a sound list to append to s2
			(let ((v2 (checks v)))
				(cond (v2 (setq s2 (append s2 v2))))

			; debug track index  globally
			(setq sk (1+ sk))

; (format os "length:s0=~S~%" (snd-length (aref s 0) 2000))

; value of x is the return
(setq x (process-s 'checkchannel s))

; DEBUG output
; last value is string containing all previous stream output
; so will be displayed in a dialog
; (format os "length:x0=~S~%" (snd-length (aref x 0) 2000))
; (get-output-stream-string os)

I’m still following this thread and am happy to see the progress that’s being made. Thank you steve and lexchis for your succesful implementations!



As I replied to your question on the Wiki, Download Nyquist Plug-ins is the intended master repository, but we have not had the resources to finish that page. Plug-ins listed there would have to have a permanent link we could point to, or would have to be sent to me so I could upload them to our own hosting.

While it would be valid to post “alpha” plug-ins there for testing, I’m not sure we should post versions of a “clip fix” plug-in that compete with each other. So I think we should wait for Steve and Lex to discuss this a bit more.


Hi Lex

It wouldn’t in my opinion be generally useful for the reasons you state - on a 10 second segment of music which Clipfix (in 1.3.8) processes in 6 seconds on my system, your plug-in expects to finish in 50 minutes after taking five minutes already. It would need a Licence too, usually General Public License Version 2.

Has anyone tried Repair in Audacity Beta (only works on up to 128 samples)? Does it do anything meaningful with inverted clipping? Arnie’s does not seem to be available now, so I never tested with a sample that has this sort of clipping. I’m still not very clear how common this type of clipping is in the wild. Is it detectable by Audacity’s Find Clipping?

Steve, I’m still interested in your modified Clipfix with inverted polarity fixing, if you are in a position to make it available sometime.


<<<I’m still not very clear how common this type of clipping is in the wild.>>>

It’s common enough to make this one of the longest forum threads…ever. The people over on the video forums would kill to be able to “repair” clipping. The broadcast sound levels were chosen to be very robust and allow for a generous headroom and very, very good noise floor.

The consumer camcorders hit the stores with a different sound standard and no overload protection. Zero. The first time the talent leans into the microphone or clears their throat, you’re dead. Remember over there, nobody is paying any attention to the sound until they get back to the studio.

Why is my interview snappy and crunchy?

Over here in audio land everybody knows you need to record live performances peaking at 0, just the same level that all recorded music is delivered and enjoyed. Nobody does that more than once, of course, but that once is a capture of the only time “Earth At Night” ever performed on the New Jersey Shore and it will never happen again.

This is also, sad to say, a tool that will satisfy nobody, much like Vocal Removal, Silence Sense and, in some cases, Noise Removal. It was designed to correct five or six snapping peaks per hour of show, not continuous 25% harmonic distortion for 60 minutes. Nobody is coming back from that. By the time you realize you need the tool, it’s too late.


I’ve tried the same thing. Downloaded the plugin, unzipped it, put it into the C:Program FilesAudacityPlug-Ins directory, then restarted Audacity, but no luck. It’s not on the Effects menu. I checked the folder, and clipfix.ny is right there along with the other 14 plug-ins. Restarted Audacity again, and again. Still no luck.

Any clue what I’m doing wrong?


You have to look lower down the Effect menu, underneath the divider.

If you use the current (1.3.12) Audacity Beta, Clip Fix is now included.


Thanks. However, when I said Clip Fix is not included in the Effects menu, I did not mean it wasn’t included only in the top part. It was not ANYWHERE on the menu, including below the divider.

I’ll download the 1.3.12 version, but when I go to the Help/About Audacity menu, it says I have version 1.3.5. Will I be going backwards? Or is that version a misprint?

12 is a greater number than 5, so yes 1.3.12 is a later version than 1.3.5. If I had written, then it would be earlier than 1.3.5.


Ok minor apologies to bring up and old thread but is the experimental ESP clip fix plugin still available somewhere as I have a piece of non redoeable audio with inverted clipping and it seems like this function was never included in the standard clip fix plugin as planned.

Inspiring work :wink:



Here’s the experimental plug-in
ESP-clipfix.ny (5.79 KB)
Note that it will only work with mono tracks.
I was very new to Nyquist when I wrote this, so there’s no guarantees about the code being correct - use at your own risk :wink:

If your recording is stereo you can split the track (click on the track name and select “Split Stereo Track” from the drop down menu), apply the effect, then click on the name of the upper track and select “Make Stereo Track” to join the tracks back together.

Wow and many thanks :slight_smile: .
You’re looking at a risk taker :smiley: .
I will report back on this one …



Ok as promised some feedback.
The file in question had a 10 minute section with the inverted clipping. I had to raise the level so average peaking were around -0.3 db which meant some clipping, which got mopped up later anyway. Content was speech. From my distant memory this kind of disortion is caused from HF instability occuring when gain is too high, something is microphonic or perhaps alc is getting carried away. Transients trigger massive oscillation which overloads the circuit/supply giving the resultant dips. This can happen during a peak or often on the decay afterwards…in other words it not a simple level clip. ESP clip fix did pick up the transients and flatten them but the following standard clip fix resoration needed the higher level to take some action. The result was at least a softening of the distortion which was then smoothed over which a sharp low pass filter (which also helped generally as in the region of the distortion HF noise was apparent which further confirmed the instability cause theory…there is also a distinct ringing artifact on the track in the affected portion). The improvement was to go from very painful to a tolerable noise resembling a match being struck…a success in this case as content rather than fidelity was the important factor.
I found picking out sections of up to 2 seconds worked best with much use of ctrl-R.

Many thanks for the help


Thanks for the feedback mike and glad you had a success. :slight_smile: