3
$\begingroup$

Right now I simply do this (pseudocode)

for freq in freqs:
    for i in len(samples):
        samples[i] += sin(...)

Is there a better way? This is for outputting to a 16-bit WAV file, using C on an ARM7 device. The frequencies are actually the centers of FFT bins for a particular bin size.

$\endgroup$
  • 2
    $\begingroup$ depending on what you're doing, inverse FFT can be a much faster way to reproduce the same signal. it's best for periodic signals that are an exact fit for the sampling rate, though. I would guess that the IFFT has less numerical error, too, but I'm not sure $\endgroup$ – endolith May 23 '13 at 16:20
  • 2
    $\begingroup$ Are the frequencies arbitrary, or is there some known relationship among them ahead of time? Also, if you're concerned about maximizing performance, then details of the implementation (like platform, programming language, and the such) are important. Your sample suggests that maybe you're using almost pure Python, which wouldn't be a good choice for maximum throughput. $\endgroup$ – Jason R May 23 '13 at 16:55
  • $\begingroup$ Sure @JasonR I'll clarify: I'm using C on an ARM7 device, and the frequencies are the centers of various FFT bins. The frequencies don't change, though the amplitudes do. $\endgroup$ – Keith May 23 '13 at 18:15
4
$\begingroup$

The most computationally efficient approach is to set up the data you want in the frequency domain by putting in the tones as "spikes" in the appropriate frequency bins, and then inverse FFT'ing it to get the time domain tones. This approach will likely be a couple of orders of magnitude or so faster than your approach.

There are some downsides to doing it this way. For tones whose frequencies are integer multiple of the sample frequency divided by the number of samples it is easy- just put a spike in at the appropriate frequency bin. For all other frequencies, though, you need to put some energy into the neighboring bins as well. I would have to think about how exactly to figure out how to do this. I'm sure it can be done, but it's not a no-brainer.

Also, if you need to just produce a block of samples with the tones this approach is ideal. If you need to create a continuous stream, though, care would have to be taken to get the phases right from block to block. Again, not a no-brainer.

All that being said, I am pretty sure that this is the most computationally efficient approach.

$\endgroup$
1
$\begingroup$

Additive synthesis is always going to be fairly heavy on the CPU. If you are not already I would look at using a Wavetable Oscillator to generate your sin waves. It may also be worth unrolling your loop (be sure to profile so you can see if it makes a difference).

You really need to profile your code and identify the bottlenecks unique to your program.

Mike

$\endgroup$
1
$\begingroup$

There are two things that you can do to speed things up:

  1. Do frame based processing, i.e. compute 1000 or so samples at a time before writing them to the wavefile
  2. Minimize calls to transcendental functions (such as sine and cosine)

The second answer in this question How to create a sine wave generator that can smoothly transition between frequencies shows an algorithm that uses transcendental functions only during initialization and uses just 4 multiplies per sample and sine wave.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.