在PortAudio回调中总结波

时间:2016-06-16 00:16:18

标签: c++ audio signal-processing portaudio

我正在使用PortAudio开发声音应用程序。我有一个结构数组,表示传递给主回调函数的各个振荡器的数据。

当我尝试在一行中将两个波相加并播放时,它可以正常工作,但是当我尝试使用for循环时,我可以任意求和振荡器,但事实并非如此。

这样可行:

*out = sinetable[(int)oscs[0].phase % TABLE_SIZE] * (oscs[0].amp / 2) + sinetable[(int)oscs[1].phase % TABLE_SIZE] * ( oscs[1].amp / 2);

但这不是:

for(int j = 0; j < 2; j++ )
   {     
     *out += sinetable[(int)oscs[j].phase % TABLE_SIZE] * (oscs[j].amp / 2);                      
    }

我只需要一种方法来对任意数量的振荡器求和。我认为这两种方法相当,但显然它们不是。

非常感谢任何帮助。

编辑: 完整的代码在这里:

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

         #define NUM_SECONDS   (2)
         #define SAMPLE_RATE   (44100)
         #define FRAMES_PER_BUFFER  (0) // PA will select in it's own..

         #ifndef M_PI
         #define M_PI  (3.14159265)
         #endif

         #define TABLE_SIZE   (4096)  // < Hz / (sampling rate / table size ) [line 53]]
         float sinetable[TABLE_SIZE];

         typedef struct
         {
             float phase = 0;
             float freq = 300;
             float amp = 0.5;
         }
         Osc;

         void initTable(){
             for(int i = 0; i < TABLE_SIZE; i++){

                 sinetable[i] = sin(2 * M_PI * i/TABLE_SIZE);
             }
         }

         /* This routine will be called by the PortAudio engine when audio is needed.
         ** It may called at interrupt level on some machines so don't do anything
         ** that could mess up the system like calling malloc() or free().
         */
         static int patestCallback( const void *inputBuffer, void *outputBuffer,
                                     unsigned long framesPerBuffer,
                                     const PaStreamCallbackTimeInfo* timeInfo,
                                     PaStreamCallbackFlags statusFlags,
                                     void *userData )
         {
             Osc *oscs = (Osc*)userData;
             float *out = (float*)outputBuffer;
             unsigned long i;

             (void) timeInfo; /* Prevent unused variable warnings. */
             (void) statusFlags;
             (void) inputBuffer;


             for( i=0; i<framesPerBuffer; i++ )
             {

                /*
                for(int j = 0; j < 2; j++ ){      //this won't work...
                 *out += sinetable[(int)oscs[j].phase % TABLE_SIZE] * (oscs[j].amp / 2);                      
                }
                 */
                 // but this does:
                *out = sinetable[(int)oscs[0].phase % TABLE_SIZE] * (oscs[0].amp / 2) +  sinetable[(int)oscs[1].phase % TABLE_SIZE] * ( oscs[1].amp / 2);        

                oscs[0].phase += oscs[0].freq / ( SAMPLE_RATE/(float)TABLE_SIZE );        
                oscs[1].phase += oscs[1].freq / ( SAMPLE_RATE/(float)TABLE_SIZE );   

                *out++;
             }

             return paContinue;
         }



         /*
          * This routine is called by portaudio when playback is done.
          */
         static void StreamFinished( void* userData )
         {
            Osc *oscs = (Osc *) userData;
          //  printf( "Stream Completed: %s\n", oscs->message );
         }

         /*******************************************************************/
         int main(void);
         int main(void)
         {
             PaStreamParameters outputParameters;
             PaStream *stream;
             PaError err;
             Osc oscs[5];
             int i;
             oscs[1].freq = 400; oscs[2].freq = 500; oscs[3].freq = 600; oscs[4].freq = 700;

             printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);

             /* initialise sinusoidal wavetable */
             for( i=0; i<TABLE_SIZE; i++ )
             {
                sinetable[i] = 2* M_PI *  (i / TABLE_SIZE ); //using this global table        
             }
            // oscs.phase = 0;

             initTable();

             err = Pa_Initialize();
             if( err != paNoError ) goto error;

             outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
             if (outputParameters.device == paNoDevice) {
               fprintf(stderr,"Error: No default output device.\n");
               goto error;
             }
             outputParameters.channelCount = 1;       /* mono output */
             outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
             outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
             outputParameters.hostApiSpecificStreamInfo = NULL;

             err = Pa_OpenStream(
                       &stream,
                       NULL, /* no input */
                       &outputParameters,
                       SAMPLE_RATE,
                       FRAMES_PER_BUFFER,
                       paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                       patestCallback,
                       &oscs );
             if( err != paNoError ) goto error;

            // sprintf( oscs.message, "No Message" );
             err = Pa_SetStreamFinishedCallback( stream, &StreamFinished );
             if( err != paNoError ) goto error;

             err = Pa_StartStream( stream );
             if( err != paNoError ) goto error;

             printf("Play for %d seconds.\n", NUM_SECONDS );
             Pa_Sleep( NUM_SECONDS * 1000 );

             err = Pa_StopStream( stream );
             if( err != paNoError ) goto error;

             err = Pa_CloseStream( stream );
             if( err != paNoError ) goto error;

             Pa_Terminate();
             printf("Test finished.\n");

             return err;
         error:
             Pa_Terminate();
             fprintf( stderr, "An error occured while using the portaudio stream\n" );
             fprintf( stderr, "Error number: %d\n", err );
             fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
             return err;
         }

1 个答案:

答案 0 :(得分:2)

我可以建议在求和之前归零*out吗?

*out = 0;
for(int j = 0; j < 2; j++ )
{     
    *out += sinetable[(int)oscs[j].phase % TABLE_SIZE] * (oscs[j].amp / 2);                      
}

由于out+=的任何内容都会在总和中结束。

*out = sinetable[(int)oscs[0].phase % TABLE_SIZE] * (oscs[0].amp / 2) +  
       sinetable[(int)oscs[1].phase % TABLE_SIZE] * (oscs[1].amp / 2);

解决了这个问题,因为=会覆盖out处的任何内容。