CamillaDSP - Cross-platform IIR and FIR engine for crossovers, room correction etc.

There's not a strong audible warning that sample rates are mismatched anymore. This seems to be a product of the I2S frontend in the Pi only having a single clock, and ALSA commands ignoring the sample rate.

All alsa operations are timed by the soundcard. In your case it is the external BCLK you feed the I2S controller with. Alsa and your hardware has no simple way to check if the clock frequency corresponds to the number passed to the driver in rate field. It would have to software-measure speed at which the I2S controller consumes the samples (similarly to what camilladsp does when adjusting the capture rate).

There is no rate indicator on I2S bus. SPDIF preable can carry rate information, but it is just an optional data field and the actual rate can be anything. SPDIF receivers typically have a hardware circuit which compares known crystal clock to the recovered SPDIF clock, guesses one of the audio rates, and offers this information through its registers or pins.
 
...16-bit I2S works fine :confused: and 24-bit Left Justified works wonderfully as well. 24-bit I2S is horrible white noise (still), and 24-bit Right Justified is massive clipping. This is making me think that there's something wrong with the I2S stream along the way...

So, it looks like the WM8804 board I'm using is all borked up in the AIF mode configuration. :rolleyes: I probed it out, and it seems the switches are swapped, making the "24b I2S" setting actually configure 24b LJ and vice-versa. 16b I2S and 24b RJ work as expected since those configs are symmetrical (00 and 11, respectively). This also explains why 16b I2S works as it should.

raptorlightning: dai-tdm-slot-width is terminology of the Alsa SoC framework (ASoC) for the device tree configuration.

Constants for the "format" parameter are soc-core.c - sound/soc/soc-core.c - Linux source code (v4.9) - Bootlin and soc-dai.h - include/sound/soc-dai.h - Linux source code (v4.9) - Bootlin

Your I2S interface must be configured to fit the I2S interface of WM8804. I have not tested other formats than "i2s" yet, but the "dai-tdm-slot-width" parameter works correctly for RPi4 - I tested I2S loopback from 8bits to 32bits. IMO the other supported formats work OK too bcm2835-i2s.c - sound/soc/bcm/bcm2835-i2s.c - Linux source code (v5.13) - Bootlin

Before I did any of the probing, I went through the code to the best of my ability and tried to find anything obviously wrong. I couldn't find anything wrong - it respects the 1xBCLK delay, and everything in the DT overlay is configured to I2S. I learned a bit more on how the BCM AIF works along the way, so it wasn't a total loss of time.

If all ALSA operations are timed by the AIF, then the sonic changes to the filters make sense as it's effectively shifting the filter frequencies around because of the difference between I/O sample rate and DSP config rate. I guess I'm a little surprised there aren't glitches, but perhaps there won't be since the DSP algorithm seems to slave itself off of the ALSA stream - the filters are just going to be "shifted off" by a frequency factor of data_rate/config_rate, as a simplified view.

The arecord | aplay command will just copy the bytes without looking at them. So even if things are wrong, it will still work as long as they are wrong the same way for playback and capture.

This appears to be proven, the bytes got piped through and it was wrong both ways - the 8804 was configured for LJ mode, and this affects both output and input!
 
raptorlightning: Do I understand correctly that setting the correct I2S mode in the DTS fixed the noise?

CamillaDSP will run at capture/playback speed, whatever it is. Yes, if its filters are calculated for samplerate X and it actually runs at samplerate Y, the resultant frequency transfer will be shifted accordingly.
 
Would anyone be interested in writing custom filter plugins in Python?

I had an idea of using PyO3: Introduction - PyO3 user guide to support user-written filters. These would be supplied as a .py-file. Something like this:
Code:
class Multiplier():
    def __init__(self, factor):
        self.factor = factor

    def process(self, values):
        newvals = [self.factor*val for val in values]
        return newvals
Would this be useful for you? If yes, what would you use it for?
 
raptorlightning: Do I understand correctly that setting the correct I2S mode in the DTS fixed the noise?

Not in the DTS, the software was all fine. Setting the right hardware pin configuration on the cheapo WM8804 board after buzzing out that the silkscreen legend was wrong fixed it. :rolleyes:

These two pins were flipped electrically on the board compared to what the silkscreen configuration was showing:

GfyvbQz.png
 

TNT

Member
Joined 2003
Paid Member
What could cause this:

clippedsamples
0
state
Paused
capturesignalrms
[-1000.0, -1000.0]
playbacksignalrms
[-78.15968, -77.54676]
capturerate
44100
rateadjust
0.0
bufferlevel
0
clippedsamples
0
state


... and it seems to flicker a tiny bit...

My player is stopped. Still, ther seem to some playback.. but I cant hear any noise etc... but it is low - 77dB so.. but still - should this not be much lower?

//
 
It's quite easy as long as you are ok with reloading the config when switching between analog in and streaming.
Use "arecord -l" to find the device name. Also try recording something with it to check that you get something. Then put the device name in your camilladsp config. You can disable rate adjust and resampling, none of that is needed.
 

TNT

Member
Joined 2003
Paid Member
Squeezlite running on a Mac Mini with the built in Toslink out to a Soekris DAC. Config is quite "busy" ;)

Versions
CamillaDSP: 0.5.0-beta5
pyCamillaDSP: 0.5.0
Backend: 0.6.0

devices:
adjust_period: 10
capture:
channels: 2
device: Soundflower (2ch)
format: FLOAT32LE
type: CoreAudio
capture_samplerate: 0
chunksize: 1024
enable_rate_adjust: false
enable_resampling: false
playback:
channels: 2
device: Built-in Output
format: FLOAT32LE
type: CoreAudio
queuelimit: 100
resampler_type: FastAsync
samplerate: 44100
silence_threshold: -90
silence_timeout: 10
target_level: 0
filters:
3kwide:
parameters:
freq: 3000
gain: -2.2
q: 0.55
type: Peaking
type: Biquad
Dither:
parameters:
bits: 16
type: Fweighted441
type: Dither
HFatt:
parameters:
freq: 12000
gain: -3.7
q: 1.5
type: Peaking
type: Biquad
HS2:
parameters:
freq: 900
gain: 1.4
type: HighshelfFO
type: Biquad
LT1:
parameters:
freq_act: 100
freq_target: 30
q_act: 1.3
q_target: 0.5
type: LinkwitzTransform
type: Biquad
No200:
parameters:
freq: 200
gain: -9
q: 10
type: Peaking
type: Biquad
Notch200:
parameters:
freq: 200
q: 4
type: Notch
type: Biquad
PK1:
parameters:
freq: 50
gain: 2.5
q: 6
type: Peaking
type: Biquad
PK12,4:
parameters:
freq: 12400
gain: -2
q: 6
type: Peaking
type: Biquad
PK2:
parameters:
freq: 250
gain: -4
q: 6
type: Peaking
type: Biquad
PK250:
parameters:
freq: 250
gain: -7
q: 1.5
type: Peaking
type: Biquad
PK2k:
parameters:
freq: 1900
gain: 5
q: 5
type: Peaking
type: Biquad
PK3:
parameters:
freq: 1800
gain: 0
q: 6
type: Peaking
type: Biquad
PK3,2:
parameters:
freq: 3200
gain: -1.8
q: 6
type: Peaking
type: Biquad
PK4:
parameters:
freq: 6100
gain: 3.8
q: 6
type: Peaking
type: Biquad
PK400:
parameters:
freq: 400
gain: -2
q: 0.5
type: Peaking
type: Biquad
PK5:
parameters:
freq: 10000
gain: 3
q: 6
type: Peaking
type: Biquad
PK5,8:
parameters:
freq: 5800
gain: -3
q: 6
type: Peaking
type: Biquad
PK6:
parameters:
freq: 100
q: 4
type: Notch
type: Biquad
PK7:
parameters:
freq: 250
gain: -6
q: 6
type: Peaking
type: Biquad
PK700:
parameters:
freq: 700
gain: 1.6
q: 3
type: Peaking
type: Biquad
PK8:
parameters:
freq: 400
gain: -7
q: 6
type: Peaking
type: Biquad
PK9:
parameters:
freq: 15000
gain: 11.9
q: 0.3
type: Peaking
type: Biquad
PKB:
parameters:
freq: 100
gain: 10
q: 0.5
type: Peaking
type: Biquad
PKB2:
parameters:
freq: 45
gain: 4
q: 1.2
type: Peaking
type: Biquad
Pk7,7:
parameters:
freq: 7700
gain: -4
q: 6
type: Peaking
type: Biquad
attn:
parameters:
gain: -10
inverted: false
mute: false
type: Gain
mixers: {}
pipeline:
- channel: 0
names:
- attn
- PK1
- PK2
- PK3
- PK4
- PK5
- PK6
- LT1
- PK7
- PK8
- PK9
- PKB
- PKB2
- PK2k
- PK400
- HFatt
- No200
- HS2
- PK250
- PK700
- PK3,2
- PK5,8
- Pk7,7
- PK12,4
- 3kwide
- Dither
type: Filter
- channel: 1
names:
- attn
- PK1
- PK2
- PK3
- PK4
- PK5
- PK6
- LT1
- PK7
- PK8
- PK9
- PKB
- PKB2
- PK2k
- PK400
- HFatt
- No200
- HS2
- PK250
- PK700
- PK3,2
- PK5,8
- Pk7,7
- PK12,4
- 3kwide
- Dither
type: Filter


//
 
This is the source if the noise you are seeing!

Dither:
parameters:
bits: 16
type: Fweighted441
type: Dither


The shaped dither types put most of the dither noise in a limited frequency range at the top. The sum of the needed noise energy is constant, if you compress it in a narrow range, the amplitude gets larger.



This is what the noise spectrum looks like (after boosting it by 50 dB):

dithernoise.png
All the energy is up close to 20 kHz. In summary, everything is as it's supposed to be :)
 
Now, I'm a bit surprised - isn't dither suppose to twinkle LSB?
//
Yes, for non-shaped dither. But an amplitude of 1 LSB isn't enough when you want to also do noise shaping, then you need a little more. How much more depends on how aggressive the shaping is.
You could try "Lipshitz" instead, should give a lower minimum level. Or even "Simple". But both of these will be more audible!
 
Hmm. If the Fs is 44,1 I think the VU meters should not show -75 for the scart in post #2111!?

//
It doesn't work to compare the numbers like that. The VU meters show the total signal level in RMS, and the chart shows the spectral distribution. You would need to take the integral of the spectrum to compare. Basically summing up the many frequency components at -105-ish dB gives that total amplitude of -75 dB.
 

TNT

Member
Joined 2003
Paid Member
968053d1626634631-camilladsp-cross-platform-iir-fir-engine-crossovers-correction-etc-dithernoise-png


The dither is a process in the digital domain and any shifted noise above Fs/2 should be taken away with the reconstruction filter. In te case of 44,1 thats 22,05. But the picture above only indicated noise above 65k.

Why is an added energy at 65k showing up as a VU signal level in a 44,1 ksps system - I suppose thats my question?

//
 
It's quite easy as long as you are ok with reloading the config when switching between analog in and streaming.

I already wrote a little GUI to control volume and switch between configs with a remote control. I was also thinking it would be possible to switch out the capture device in place.

Use "arecord -l" to find the device name. Also try recording something with it to check that you get something. Then put the device name in your camilladsp config. You can disable rate adjust and resampling, none of that is needed.

Yes, I should have tried recording first. :)

arecord -l only reports the one device:

Code:
card 4: M4 [M4], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

So, naively, I tried that as both capture and playback, but that didn't work. I'm thinking that I have to do more ALSA setup to get a capture device, so time to get a better handle on ALSA (or Jack, perhaps).

Code:
  samplerate: 96000
  buffersize: 8192
  enable_rate_adjust: true
  capture:
    type: Alsa
    channels: 4
    device: "hw:CARD=M4,DEV=0"
    format: S32LE
  playback:
    type: Alsa
    channels: 4
    device: "hw:CARD=M4,DEV=0"
    format: S32LE

filters:
  volume:
    type: Volume
    parameters:
      ramp_time: 200
  drc_l:
    type: Conv
    parameters:
      type: Wav
      filename: filters/s400+sb1kp-pos2-normal-96000.wav
      channel: 0
  drc_r:
    type: Conv
    parameters:
      type: Wav
      filename: filters/s400+sb1kp-pos2-normal-96000.wav
      channel: 1
  mainshighpass:
    type: BiquadCombo
    parameters:
      type: LinkwitzRileyHighpass
      freq: 80
      order: 8
  sublowpass:
    type: BiquadCombo 
    parameters:
      type: LinkwitzRileyLowpass
      freq: 80
      order: 8
  mainsdelay:
    type: Delay
    parameters:
      delay: 9.2
      unit: ms
      subsample: false
  
mixers:
  to3:
    channels:
      in: 4
      out: 4
    mapping:
      - dest: 0
        sources:
          - channel: 2
            gain: 0
            inverted: false
      - dest: 1
        sources:
          - channel: 3
            gain: 0
            inverted: false
      - dest: 2
        sources:
          - channel: 2
            gain: 0
            inverted: false
          - channel: 3
            gain: 0
            inverted: false

pipeline:
  - type: Filter
    channel: 2
    names:
      - volume
      - drc_l
  - type: Filter
    channel: 3
    names:
      - volume
      - drc_r
  - type: Mixer
    name: to3
  - type: Filter
    channel: 0
    names:
      - mainshighpass
      - mainsdelay
  - type: Filter
    channel: 1
    names:
      - mainshighpass
      - mainsdelay
  - type: Filter
    channel: 2
    names:
      - sublowpass