I’ve not seen a suitable plug-in, but it could be an interesting project to make one using Nyquist.
According to this page the carrier signal was in the range of 30 to 50kHz (30,000Hz to 50,000 Hz), so for this to work your audio equipment must all be capable of handling these very high frequencies. In particular, your sound card must be capable of recording with a sample rate of 192 kHz and must not filter out frequencies between 30 and 50 kHz.
I’d be happy to help develop a Nyquist plug-in for this, but I don’t have CD4 capable equipment.
I wrote previously: “A good place to start would be to record a short sample recording (WAV format) using a sample rate of 192 kHz so that we can examine it”. I’m still waiting.
I have attached a short sample in FLAC format as the WAV file was a bit too short, unfortunately its only a 96kHz I hope that will do. I try and get a 192kHz sample and as soon as I do I’ll post it.
I’ve been searching around for some documentation on CD-4 (Quadradisc) and turned up a PDF of an original document that was pa-portably written by JVC entitled “CD-4 AN INTRODUCTION”.
It goes in to detail about the modulation methods used; specifically that both frequency and phase modulation techniques are utilised. In addition there it goes in to detail about the “Automatic Noise Reduction System” (ANRS) it utilises to prevent interference between the audio signal and the modulated difference signal.
I have also analysed the file I sent you and the is a clear peak around 30kHz which is what you expect based on the information we have about the CD-4 format. See attachment showing the graph
Hi TimDog73, Just to say I’m not ignoring you - I’ve just got back from vacation/holiday and I’m working my way through a mountain of e-mail. It’ll be a while 'till I get to reading through the documentation that you posted but I look forward to doing so as soon as possible.
I’ve used this time to try and understand the demodulation steps we have to follow to recreate the four channels and produced the attached diagram (with thanks to my friend Steve for peer reviewing the diagram).
I’ve attached a flow diagram for the demodulator with the ANRS expander circuit split in to its filtering components and expansion components. There is also more explanation in this one too.
That’s pretty complex.
The difficult parts are getting the demodulation to work correctly and then handling the dynamic expansion correctly.
As a proof of concept (but dreadful sound quality) we can demodulate the 30 kHz fm component with something like:
(setf s
(mult
(highpass8
(lowpass8 s 30000)
30000)
(hzosc 30000)))
(defun normalize (sig)
(mult 0.8 sig (/ (peak sig ny:all))))
(multichan-expand #'normalize s)
The result from your demo file is:
(I said it was awful )
I’m sure that the demodulation could be improved (though I’m not sure off the top of my head how we would emulate a phase locked loop digitally), but then the dynamic expansion will need to be exactly right in order to achieve correct sum and differencing. I’ve not read all of the pdf files that you posted, but to get that right there’s a lot of parameters that we need to know (both the compression and expansion thresholds, the attack and release times, and the knee radii), and if that was not enough, we also need to maintain the correct phase relationships across the full audio frequency spectrum. If we don’t get all of this right the result will (sadly) be garbage.
In short, it is easy to demonstrate the principle, but to produce a CD4 decoder that sounds anything other than complete rubbish is likely to be very difficult.
Hi Tim,
Steve was so kind to introduce us presently. I’ll try to support you in the plug-in development.
Firstly, I must familiarize myself with the cd4 stuff. Unfortunately is none of your provided material accessible for screen readers. Hence, I must do some OCR with them.
It’s certainly a interesting project and other topics could profit from the outcome as well (Dolby decoding for example).
Best regards
Robert
Yeah, but only if the direct signal (with about 40 ms delay) and the demodulated signal can be properly synchronized.
The low-pass and band-pass filters, along with the equalization and compander section produce serious phase shifts. Even on the original device are 3 to 4 knobs to set up the turntable after each individual cartridge change. It is like solving an equation with 10 unknown variables. Ideally, we had both, a record from tim’s device and the digitally transferred channels from a quadrophonic turntable, e.g. a test disc.
Big question, why does Steve’s code snippet produce such a horrible sound?
Answer: Better ask, why does it produce sound at all?
The code does an AM demodulation, i.e. the message is coded in the instant amplitude of the 30000 Hz carrier.
CD-4 is modulated in “FM PM and SSB-FM” which is very nasty to demodulate.
Frequency modulation only squeezes and expands the wave-form horizontally, that’s why we shouldn’t hear anything after an AM demodulation.
The developers have now decided to adapt the FM-signal to the Main (what you hear on a stereo TT) signal’s amplitude, always about 19 dB below it (so called CLC - Carrier Level Control).
There’s additional noise and the groove in the other wall that modulates the carrier’s amplitude further.
FM-demodulation hinges on a signal of equal amplitude throughout. Hence, the first steps to retrieve the original message are:
Highpass filter at 15500 Hz to get the carrier + side bands.
Limit the carrier by hard-clipping.
(clip (mult 10000 carrier) 1)
Bandpass the result between 20000 and 45000 Hz to remove the square wave harmonics.
Actual demodulation with phase lock loop
The PLL from the last step is rather hard to implement because it uses a recursive approach.
I’ve now employed another method that firstly doesn’t need a limiter and secondly needs no lowpass update recursively.
It splits the signal into sine and cosine and keeps tracking the instant phase.
It works at the moment only for short periods because the result is gathered in a fixed array.
That’s the sound of the left channel from the sample Audio above:
I’ve also used an inverse RIAA to bring up the bass a bit. Unfortunately, CD-4 uses an own equalization curve + a expander for the noise reduction. I can’t read the response curves myself, thus…
By the way, the FM signal has only -31 to 36 dB, instead of -19 dB. I therefore assume that a normal stylus has been used.
The snippet first applies a narrow band pass filter at the carrier frequency.
The FM signal frequency has an average frequency equal to the carrier, but is modulated (by the message signal) so that the instantaneous frequency oscillates either side of the carrier frequency.
As the FM signal frequency moves through the narrow filter range, the amplitude will change such that maximum signal occurs when the FM signal frequency matches the filter’s centre frequency. In effect, the narrow pass band filter converts from FM to AM.
The resulting AM signal is then demodulated.
It’s primitive, but simple code and works as a proof of concept.
No, it doesn’t work with FFT, but it is related to it.
It is a Discreet Fourier transformation that returns for each sample a complex vector, tuned to the carrier frequency. It is basically what I’ve written in my PM.
(setf sig (highpass8 s 15500))
(defun dem (s cf)
(let* ((ref-osc (osc (hz-to-step cf)))
(ref-quad (osc (hz-to-step cf) 1 *table* 90))
(sn (lowpass8 (mult s ref-osc) 15500))
(cs (lowpass8 (mult s ref-quad) 15500 ))
(out (make-array (truncate len)))
(c-1 0) (s-1 1))
(dotimes (i (truncate len))
(let* ((c0 (snd-fetch cs)) (s0 (snd-fetch sn)))
(setf (aref out i)
(atan (+ (* s-1 c0) (* c-1 s0))
(- (* c-1 c0) (* s-1 s0))))
(psetq c-1 c0 s-1 (* -1 s0))))
(setf (aref out 0) 0)
(snd-from-array 0 *sound-srate* out)))
(setf sig (highpass8 (dem sig 30000) 50))
(mult (/ (peak sig ny:all)) sig)
C0/s0 is the current complex pair and c-1/s-1 is the conjugate of the past one. The atan function returns the instant phase for the pair.
This is normally very slow and therefore replaced with a lookup-table or a curve fitting function.
The code is not yet very refined, it should actually be implemented as object/class.
Please note: it works only with a mono track, stereo gives a nice visual C++ runtime error.