Audio pitch down code debugging

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am sampling an audio signal of 1 KHz and would like to generate an output which is half of that. ADC & DACs are working properly, however it seems the output freq. is still 1 KHz. What am I missing here?

 

Here is the code:

if (state == 0)
{
    DACWrite(SRAMByteRead(readPtr));
	if (readPtr++ == AddressTop)
		readPtr = 0;
	state++;
} 
else if (state == 1) state = 0;

// combine prev. & new samples
if (AddressCurrent == 0)
	SRAMByteWrite((uint16_t)((SRAMByteRead(AddressTop) + ADC0.RES)) >> 1, AddressCurrent);
else
	SRAMByteWrite((SRAMByteRead(AddressCurrent - 1) + ADC0.RES) >> 1, AddressCurrent);

if (AddressCurrent++ == AddressTop)
	AddressCurrent = 0;
}

The audio samples are written into a cyclic buffer with AddressCurrent holding the index, each time it is incremented until it hits the top and then goes to 0.

At each memory location the previous sample and averaged with the new sample to avoid clicking noise when the index "completes a cycle" in the buffer.

 

state regulated how often the DAC is written, with a value of 1 the DAC is written at half the rate this procedure is being called, which is also half the sampling rate so I expect it to generate a freq. half of the input. If state = 2 it should be 1/3 and so on.

 

However this does not work.

Last Edited: Sat. Jul 6, 2019 - 08:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I read this requirement correctly: slow_rider is trying in real-time to play sampled audio at half speed. Y/N ?

 

If above is true, grab a cup of tea and sit and think what would happen, if; in the incoming audio the bass guitar starts playing at 10s and the vocalist sings at 20s, what is your half-speed output playing at 10s (ans=still playing the intro) and the output when the vocalist begins at 20s is in fact the bassist starting to play.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:

If I read this requirement correctly: slow_rider is trying in real-time to play sampled audio at half speed. Y/N ?

 

If above is true, grab a cup of tea and sit and think what would happen, if; in the incoming audio the bass guitar starts playing at 10s and the vocalist sings at 20s, what is your half-speed output playing at 10s (ans=still playing the intro) and the output when the vocalist begins at 20s is in fact the bassist starting to play.

 

 

The pitch conversion is not suppose to happen in "real" real-time, since playback is slower than the recording it will never catch up. We can think of this as having a pre-recorded buffer with no new info added. The general concept of playing back at half speed should lower the pitch though, right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could've simplified your code so we could read it!

 

You get two input samples and output them at half the rate. 1kHz is still 1kHz, you've just decreased the sample rate! Try the original samples and output at a slower rate.  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

You get two input samples and output them at half the rate. 1kHz is still 1kHz, you've just decreased the sample rate! Try the original samples and output at a slower rate.  

 

The above code is called 10,000 times a second, every time a new sample is added to the buffer. Every other time a sample is sent to the DAC so the output is at half the freq. of the input.

Perhaps I am misunderstanding what you wrote?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

slow_rider wrote:
The pitch conversion is not suppose to happen in "real" real-time,

So where do your input samples come from ? SD-card / program flash ??

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You aren't going to do real-time audio pitch-shifting with an AVR.  You need a 100MHz 32-bit CPU with DSP registers to do that.

 

You can get sort-of audio pitch shifting by putting the audio signal into a fuzz-box to make it into square waves.  Then make these square waves be +5V and Gnd.   Now use a 4046 phase-lock loop to multiply these square waves and a set of flip-flops to divide them.  Finally run the +5_gnd through a voltage divider to get a 1V peak-to-peak line-input audio signal and a low-pass filter to get rid of the fuzz tone.

 

Works best with an electric guitar.  Multiply the input signal by three using the 4046 and then divide by two to get a 1.5x frequency signal.  This produces a synthetic note that is a fifth above (freq * 1.5) the guitar's note.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You aren't going to do real-time audio pitch-shifting with an AVR.  You need a 100MHz 32-bit CPU with DSP registers to do that.

Try again...here is a pitch changer, all ready to use...code and everything right here & ready to change pitch up or down in real time 

http://www.technoblogy.com/show?1L02

 

 

here's some more...a bit fancier   http://elm-chan.org/works/vp/report.html

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sat. Jul 6, 2019 - 11:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:

http://www.technoblogy.com/show?1L02

 

here's some more...a bit fancier   http://elm-chan.org/works/vp/report.html

 

Honestly I started with the ELM-CHAN website. At some point he talks about a "cross-over" buffer which I solved in the very similar fashion what is actually shown on the 1st website you have mentioned. I simple did an average of two consecutive samples all over the buffer. In normal playback it sounds OK. I am not 100% sure what am I doing different than him.

 

My entire code runs at 10 KHz, each time a new sample is added to the buffer (using the averaging scheme I've just mentioned). Another buffer pointer is used to read from the buffer and it is done at fractions of 10 KHz but using a simple software freq. divider:

 

if (state == 0)
{
    DACWrite(SRAMByteRead(readPtr));
	if (readPtr++ == AddressTop)
		readPtr = 0;
	state++;
} 
else if (state == 1) state = 0;

Perhaps I am doing this the wrong way, I am writing the DAC at half the sampling rate here. Isn't that the same as using another timer at lower freq. to do the reading from the buffer?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

slow_rider wrote:
Every other time a sample is sent to the DAC so the output is at half the freq. of the input.

Nope.  Half the sampling period resolution, but the output frequency will still be the same as the input frequency (until you hit Nyquist territory).  Draw a wave, mark some equally-spaced sample points, then erase half of them.  No change in frequency, just a less accurate waveform representation.

Last Edited: Mon. Jul 8, 2019 - 12:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Off-topic: I just saw a film about Woodstock which was

great and it reminded me of an effect pedal Jimi Hendrix

used to double the pitch of his guitar (opposite of what

the OP is after).  Very simple - you just route the guitar

signal through a full-wave rectifier. The negative half of

the wave is folded over zero making the signal look like

it's twice as fast. This only sounds good when playing

single notes so it's used for guitar solos.

 

--Mike

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
if (state == 0)
{
    DACWrite(SRAMByteRead(readPtr));
	if (readPtr++ == AddressTop)
		readPtr = 0;
	state++;
} 
else if (state == 1) state = 0;

// combine prev. & new samples
if (AddressCurrent == readPtr)
	SRAMByteWrite((uint16_t)((SRAMByteRead(readPtr) + ADC0.RES)) >> 1, AddressCurrent);
else
	SRAMByteWrite(ADC0.RES , AddressCurrent);

if (AddressCurrent++ == AddressTop)
	AddressCurrent = 0;
}

I think I fixed that for you.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@N.Winterbottom - So now each time a new ADC sample will be written to the buffer its as-is other than when the write & read pointers are equal and in that case we blend the ADC sample with the previous sample stored in the memory cell. If I understand your rationale, shouldn't the condition actually be:

if (AddressCurrent >= readPtr)

otherwise the only time you are blending is on the 1st sample, next time when the writing finishes a loop around the buffer and starts writing from the beginning and hitting the same memory cell as the read pointer (exact time when this happens depends on buffer's length)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I understood from the linked articles in #8 the "blending" is only a hack at suppresing the click as you jump from an old set of samples to a new set.

Keep it simple - Just try without the "blending" for now.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The idea is similar to "granular synthesis".  You are recording short (about 10 ms) samples into a circular buffer and overlapping.  You need to average (cross-fade) the overlapping portion to avoid the clicking.  To change pitch you simply reduce or speed up your playback from the circular buffer.  The result is a bit choppy but for voice alterations it works fine.

 

So the idea is you playback at half the rate of your recording speed.

 

The best explanation I found is from the Adafruit tutorial,  https://learn.adafruit.com/wave-shield-voice-changer/principles-of-operation

This example uses a standard Arduino code for an ATmega328P.

 

Last Edited: Tue. Jul 23, 2019 - 12:21 PM