speex+portaudioを使ったechoアプリを書いてみた

Posted on 10月 18, 2008
Filed Under speex, portaudio |

マイク → speexエンコーダ → speexデコーダ → スピーカー
だけの。
speexエンコーダ、speexデコーダ無しだと問題ないところ、ありにすると、
なんか高音のノイズが入るのはなぜ?

speex-devのMLに質問中。。。

コードはこんな

C++:
  1. #include "stdafx.h"
  2.  
  3. #include <queue>
  4. #include <iostream>
  5.  
  6. #include <portaudio .h>
  7. #include <speex /speex.h>
  8. #include </speex><speex /speex_callbacks.h>
  9.  
  10.  
  11. #define USE_SPEEX
  12.  
  13.  
  14. #define SAMPLE_RATE  (44100)
  15. #define FRAMES_PER_BUFFER (256)
  16. #define NUM_SECONDS     (10)
  17. //#define NUM_CHANNELS    (2)
  18. #define NUM_CHANNELS    (1) // try mono first
  19. /* #define DITHER_FLAG     (paDitherOff) */
  20. #define DITHER_FLAG     (0) /**/
  21.  
  22. /* Select sample format. */
  23. #define PA_SAMPLE_TYPE  paFloat32
  24. typedef float SAMPLE;
  25. #define SAMPLE_SILENCE  (0.0f)
  26. #define PRINTF_S_FORMAT "%.8f"
  27.  
  28. typedef struct {
  29.     int                frameIndex;  /* Index into sample array. */
  30.     int                maxFrameIndex;
  31.     std::queue<sample> qu_microphone;
  32.     std::queue<char>   qu_speex;
  33.     std::queue<sample> qu_speaker;
  34. } paTestData;
  35.  
  36. // speex globals
  37. SpeexBits encoder_bits;
  38. SpeexBits decoder_bits;
  39. void *encoder_state;
  40. void *decoder_state;
  41. unsigned int speex_frame_size;  // 320 or 640 or ..
  42. unsigned int speex_packet_size = 74;    // 74 or something
  43.  
  44. /* This routine will be called by the PortAudio engine when audio is needed.
  45. ** It may be called at interrupt level on some machines so don't do anything
  46. ** that could mess up the system like calling malloc() or free().
  47. */
  48. static int echo( const void *inputBuffer,
  49.                             void *outputBuffer,
  50.                             unsigned long framesPerBuffer,
  51.                             const PaStreamCallbackTimeInfo* timeInfo,
  52.                             PaStreamCallbackFlags statusFlags,
  53.                             void *userData
  54.                            )
  55. {
  56.     paTestData *data = (paTestData*)userData;
  57.     const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
  58.     SAMPLE       *wptr = (SAMPLE*)outputBuffer;
  59. #ifdef USE_SPEEX
  60.     std::queue</sample><sample>& qu_microphone = data->qu_microphone;
  61.     std::queue<char>&   qu_speex      = data->qu_speex;
  62.     std::queue<sample>& qu_speaker    = data->qu_speaker;
  63. #else
  64.     std::queue</sample><sample>& qu_microphone = data->qu_microphone;
  65.     //std::queue<char>&   qu_speex      = data->qu_speex;
  66.     std::queue<sample>& qu_speaker    = data->qu_microphone;    // point at the same queue, to feed the speaker what we got from the microphone
  67. #endif
  68.     long framesToCalc;
  69.     long i;
  70.     int finished;
  71.     unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
  72.  
  73.     if( framesLeft <framesPerBuffer ) {
  74.         framesToCalc = framesLeft;
  75.         finished = paComplete;
  76.     }
  77.     else {
  78.         framesToCalc = framesPerBuffer;
  79.         finished = paContinue;
  80.     }
  81.  
  82.     // process input from mic
  83.     if( rptr == 0 ) {
  84.     }
  85.     else {
  86.         for( i=0; i<framesToCalc; i++ ) {
  87.             qu_microphone.push( *rptr++ );
  88.             if ( NUM_CHANNELS == 2 ) {
  89.                 qu_microphone.push( *rptr++ );
  90.             }
  91.         }
  92.     }
  93.  
  94. #ifdef USE_SPEEX
  95.     // speex encoder
  96.     while ( qu_microphone.size()> speex_frame_size ) {
  97.         float input[1000];
  98.         char  encoded[1000];
  99.         for ( unsigned int i=0; i<speex_frame_size ; i++ ) {
  100.             input[i] = qu_microphone.front(); qu_microphone.pop();
  101.         }
  102.         speex_bits_reset( &encoder_bits );
  103.         speex_encode( encoder_state, input, &encoder_bits );
  104.         speex_packet_size = speex_bits_write( &encoder_bits, encoded, 1000 );
  105.         for ( unsigned int i=0; i<speex_packet_size; i++ ) {
  106.             qu_speex.push( encoded[i] );
  107.         }
  108.     }
  109.        
  110.  
  111.     // speex decoder
  112.     while ( qu_speex.size()>= speex_packet_size ) {
  113.         float output[1000];
  114.         char  encoded[1000];
  115.         int nbBytes = speex_packet_size;
  116.         for ( int i=0; i<nbbytes ; i++ ) {
  117.             encoded[i] = qu_speex.front(); qu_speex.pop();
  118.         }
  119.         speex_bits_read_from( &decoder_bits, encoded, nbBytes );
  120.         speex_decode( decoder_state, &decoder_bits, output );
  121.         for ( unsigned int i=0; i<speex_frame_size; i++ ) {
  122.             qu_speaker.push( output[i] );
  123.         }
  124.     }
  125. #endif
  126.  
  127.     // pop to speaker
  128.     for( i=0; i<framesToCalc; i++ ) {
  129.         if ( qu_speaker.size()> 0 ) {
  130.             *wptr++ = qu_speaker.front(); qu_speaker.pop();
  131.         }
  132.         else {
  133.             *wptr++ = SAMPLE_SILENCE;
  134.         }
  135.         if( NUM_CHANNELS == 2 ) {
  136.             if ( qu_speaker.size()> 0 ) {
  137.                 *wptr++ = qu_speaker.front(); qu_speaker.pop();
  138.             }
  139.             else {
  140.                 *wptr++ = SAMPLE_SILENCE;
  141.             }
  142.         }
  143.     }
  144.    
  145.     data->frameIndex += framesToCalc;
  146.     return finished;
  147. }
  148.  
  149. /*******************************************************************/
  150. int main(void)
  151. {
  152.     PaStreamParameters  inputParameters,
  153.                         outputParameters;
  154.     PaStream*           stream;
  155.     PaError             err = paNoError;
  156.     int                 totalFrames;
  157.  
  158.     std::cout <<"patest_record.c";
  159.  
  160.     paTestData          data;
  161.     data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
  162.     data.frameIndex = 0;
  163.     int numSamples = totalFrames * NUM_CHANNELS;
  164.     int numBytes   = numSamples * sizeof(SAMPLE);
  165.     int tmp;
  166.    
  167.     // encoder
  168.     encoder_state = speex_encoder_init( speex_lib_get_mode(SPEEX_MODEID_UWB) );
  169.  
  170.     tmp=0;
  171.     speex_encoder_ctl(encoder_state, SPEEX_SET_VBR, &tmp);
  172.     tmp=8;
  173.     speex_encoder_ctl(encoder_state, SPEEX_SET_QUALITY, &tmp)// 8: 27,800[bps]
  174.     tmp=3;
  175.     speex_encoder_ctl(encoder_state, SPEEX_SET_COMPLEXITY, &tmp);
  176.     // 320[samples] = 20ms? according to http://www.speex.org/docs/manual/speex-manual/node7.html
  177.     speex_encoder_ctl( encoder_state, SPEEX_GET_FRAME_SIZE, &speex_frame_size );
  178.     speex_bits_init( &encoder_bits );
  179.  
  180.  
  181.     // decoder
  182.     decoder_state = speex_decoder_init( speex_lib_get_mode(SPEEX_MODEID_UWB) );
  183.  
  184.     SpeexCallback callback;
  185.     callback.callback_id = SPEEX_INBAND_CHAR;
  186.     callback.func = speex_std_char_handler;
  187.     callback.data = stderr;
  188.     speex_decoder_ctl(decoder_state, SPEEX_SET_HANDLER, &callback);
  189.     tmp=1;
  190.     speex_decoder_ctl(decoder_state, SPEEX_SET_ENH, &tmp);
  191.     speex_bits_init( &decoder_bits );
  192.  
  193.  
  194.     err = Pa_Initialize();
  195.     if( err != paNoError ) goto done;
  196.  
  197.     inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
  198.     if (inputParameters.device == paNoDevice) {
  199.         fprintf(stderr,"Error: No default input device.\n");
  200.         goto done;
  201.     }
  202.     inputParameters.channelCount = NUM_CHANNELS;                    /* stereo input */
  203.     inputParameters.sampleFormat = PA_SAMPLE_TYPE;
  204.     inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
  205.     inputParameters.hostApiSpecificStreamInfo = 0;
  206.  
  207.     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
  208.     if (outputParameters.device == paNoDevice) {
  209.         fprintf(stderr,"Error: No default output device.\n");
  210.         goto done;
  211.     }
  212.     outputParameters.channelCount = NUM_CHANNELS;                     /* stereo output */
  213.     outputParameters.sampleFormat =  PA_SAMPLE_TYPE;
  214.     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
  215.     outputParameters.hostApiSpecificStreamInfo = 0;
  216.  
  217.     /* start echo -------------------------------------------- */
  218.     err = Pa_OpenStream(
  219.               &stream,
  220.               &inputParameters,
  221.               &outputParameters,
  222.               SAMPLE_RATE,
  223.               FRAMES_PER_BUFFER,
  224.               paClipOff,      /* we won't output out of range samples so don't bother clipping them */
  225.               echo,
  226.               &data );
  227.     if( err != paNoError ) goto done;
  228.  
  229.     err = Pa_StartStream( stream );
  230.     if( err != paNoError ) goto done;
  231.     printf("Now recording!!\n"); fflush(stdout);
  232.  
  233.     while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
  234.     {
  235.         Pa_Sleep(1000);
  236.         printf("index = %d\n", data.frameIndex ); fflush(stdout);
  237.     }
  238.     if( err <0 ) goto done;
  239.  
  240.     err = Pa_CloseStream( stream );
  241.     if( err != paNoError ) goto done;
  242.  
  243. done:
  244.     // destruct speex
  245.     speex_encoder_destroy( encoder_state );
  246.     speex_bits_destroy( &encoder_bits );
  247.     speex_decoder_destroy( decoder_state );
  248.     speex_bits_destroy( &decoder_bits );
  249.  
  250.  
  251.     Pa_Terminate();
  252.     if( err != paNoError )
  253.     {
  254.         fprintf( stderr, "An error occured while using the portaudio stream\n" );
  255.         fprintf( stderr, "Error number: %d\n", err );
  256.         fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
  257.         err = 1;          /* Always return 0 or 1, but no other return codes. */
  258.     }
  259.     return err;
  260. }

Comments

2 Responses to “speex+portaudioを使ったechoアプリを書いてみた”

  1. BobLeponge on 11月 15th, 2010 21:19

    Franchement GG meme nous on sais pas pourquoi sa bipos

  2. Xavier Duale on 11月 15th, 2010 21:22

    Salut,

    Je sais aps si tu comprendras ce message mais au pire google trad le fera a peu pres bien.

    Si sa bip c’est a cause de l’offset multiplexé.
    En effet ton encoder_state ne correspond pas a la valeur de l’offset sur la stack.

    Et ton decode chie dans le paté.

    Coradialement

Leave a Reply