Voice changer

What are you developing?
User avatar
Pito
Posts: 1529
Joined: Sat Mar 26, 2016 3:26 pm
Location: Rapa Nui

Re: Voice changer

Post by Pito » Mon Jun 20, 2016 9:35 am

You have to convert signed short inputs (16bit) into q15 (that is a different encoding scheme).
And vice versa..
Pukao Hats Cleaning Services Ltd.

User avatar
RogerClark
Posts: 6911
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Voice changer

Post by RogerClark » Mon Jun 20, 2016 9:46 am

Pito wrote:You have to convert signed short inputs (16bit) into q15 (that is a different encoding scheme).
And vice versa..
Umm

More processing. Thats a shame. It takes processor cycles

User avatar
Pito
Posts: 1529
Joined: Sat Mar 26, 2016 3:26 pm
Location: Rapa Nui

Re: Voice changer

Post by Pito » Mon Jun 20, 2016 10:02 am

Not really, I guess. The fft works faster with q15 (??), as it must not mess with saturation (q15 is fractional -1.000 to +0.999 afaik).
There are routines in cmsis from float to q15/31/7 and vice versa, but not from integer - it must not be difficult to convert, though..
There is a type q15_t (q31_t, q7_t) available, I think.
Ie. for 12bit ADC:

Code: Select all

Q15_data = (q15_t) ((12bit_ADC_data) << 4);
or something like that.. Not sure whether to subtract an offset before the shift, in order to create a signed value.. Like

Code: Select all

Q15_data = (q15_t) ((12bit_ADC_data - offset) << 4);
Needs to be double-checked whether that works.. No warranties of any kind :D
Pukao Hats Cleaning Services Ltd.

User avatar
RogerClark
Posts: 6911
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Voice changer

Post by RogerClark » Mon Jun 20, 2016 10:46 am

OK

I think I have other problems with the ARM code, not just the data format, unless the FFT can't handle blank data (all zeros)

But, I've had to put this project idea on hold, as I have got some other things I need to get working.

melodic
Posts: 8
Joined: Mon Oct 05, 2015 5:17 pm

Re: Voice changer

Post by melodic » Tue Oct 18, 2016 4:29 pm

For sound processing on stm32 i`m using Arduino FFT library.
fft.h

Code: Select all

#ifndef FIXFFT_H
#define FIXFFT_H

#include <WProgram.h>




/*
 fix_fft() - perform forward/inverse fast Fourier transform.
 fr[n],fi[n] are real and imaginary arrays, both INPUT AND
 RESULT (in-place FFT), with 0 <= n < 2**m; set inverse to
 0 for forward transform (FFT), or 1 for iFFT.
*/
int fix_fft(int fr[], int fi[], int m, int inverse);



/*
 fix_fftr() - forward/inverse FFT on array of real numbers.
 Real FFT/iFFT using half-size complex FFT by distributing
 even/odd samples into real/imaginary arrays respectively.
 In order to save data space (i.e. to avoid two arrays, one
 for real, one for imaginary samples), we proceed in the
 following two steps: a) samples are rearranged in the real
 array so that all even samples are in places 0-(N/2-1) and
 all imaginary samples in places (N/2)-(N-1), and b) fix_fft
 is called with fr and fi pointing to index 0 and index N/2
 respectively in the original array. The above guarantees
 that fix_fft "sees" consecutive real samples as alternating
 real and imaginary samples in the complex array.
*/
int fix_fftr(int f[], int m, int inverse);




#endif
 
fft.cpp

Code: Select all

#include <avr/pgmspace.h>
#include "fft.h"
#include <WProgram.h>

/* fix_fft.c - Fixed-point in-place Fast Fourier Transform  */
/*
 All data are fixed-point short integers, in which -32768
 to +32768 represent -1.0 to +1.0 respectively. Integer
 arithmetic is used for speed, instead of the more natural
 floating-point.

 For the forward FFT (time -> freq), fixed scaling is
 performed to prevent arithmetic overflow, and to map a 0dB
 sine/cosine wave (i.e. amplitude = 32767) to two -6dB freq
 coefficients. The return value is always 0.

 For the inverse FFT (freq -> time), fixed scaling cannot be
 done, as two 0dB coefficients would sum to a peak amplitude
 of 64K, overflowing the 32k range of the fixed-point integers.
 Thus, the fix_fft() routine performs variable scaling, and
 returns a value which is the number of bits LEFT by which
 the output must be shifted to get the actual amplitude
 (i.e. if fix_fft() returns 3, each value of fr[] and fi[]
 must be multiplied by 8 (2**3) for proper scaling.
 Clearly, this cannot be done within fixed-point short
 integers. In practice, if the result is to be used as a
 filter, the scale_shift can usually be ignored, as the
 result will be approximately correctly normalized as is.

 Written by:  Tom Roberts  11/8/89
 Made portable:  Malcolm Slaney 12/15/94 malcolm@interval.com
 Enhanced:  Dimitrios P. Bouras  14 Jun 2006 dbouras@ieee.org
 Modified for 8bit values David Keller  10.10.2010
*/


#define N_WAVE      256    /* full length of Sinewave[] */
#define LOG2_N_WAVE 8      /* log2(N_WAVE) */




/*
 Since we only use 3/4 of N_WAVE, we define only
 this many samples, in order to conserve data space.
*/



const int Sinewave[N_WAVE-N_WAVE/4]  = {
0, 3, 6, 9, 12, 15, 18, 21, 
24, 28, 31, 34, 37, 40, 43, 46, 
48, 51, 54, 57, 60, 63, 65, 68, 
71, 73, 76, 78, 81, 83, 85, 88, 
90, 92, 94, 96, 98, 100, 102, 104, 
106, 108, 109, 111, 112, 114, 115, 117, 
118, 119, 120, 121, 122, 123, 124, 124, 
125, 126, 126, 127, 127, 127, 127, 127, 

127, 127, 127, 127, 127, 127, 126, 126, 
125, 124, 124, 123, 122, 121, 120, 119, 
118, 117, 115, 114, 112, 111, 109, 108, 
106, 104, 102, 100, 98, 96, 94, 92, 
90, 88, 85, 83, 81, 78, 76, 73, 
71, 68, 65, 63, 60, 57, 54, 51, 
48, 46, 43, 40, 37, 34, 31, 28, 
24, 21, 18, 15, 12, 9, 6, 3, 

0, -3, -6, -9, -12, -15, -18, -21, 
-24, -28, -31, -34, -37, -40, -43, -46, 
-48, -51, -54, -57, -60, -63, -65, -68, 
-71, -73, -76, -78, -81, -83, -85, -88, 
-90, -92, -94, -96, -98, -100, -102, -104, 
-106, -108, -109, -111, -112, -114, -115, -117, 
-118, -119, -120, -121, -122, -123, -124, -124, 
-125, -126, -126, -127, -127, -127, -127, -127, 

/*-127, -127, -127, -127, -127, -127, -126, -126, 
-125, -124, -124, -123, -122, -121, -120, -119, 
-118, -117, -115, -114, -112, -111, -109, -108, 
-106, -104, -102, -100, -98, -96, -94, -92, 
-90, -88, -85, -83, -81, -78, -76, -73, 
-71, -68, -65, -63, -60, -57, -54, -51, 
-48, -46, -43, -40, -37, -34, -31, -28, 
-24, -21, -18, -15, -12, -9, -6, -3, */
};






/*
 FIX_MPY() - fixed-point multiplication & scaling.
 Substitute inline assembly for hardware-specific
 optimization suited to a particluar DSP processor.
 Scaling ensures that result remains 16-bit.
*/
inline int FIX_MPY(int a, int b)
{
 
 //Serial.println(a);
//Serial.println(b);
 
 
   /* shift right one less bit (i.e. 15-1) */
   int c = ((int)a * (int)b) >> 6;
   /* last bit shifted out = rounding-bit */
   b = c & 0x01;
   /* last shift + rounding bit */
   a = (c >> 1) + b;

       /*
       Serial.println(Sinewave[3]);
       Serial.println(c);
       Serial.println(a);
       while(1);*/

   return a;
}

/*
 fix_fft() - perform forward/inverse fast Fourier transform.
 fr[n],fi[n] are real and imaginary arrays, both INPUT AND
 RESULT (in-place FFT), with 0 <= n < 2**m; set inverse to
 0 for forward transform (FFT), or 1 for iFFT.
*/
int fix_fft(int fr[], int fi[], int m, int inverse)
{
   int mr, nn, i, j, l, k, istep, n, scale, shift;
   int qr, qi, tr, ti, wr, wi;

   n = 1 << m;

   /* max FFT size = N_WAVE */
   if (n > N_WAVE)
       return -1;

   mr = 0;
   nn = n - 1;
   scale = 0;

   /* decimation in time - re-order data */
   for (m=1; m<=nn; ++m) {
       l = n;
       do {
           l >>= 1;
       } while (mr+l > nn);
       mr = (mr & (l-1)) + l;

       if (mr <= m)
           continue;
       tr = fr[m];
       fr[m] = fr[mr];
       fr[mr] = tr;
       ti = fi[m];
       fi[m] = fi[mr];
       fi[mr] = ti;
   }

   l = 1;
   k = LOG2_N_WAVE-1;
   while (l < n) {
       if (inverse) {
           /* variable scaling, depending upon data */
           shift = 0;
           for (i=0; i<n; ++i) {
               j = fr[i];
               if (j < 0)
                   j = -j;
               m = fi[i];
               if (m < 0)
                   m = -m;
               if (j > 16383 || m > 16383) {
                   shift = 1;
                   break;
               }
           }
           if (shift)
               ++scale;
       } else {
           /*
             fixed scaling, for proper normalization --
             there will be log2(n) passes, so this results
             in an overall factor of 1/n, distributed to
             maximize arithmetic accuracy.
           */
           shift = 1;
       }
       /*
         it may not be obvious, but the shift will be
         performed on each data point exactly once,
         during this pass.
       */
       istep = l << 1;
       for (m=0; m<l; ++m) {
           j = m << k;
           /* 0 <= j < N_WAVE/2 */
           wr =  Sinewave [j+N_WAVE/4];

/*Serial.println("asdfasdf");
Serial.println(wr);
Serial.println(j+N_WAVE/4);
Serial.println(Sinewave[256]);

Serial.println("");*/


           wi = -Sinewave[j];
           if (inverse)
               wi = -wi;
           if (shift) {
               wr >>= 1;
               wi >>= 1;
           }
           for (i=m; i<n; i+=istep) {
               j = i + l;
               tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]);
               ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]);
               qr = fr[i];
               qi = fi[i];
               if (shift) {
                   qr >>= 1;
                   qi >>= 1;
               }
               fr[j] = qr - tr;
               fi[j] = qi - ti;
               fr[i] = qr + tr;
               fi[i] = qi + ti;
           }
       }
       --k;
       l = istep;
   }
   return scale;
}

/*
 fix_fftr() - forward/inverse FFT on array of real numbers.
 Real FFT/iFFT using half-size complex FFT by distributing
 even/odd samples into real/imaginary arrays respectively.
 In order to save data space (i.e. to avoid two arrays, one
 for real, one for imaginary samples), we proceed in the
 following two steps: a) samples are rearranged in the real
 array so that all even samples are in places 0-(N/2-1) and
 all imaginary samples in places (N/2)-(N-1), and b) fix_fft
 is called with fr and fi pointing to index 0 and index N/2
 respectively in the original array. The above guarantees
 that fix_fft "sees" consecutive real samples as alternating
 real and imaginary samples in the complex array.
*/
int fix_fftr(int f[], int m, int inverse)
{
   int i, N = 1<<(m-1), scale = 0;
   int tt, *fr=f, *fi=&f[N];

   if (inverse)
       scale = fix_fft(fi, fr, m-1, inverse);
   for (i=1; i<N; i+=2) {
       tt = f[N+i-1];
       f[N+i-1] = f[i];
       f[i] = tt;
   }
   if (! inverse)
       scale = fix_fft(fi, fr, m-1, inverse);
   return scale;
}
This is very fast implementation.

alexandros
Posts: 19
Joined: Mon Oct 02, 2017 6:51 pm

Re: Voice changer

Post by alexandros » Mon Oct 02, 2017 9:47 pm

Sorry just a stupid question,
how do you read the second pinMapADCin2 values? any example?, seems that you working only with pinMapADCin.


racemaniac wrote:
Fri Jun 10, 2016 6:09 pm
As i said in the 10$-o-scope project, i borrowed some of its code to do a fun little project

I read you can change the pitch of a voice by sampling sound into a circular buffer, and then reading from that buffer at a different rate, so no need for fourier transformations and difficult mathematics.
So i borrowed the DMA ADC code from the scope project (altered it a bit for this purpose), and connected a MP4725 DAC to my maple mini, and had some fun testing this little trick :)

How to wire it (on a maple mini):
I2C port 1 gets a mp4725 DAC
pin 3: analog input for the microphone
pin 4: analog input for a potentiometer that will alter the pitch

How it works:
I modified the dma code of this project to
- sample slower (about 35 khz)
- sample 2 pins simultaneously iso 1 pin interleaved
- run in circular mode
- only put the data of the sound sample pin into the memory (so i'm sampling 16 bits via dma, even though the 2 channels together are writing all 32 bits of the data register). I read the other pin directly from the dataregister when i need it.

The playback
- uses i2c clockstretching for a variable sampling rate
- tries to smooth out clicks (experiment with it, seems to work fairly well)

feel free to experiment with this fun little project :)
And let me know if you find better settings for buffer size & click removal

First thing that can be tried is a higher sampling rate, you should be able to push the DAC to 60khz with this code, and the sampling rate for the microphone can naturally also go a lot faster :).
Also better would be using a timer to send the samples :), but i just quickly wrote it with a delay :).

Also fun to try, program some special effects on it:
- maybe some echo?
- i read a dalek vocie is made by multiplying a sound signal by a sine wave (i think 100hz or something like that)
- feel free to add ideas

Code: Select all

#include <dma_private.h>
#include <libmaple/adc.h>
#include <wirish.h>
#include <libmaple/i2c.h>

#define ADC_CR1_SIMULTANEOUS 0x60000 // simultaneous mode DUAL MODE bits 19-16
#define BufferSize        2048
uint16_t bufferPos = 0;
const int8_t analogInPin = PB0;
const int8_t analogInPin2 = PA7;

uint16_t dataBuffer[BufferSize];

const uint16_t clickTreshold = 100;
uint16_t lastSample = 2048;

uint16_t* analog2 = (uint16_t*)&(ADC1->regs->DR) + 1;

int modulationDelay = 40;
int wait = 0;

void i2c_str_InitPort(i2c_dev *device, uint8_t divider);
void i2c_str_StartSending(i2c_dev *device, uint8_t address);
void i2c_str_sendBytes(i2c_dev *device, uint8_t data, uint8_t data2);
void i2c_str_ReleasePort(i2c_dev *device);
void i2c_str_StopSending(i2c_dev *device);

#define I2C_STRETCH_SB_TIMEOUT 10000 //how many times we will check the status registers to see if the device is starting

#define I2C_STRETCH_DIV_400KHZ 30
#define I2C_STRETCH_DIV_600KHZ 20
#define I2C_STRETCH_DIV_800KHZ 15
#define I2C_STRETCH_DIV_1200KHZ 10

void setup()
{
  Serial.begin();
  pinMode(4, INPUT_ANALOG);
  setADC();
  startSampling ();
  i2c_str_InitPort(I2C1, I2C_STRETCH_DIV_600KHZ);
  i2c_str_StartSending(I2C1, 0x60);
}

void loop() 
{
  uint16_t sample = dataBuffer[bufferPos];
  if((uint16_t)(sample - lastSample) > clickTreshold && (uint16_t)(lastSample - sample) > clickTreshold)
  {
    if(sample > lastSample)
      sample = lastSample + clickTreshold;
    else
      sample = lastSample - clickTreshold;
  }
  i2c_str_sendBytes(I2C1, (uint8_t)(sample>>8), sample & 255);
  bufferPos = (++bufferPos)%BufferSize;
  lastSample = sample;
  delayMicroseconds(20 + (*analog2>>7));
}

void startSampling ()
{
  dma_init(DMA1);

  adc_dma_enable(ADC1);
  dma_setup_transfer(DMA1, DMA_CH1, &ADC1->regs->DR, DMA_SIZE_16BITS,
                     dataBuffer, DMA_SIZE_16BITS, (DMA_MINC_MODE | DMA_CIRC_MODE));// Receive buffer DMA
  dma_set_num_transfers(DMA1, DMA_CH1, BufferSize);
  dma_enable(DMA1, DMA_CH1);
}

void setADC ()
{
  //  const adc_dev *dev = PIN_MAP[analogInPin].adc_device;
  int pinMapADCin = PIN_MAP[analogInPin].adc_channel;
  int pinMapADCin2 = PIN_MAP[analogInPin2].adc_channel;
  adc_set_prescaler(ADC_PRE_PCLK2_DIV_8);
  adc_set_sample_rate(ADC1, ADC_SMPR_239_5);
  adc_set_sample_rate(ADC2, ADC_SMPR_239_5);

  //  adc_reg_map *regs = dev->regs;
  adc_set_reg_seqlen(ADC1, 1);
  ADC1->regs->SQR3 = pinMapADCin;
  ADC1->regs->CR2 |= ADC_CR2_CONT; // | ADC_CR2_DMA; // Set continuous mode and DMA
  ADC1->regs->CR1 |= ADC_CR1_SIMULTANEOUS;
  ADC1->regs->CR2 |= ADC_CR2_SWSTART;

  ADC2->regs->CR2 |= ADC_CR2_CONT; // ADC 2 continuos
  ADC2->regs->SQR3 = pinMapADCin2;
}

void adc_dma_enable(const adc_dev * dev) {
  bb_peri_set_bit(&dev->regs->CR2, ADC_CR2_DMA_BIT, 1);
}

void adc_dma_disable(const adc_dev * dev) {
  bb_peri_set_bit(&dev->regs->CR2, ADC_CR2_DMA_BIT, 0);
}

static void (*i2c_str_old_error_handler)(i2c_dev *dev);

void i2c_str_InitPort(i2c_dev *device, uint8_t divider)
{
  i2c_peripheral_disable(device);
  i2c_bus_reset(device);
  i2c_init(device);//initialize it
  i2c_config_gpios(device);//configure the gpios
  device->regs->CR2 = I2C_CR2_ITERREN | 36; //dma enabled, peripheral frequency is 36Mhz
  device->regs->CCR = I2C_CCR_FS | divider; //default 30
  device->regs->TRISE = 11;
}
void i2c_str_ReleasePort(i2c_dev *device)
{
  i2c_str_StopSending(device);
}

void i2c_str_StartSending(i2c_dev *device, uint8_t address)
{
  i2c_peripheral_enable(device);//enable the port
  i2c_start_condition(device);//set the start condition

  uint32_t sr1 = device->regs->SR1;
  uint32_t sr2 = device->regs->SR2;
  uint16_t wait = 0;
  while(!(sr1&I2C_SR1_SB))
  {
    if(wait++ > I2C_STRETCH_SB_TIMEOUT)
    {
      i2c_peripheral_disable(device);
      return;
    }
    sr1 = device->regs->SR1;
    sr2 = device->regs->SR2;
  }
  i2c_write(device, address<<1);//write the address of the device you want to contact (shifted 1 to the left)
}
void i2c_str_StopSending(i2c_dev *device)
{
  i2c_stop_condition(device);
  i2c_peripheral_disable(device);
}

bool i2c_str_IsSending(i2c_dev *device)
{
  return !(device->regs->SR1 & I2C_SR1_TXE);
}

bool i2c_str_IsError(i2c_dev *device)
{
  return device->regs->SR1 & (I2C_SR1_BERR | I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_PECERR | I2C_SR1_TIMEOUT);
}

bool i2c_str_PortEnabled(i2c_dev *device)
{
  return device->regs->CR1 & I2C_CR1_PE;
}

void i2c_str_sendByte(i2c_dev *device, uint8_t data)
{
  uint32_t sr1 = device->regs->SR1;
  uint32_t sr2 = device->regs->SR2;
  if(sr1&(I2C_SR1_BTF | I2C_SR1_TXE | I2C_SR1_ADDR))
  {
    i2c_write(device, data);
  }
}

void i2c_str_sendBytes(i2c_dev *device, uint8_t data, uint8_t data2)
{
  uint32_t sr1 = device->regs->SR1;
  uint32_t sr2 = device->regs->SR2;
  if(sr1&(I2C_SR1_BTF | I2C_SR1_ADDR))
  {
    i2c_write(device, data);
    i2c_write(device, data2);
  }
}

alexandros
Posts: 19
Joined: Mon Oct 02, 2017 6:51 pm

Re: Voice changer

Post by alexandros » Mon Oct 02, 2017 10:34 pm

basically hw can i save the pinMapADCin2 values into a second dataBuffer?

racemaniac
Posts: 520
Joined: Sat Nov 07, 2015 9:09 am

Re: Voice changer

Post by racemaniac » Tue Oct 03, 2017 5:50 am

alexandros wrote:
Mon Oct 02, 2017 10:34 pm
basically hw can i save the pinMapADCin2 values into a second dataBuffer?
for reading both adc's, better have a look at the 10$ o scope project on this forum. There they work with sampling both channels as fast as they can.

Post Reply