1
votes

Is it possible to phase shift audio in real time on the iPhone?

I have a simple application set up that outputs the audio from the microphone in real time. What I am trying to do is process this audio i.e. Do a phase shift.

Am I correct the only way to proceed is to take my sample space, do a FFT, phase shift, and then inverse fft?

I am aware of the vDSP library but it seems like too much overhead for a simple task.

UPDATE: I have a grounding in DSP from Elec Eng, and yes I really do want to do a phase shift. I do not need to do a freq shift or filter, they are seperate process's I will later implement.

5
You mean frequency shift? Phase shift is pointless and trival. That would just be a time delayed version of the original.TJD
Yes, I do want to phase shift alright.Colin
If all you want to do is perform a phase shift, you don't need any DSP at all, because that's just a time delay on input. Just read in audio data and output it -- that'll introduce a phase shift for you.user149341

5 Answers

3
votes

Yep, that's a way to do it.

This sample C code shows it:

#include <stdio.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358979324
#endif

typedef struct
{
  double x, y;
} tComplex;

tComplex complexAdd(const tComplex* a, const tComplex* b)
{
  tComplex c;
  c.x = a->x + b->x;
  c.y = a->y + b->y;
  return c;
}

tComplex complexMul(const tComplex* a, const tComplex* b)
{
  tComplex c;
  c.x = a->x * b->x - a->y * b->y;
  c.y = a->x * b->y + a->y * b->x;
  return c;
}

void dft(tComplex out[], const tComplex in[], size_t n, int direction)
{
  size_t k, i;
  for (k = 0; k < n; k++)
  {
    tComplex r = { 0, 0 }, e;
    for (i = 0; i < n; i++)
    {
      e.x = cos(-2 * direction * M_PI / n * ((double)k - n / 2) * ((double)i - n / 2));
      e.y = sin(-2 * direction * M_PI / n * ((double)k - n / 2) * ((double)i - n / 2));
      e = complexMul(&e, &in[i]);
      r = complexAdd(&r, &e);
    }
    out[k] = r;
  }
}

double maxAbs(const tComplex in[], size_t n)
{
  double m = 0;
  while (n--)
  {
    double a = hypot(in->x, in->y);
    if (m < a) m = a;
    in++;
  }
  return m;
}

#define SAMPLE_CNT   32
#define SAMPLE_SHIFT 3

int main(void)
{
  tComplex signalIn[SAMPLE_CNT];
  tComplex signalOut[SAMPLE_CNT];
  tComplex tmp[SAMPLE_CNT];
  int i;

  // signalIn[] = square pulse
  for (i = 0; i < SAMPLE_CNT; i++)
  {
    signalIn[i].x = ((i - SAMPLE_CNT / 2) >= 0) * ((i - SAMPLE_CNT / 2) < SAMPLE_CNT / 4);
    signalIn[i].y = 0;
  }

  // tmp[] = DFT{signalIn[]}
  dft(tmp, signalIn, SAMPLE_CNT, 1);

  // tmp[] = DFT{signalIn[]} * exp(j * 2 * PI * f * TimeShift)
  for (i = 0; i < SAMPLE_CNT; i++)
  {
    tComplex e;
    e.x =  cos(2 * M_PI * (i - SAMPLE_CNT / 2) * SAMPLE_SHIFT / SAMPLE_CNT);
    e.y = -sin(2 * M_PI * (i - SAMPLE_CNT / 2) * SAMPLE_SHIFT / SAMPLE_CNT);
    tmp[i] = complexMul(&tmp[i], &e);
  }

  // signalOut[] = IDFT{tmp[]}
  dft(signalOut, tmp, SAMPLE_CNT, -1);

  printf("     Re{In[]}    .     Im{In[]}    |"
         "   |DFT{In[]}|   |"
         "    Re{Out[]}    .    Im{Out[]}\n");

  for (i = 0; i < SAMPLE_CNT; i++)
  {
    int j, s;

    s = signalIn[i].x / maxAbs(signalIn, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);
    printf(".");
    s = signalIn[i].y / maxAbs(signalIn, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);

    printf("|");

    s = hypot(tmp[i].x, tmp[i].y) / maxAbs(tmp, SAMPLE_CNT) * 8;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);

    printf("|");

    s = signalOut[i].x / maxAbs(signalOut, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);
    printf(".");
    s = signalOut[i].y / maxAbs(signalOut, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);

    printf("\n");
  }

  return 0;
}

Output:

     Re{In[]}    .     Im{In[]}    |   |DFT{In[]}|   |    Re{Out[]}    .    Im{Out[]}
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |          *      |        *        .        *        
        *        .        *        |             *   |        *        .        *        
        *        .        *        |               * |        *        .        *        
                *.        *        |                *|        *        .        *        
                *.        *        |               * |        *        .        *        
                *.        *        |             *   |        *        .        *        
                *.        *        |          *      |                *.        *        
                *.        *        |        *        |                *.        *        
                *.        *        |         *       |                *.        *        
                *.        *        |         *       |                *.        *        
                *.        *        |         *       |                *.        *        
        *        .        *        |        *        |                *.        *        
        *        .        *        |        *        |                *.        *        
        *        .        *        |         *       |                *.        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        

You can see that the output is a replica of the input shifted by SAMPLE_SHIFT samples (3, here) in time.

You can change SAMPLE_SHIFT to a non-integer value too, getting a shift by a fractional number of samples.

Output for SAMPLE_SHIFT = 2.5:

     Re{In[]}    .     Im{In[]}    |   |DFT{In[]}|   |    Re{Out[]}    .    Im{Out[]}
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |          *      |        *        .        *        
        *        .        *        |             *   |        *        .        *        
        *        .        *        |               * |        *        .        *        
                *.        *        |                *|        *        .        *        
                *.        *        |               * |        *        .        *        
                *.        *        |             *   |           *     .        *        
                *.        *        |          *      |                *.        *        
                *.        *        |        *        |              *  .        *        
                *.        *        |         *       |               * .        *        
                *.        *        |         *       |              *  .        *        
                *.        *        |         *       |               * .        *        
        *        .        *        |        *        |              *  .        *        
        *        .        *        |        *        |                *.        *        
        *        .        *        |         *       |           *     .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
0
votes

What you want to do should correspond to output is a folding integral of the input with a kernel. It can be found as the output of a impulse input. If you can find it, do the folding integral using a proper numeric integration method. This amounts that one fft:s need to be done only once, The remaining should be done continuously to give the output continuously.

0
votes

Is it possible to phase shift audio in real time on the iPhone?

Well, yes, but that's probably not what you actually want to do. (see TJD's comment)

I have a simple application set up that outputs the audio from the microphone in real time. What I am trying to do is process this audio i.e. Do a phase shift.

Processing audio is not synonymous with doing a phase shift. Perhaps you want to "filter"?

Am I correct the only way to proceed is to take my sample space, do a FFT, phase shift, and then inverse fft?

Assuming what you want to do is filter the audio, then no, I don't recommend the FFT. You will struggle with many issues such as windowing, overlap/add, and performance. FFT is not a good way to design filters.

I am aware of the vDSP library but it seems like too much overhead for a simple task.

I have never used vDSP, but from quick inspection it does not look like the right solution for filtering. The right solution is more likely to use a simple biquad function. You can read about them here:

http://en.wikipedia.org/wiki/Digital_biquad_filter

and there's a nice "cookbook" for them here:

http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt

0
votes

It should be possible to do a variety of audio processing in real-time on iOS devices. There is sufficient processing power.

Perhaps you could describe in more detail what sort of phase shift you want to do. It is not strictly true that the “only” way to proceed is to use an FFT, but it may be the best way, at least for performance and battery life.

When you say vDSP seems to have too much overhead, are you referring to processor performance or to engineering effort? I agree, vDSP has some undesired engineering effort, due primarily to legacy interfaces that require real data (e.g., input signals) to be rearranged into even-odd splits. However, other aspects, such as the setup of FFT data, are necessary; they are compelled by the nature of the FFT.

Computing performance should be very good with vDSP routines. If you encounter difficulties, with either processor performance or engineering effort, please report bugs (or feature requests).

0
votes

I agree with the others that a (real time forward) phase shift does not require a DSP as it is simply a time shift of the audio samples. Of course a real time backward phase shift would require more effort than a DSP as it would require time travel.

On the other hand, I am in need of a simple an quick way to perform a fixed frequency shift of an iphone microphone audio sample. Actually several frequency shifts at, for example 1 Hz increments or there abouts. Anyone know a quick and easy way of performing that task on the iPhone?