0

I have an RtAudio callback function that fills a buffer with samples of a sine wave of a given frequency and amplitude and calculates the phase at which the sine wave written to the next buffer should begin. However, when these buffers are played, the sound generated is clearly not a sine wave.

I have printed the sample values filling the buffers and the phase between consecutive buffers is correct - there is no mismatch between consecutive buffers. I have timed the callback function and it takes less than a microsecond to fill the buffer. I am using a buffer size of 256 and a sample rate of 44110 so the time the buffer plays for is in the milliseconds.

Note that when I increase the buffer length to a large size so that playback is one the order of seconds, it sounds like a sine wave as intended. However, the latency is of course unacceptable for these buffer sizes.

Why is this RtAudio callback function generating a distorted sine wave? What is it I am not doing or what is it I am doing wrong?

int build_sound(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
               double streamTime, RtAudioStreamStatus status, void *userData) {
    
    double *buffer = (double *) outputBuffer;
    std::fill_n(buffer, nBufferFrames, 0.0);
    
    std::vector<pressedKey*> *data = static_cast<std::vector<pressedKey*>*>(userData);
    if (!data->empty()) { 
        for (auto it = data->begin(); it != data->end(); ) {
            pressedKey* trigger = *it;
            if (trigger->bufferphase.size()!=trigger->amps.size()) {
                trigger->bufferphase.resize(trigger->amps.size());
                for (int i=0; i<trigger->amps.size(); i++) {
                    trigger->bufferphase[i]=0;
                }
            }
            for (int i=0; i<trigger->amps.size(); i++) {
                for (int j=0; j<nBufferFrames; j=j+2)  {
                    double jdbl=static_cast<double>(j);
                    buffer[j]=buffer[j]+trigger->amps[i]*0.05*sin(2*PI*
                                                  trigger->freqs[i]*jdbl/SAMPLERATE
                                                  +trigger->bufferphase[i]);
                    buffer[j+1]=buffer[j+1]+trigger->amps[i]*0.05*sin(2*PI*
                                                    trigger->freqs[i]*jdbl/SAMPLERATE
                                                    +trigger->bufferphase[i]);
                }
                trigger->bufferphase[i]+=2*PI*(trigger->freqs[i]
                                              *(nBufferFrames-1)/SAMPLERATE); 
                if (i==0){
                    for (int j=0; j<nBufferFrames; j=j+2)  {
                        std::cout<<j<<" "<<buffer[j]<<std::endl;
                    }
                }  
            }
        ++it;       
        }
    }

    return 0;               
}

EDIT: Added output per Ted Lyngmo's suggestion. This clarifies the source of the problem: a buffer-sized gap in between buffers. I guess when interleaving is on RtAudio makes the buffer size nBufferSamples*2

rtaudio problem oscillogram

  • This is one of the few occations where I'd invite images. If you show an image of the undistorted sound and then one what it comes out as, it may be possible for someone spending a lot of time mixing sounds or programming software that mixes sounds to recognize what type of distortion it is by just looking at the images. – Ted Lyngmo Jun 24 '23 at 17:32
  • 1
    thats a good idea I will hook up the oscilloscope to the headphone jack. – Malcolm Regan Jun 24 '23 at 17:38
  • 1
    It may be easier to route the audio out to an in-port internally though. That's to record what you hear. If that fails, just short audio-out to audio-in physically with a short cable. Set audio-out to line levels if that's not detected automatically – Ted Lyngmo Jun 24 '23 at 17:44
  • I don't know what an in-port is. Oscilloscope and everything was set up right here so i just did it. Its a noisy capture but I think its sufficient to illuminate to problem. Thank you for the suggestion. I will read up on what you've described for future use. I will edit the question with the oscillogram. – Malcolm Regan Jun 24 '23 at 18:06
  • 1
    Damn i see. When interleaving is on the RtAudio makes the buffer size nBufferSamples*2 – Malcolm Regan Jun 24 '23 at 18:16
  • Nice, yes that seems to me that every other buffer is sent zeroinitialized to the driver that you also mentioned. You could fill all buffers with a Nyquist sine to verify. Non-overwritten buffers would show up as a filled block where the empties now are. – Ted Lyngmo Jun 24 '23 at 18:19
  • Nice, you found it! Great! – Ted Lyngmo Jun 24 '23 at 18:19

0 Answers0