For the non-expert:
DSP = Digital Signal Processing
PDM = Pulse Density Modulation
PCM - Pulse Code Modulation
I am struggling with what is sounding like an evenly distributed hiss on top of otherwise very good sounding audio when decoding pulse density modulated audio to pulse code modulated audio.
The bitstreams I am working with are 1 bit 2.8225 Mhz PDM, and I am "resampling" it to 16 bit 44.1 kHz PCM. That translates to about 64 one bit samples per 16 bit PCM samples at 44100, thus I am using a 64 order window function to make PCM out of PDM.
How I am processing the audio is like this:
First I am making a precalculated window function like this:
for (int i = 0; i < 64; i++) {
fir64Coff[i] = (double_t)(1 << 10);
//Hanning Window (less noise than hamming?
fir64Coff[i] *= 0.5 * (1 - cos((2 * M_PI * i)/ (64 - 1)));
//Hamming Window
// fir64Coff[i] *= 0.54 - 0.46 * cos((2 * M_PI * i) / (64 - 1));
//Nuttall Window
// fir64Coff[i] *= 0.355768f - 0.487396*cos((2*M_PI*i)/(64 -1)) + 0.144232*cos((4*M_PI*i)/(64-1))-0.012604*cos((6*M_PI*i)/(64-1));
//Cosine Window
// fir64Coff[i] *= sin((M_PI * i)/(64-1));
//Blackman Harris
// double_t w = (2 * M_PI * i) / 64;
// fir64Coff[i] *= 0.422323 - 0.49755 * cos(w) + 0.07922 * cos(2 * w);
}
Hamming/Hanning sounds the best of the window types I have tried.
Here is how I generate PCM from PDM: (Be warned this is just some pilot tinkering.)
- (uint64_t)getBytes:(void *)buffer sampleCount:(long unsigned)count {
if (mIsEndOfSong) {
return 0;
}
[mutex lock];
int16_t *sampleBuffer = (int16_t *)buffer;
for (uint32_t currentSample = 0; currentSample < count; currentSample++) {
uint64_t testLeft = 0;
uint64_t testRight = 0;
int64_t sumLeft = 0;
int64_t sumRight = 0;
uint8_t coefficientIndex = 0;
for (uint8_t i = 0; i < 8; i++) {
if (currentBufferPos >= blockSize) {
if (![self readChunk]) { //Here I fetch left and right channel blockSize bytes (Usually 4096)
[mutex unlock];
return currentSample;
}
}
testLeft = bufferLeft[currentBufferPos];
testRight = bufferRight[currentBufferPos];
for (uint8_t i = 0; i < 8; i++) { //Two nested for loops, 8 * 8 = 64 bits, lsb first per byte
if ((testLeft & 0x01) == 0x01) {
sumLeft += fir64Coff[coefficientIndex]; //These are the pre-calculated window values
}
else {
sumLeft -= fir64Coff[coefficientIndex];
}
if ((testRight & 0x01) == 0x01) {
sumRight += fir64Coff[coefficientIndex];
}
else {
sumRight -= fir64Coff[coefficientIndex];
}
testLeft >>= 1; //Next bit
testRight >>= 1;
coefficientIndex++;
}
currentBufferPos++; //next byte
}
sampleBuffer[(currentSample << 1) + 0] = (int16_t)sumLeft;
sampleBuffer[(currentSample << 1) + 1] = (int16_t)sumRight;
}
[mutex unlock];
return count;
}
Pretty simple, actually, and it sounds great - except for the noise. I am not sure what I am doing wrong. I am sure super audio is not supposed to have varying degree of noise in them. I have read that when audio is being sampled, a noise shaping function is being used, and noise generated during this process is pushed up into the inaudible frequency range. I am wondering if the noise I am hearing can be this high frequency noise being aliased back into the perceptible frequency area.
Does anyone have any tips for me, how to get rid of the noise? I tried to make a low pass filter too, but that made the noise even louder and with no music at all, so there is probably something I did wrong.
Here is the function I made for that, if it can be of any help:
- (void)lowPassFIR:(double_t*)firBuffer withSampleRate:(uint64_t)Fs cutOffFrequency:(uint64_t)Fc filterLength:(uint64_t)M {
M = M -1;
double_t Ft = (double_t)Fc / (double_t)Fs;
double_t sum = 0.0f;
for (uint64_t i = 0; i < M; i++) {
if (i != ((double_t)M / 2)) {
firBuffer[i] *= sin(2.0f * M_PI * Ft * (i - ((double_t)M / 2.0f))) / (M_PI * (i - ((double_t)M / 2.0f)));
}
else {
firBuffer[i] *= 2.0f * Ft;
}
sum += firBuffer[i];
printf("%f\n", firBuffer[i]);
}
printf("Sum: %f\n", sum);
}
Edit: it is worth mentioning that I am trying to learn DSP, but I am an utter noob in this area. I have a vague understanding, but not all the pieces are in place yet, so I think I have done pretty well, considering I have just jumped into this. This is also something I am doing just for fun and to learn, and weirdly something as recognised as VLC can't play either.
Edit2: I have a feeling the noise comes because I am not properly "moving" the window, just taking a weighted average starting at every 64 bits, but I am trying to reduce the massive amount of processing I need to do if I would do a moving average of every 64 bit bits before I have produced even one PCM sample. That would make 64 * 64 = 4096 loop iterations for each PCM sample, 4096 * 44100 = 180.633.600 iterations per second... I think my computer would have hiccups long before that.
Another theory I have, is that I am just discarding the remainder of the double after a full integer, and that is generating some dithering noise.
Edit3: Just got an idea to get completely rid of the if-sentences in the inner loop. I can just make a lookup table for that as well... That means I can get rid of the whole inner loop completely.