PortAudioプログラミング入門V 19

12895 ワード

PortAudioって何?
PortAudioは、無料、プラットフォーム間、オープンソースのオーディオI/Oライブラリです.I/Oを見るとファイルが思い浮かぶかもしれませんが、PortAudioが操作するI/Oはファイルではなくオーディオデバイスです.C/C++のオーディオプログラムの設計実装を簡素化し、Windows、Macintosh OS X、UNIX上で実行できる(Linuxの各種バージョンも言うまでもない).PortAudioを使用すると、異なるプラットフォームでアプリケーションを移行できます.例えば、PortAudioベースのアプリケーションをAndroidバージョンにすることができます.
PortAudioのAPIは非常に簡単で、簡単なコールバック関数またはブロックされた読み取り/書き込みインタフェースを通じて音声を録画または再生する.PortAudioには、正弦波形のオーディオ信号を再生したり、オーディオ入力を処理したり、再生オーディオを録画したり、オーディオデバイスを列挙したりするなど、多くの例のプログラムが用意されています.
PortAudioの最新バージョンはV 19です.このバージョンについて説明します.
License
PortAudioは自分で定義したLicenseを使用しています.ポイントは次のとおりです.
1、あなたのプロジェクトやプログラムでPortAudioを無料で使用することができ、ビジネスソフトウェアも無料で使用することができます.
2、ソースを開けなくてもいいです.
3、PortAudioの著作権情報の削除を禁止する.
4、PortAudioのバグを修正した場合は、コミュニティに知らせてください.
5、もしあなたのプログラムがPortAudioで崩壊したら、私たちは何の責任も負いません.
ダウンロード
PortAudioはSVNで管理されており、パッケージされたソースコードを直接ダウンロードすることもできます.パスは次のとおりです.
http://www.portaudio.com/download.html
https://subversion.assembla.com/svn/portaudio/portaudio/
Windowsでは一般的にVSでコンパイルされていますが、詳細は以下を参照してください.
http://portaudio.com/docs/v19-doxydocs/compile_windows.html
PortAudioアーキテクチャ
PortAudioのアーキテクチャには、オーディオデバイスとオーディオストリームの2つの主要な抽象が含まれています.オーディオデバイスは、現在のシステムに搭載されている入出力端子です.PortAudioは、システムオーディオデバイスを列挙し、デバイスのプロパティを照会するインタフェースを提供します.オーディオストリームは、アクティブなオーディオの入力および出力を管理するために使用され、1つのオーディオストリームには、最大1つの入力デバイスと1つの出力デバイスしかありません.すなわち、1つのオーディオストリームは、フルデュプレクスであってもよいし、ハーフデュプレクスであってもよい.
プログラミング入門
PortAudioアプリケーションを作成するには、コールバック関数を把握するだけです.
1、コールバック関数を作成し、PortAudioはオーディオ処理を行う時に自動的に呼び出す.
2.PAライブラリを初期化し、オーディオI/Oのストリームを開く.
3、ストリームを起動し、PAはバックグラウンドでコールバック関数を呼び出す.
4、コールバック関数では、inputBufferからオーディオデータを読み込むか、outputBufferにオーディオデータを書き込むことができます.
5、コールバック関数は1を返すか、対応する関数を呼び出してストリームを停止します.
6、流れを閉じ、PAを終了する.
コールバック関数に加え、PAはブロックI/Oモデルもサポートしているが、すべての機能がサポートされているわけではない.コールバック関数を使用することをお勧めします.
次はPAでジグザグオーディオの再生を一歩一歩実現します.
ステップ1:コールバック関数の作成
まずPAのヘッダファイルを導入します.
#include "portaudio.h"

コールバック関数は、PAがオーディオデータを取得した場合と、出力としてオーディオデータを必要とした場合の両方でPAによって呼び出されます.
コールバック関数は、プログラム内の他のコードとは異なるシステムが特殊なスレッドでコールバック関数を処理し、中断によって処理するため、不思議な場所です.オーディオが時間通りにSpeakerに到達したい場合は、コールバック関数が迅速に実行されることを保証しなければなりません.異なるプラットフォームでは、どのような操作が安全で、どのような操作が安全ではなく、異なる.1つの一般的なフライングガイドラインは、メモリの割り当て解除操作、ファイルの読み書き、printf、またはOSに依存して一定時間以内に返されない他の操作を行わないことであり、コンテキストの切り替えを引き起こす可能性のある操作も含まれる.
コールバック関数のプロトタイプは次のとおりです.
typedef int PaStreamCallback( const void *input,
                                      void *output,
                                      unsigned long frameCount,
                                      const PaStreamCallbackTimeInfo* timeInfo,
                                      PaStreamCallbackFlags statusFlags,
                                      void *userData ) ;

次に、例のコールバック関数を示します.2つの(左チャネル、右チャネル)鋸歯信号を簡単に計算し、入力バッファに格納します.ユーザーがカスタマイズしたデータブロックにコールバックすることもできます.パラメータuserDataはデータを転送するポインタです.
typedef struct
{
    float left_phase;
    float right_phase;
}   

paTestData;

/* 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 )
{
    /* Cast data passed through stream to our structure. */
    paTestData *data = (paTestData*)userData; 
    float *out = (float*)outputBuffer;
    unsigned int i;
    (void) inputBuffer; /* Prevent unused variable warning. */

    for( i=0; i<framesPerBuffer; i++ )
    {
        *out++ = data->left_phase;  /* left */
        *out++ = data->right_phase;  /* right */
        /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
        data->left_phase += 0.01f;
        /* When signal reaches top, drop back down. */
        if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
        /* higher pitch so we can distinguish left and right. */
        data->right_phase += 0.03f;
        if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
    }
    return 0;
}

ステップ2:PAの初期化
PA関数を呼び出す前に、Pa_を呼び出す必要があります.Initialize().現在使用可能なオーディオデバイスをスキャンし、使用を問い合わせる.他のPA関数と同様に、エラーが発生した場合はエラータイプを返します.Pa_を通ることができますGetErrorText(err)はエラーメッセージを得る.
if( err != paNoError )
   printf(  "PortAudio error: %s
", Pa_GetErrorText( err ) );

PAを使用し終わったら、それを終了してください.
err = Pa_Terminate();
if( err != paNoError )
   printf(  "PortAudio error: %s
", Pa_GetErrorText( err ) );

ステップ3:フローを開く
次に、入力ストリームが開きます.ファイルを開くのと似ています.オーディオ入出力、トラック数、オーディオフォーマット、サンプリングレートなどが必要かどうかを指定できます.デフォルトの入力ストリームを開くことは、デフォルトのオーディオデバイスを開くことを意味し、オーディオデバイスを巡回して選択する必要はありません.
#define SAMPLE_RATE (44100)
static paTestData data;

.....

    PaStream *stream;
    PaError err;

    /* Open an audio I/O stream. */
    err = Pa_OpenDefaultStream( &stream,
                                0,          /* no input channels */
                                2,          /* stereo output */
                                paFloat32,  /* 32 bit floating point output */
                                SAMPLE_RATE,
                                256,        /* frames per buffer, i.e. the number of sample frames that PortAudio will          request from the callback. Many apps may want to use paFramesPerBufferUnspecified, which tells PortAudio to pick the best,possibly changing, buffer size.*/
                                patestCallback, /* this is your callback function */
                                &data ); /*This is a pointer that will be passed to your callback*/

if( err != paNoError ) goto error;

上記のコードは出力ストリームを開きますが、この例では入力オーディオストリームは必要ありませんので、出力ストリームを開くだけで十分です.PAはもちろん、1つの入力ストリームを開いて録画したり、入力ストリームと出力ストリームを同時に開いたりして、録画と再生を同時に行い、リアルタイムでオーディオ処理を行うこともできます.
同時に読む/書くときは、次の点に注意してください.
1、一部のプラットフォームでは、同じデバイス上で同時に読み書きを行うことしかできません.
2、複数のストリームを同時に開くことはできますが、同期は難しいです.
3、一部のプラットフォームでは、1つのデバイスで同時に複数のストリームを開くことはサポートされていません.
4、PAは複数のストリームに対するテストが不足している.
5、PA呼び出しは同じスレッドにあるか、ユーザーが同期を担当する.
ステップ4:フローの開始、停止、終了
PAは、ストリームを起動するまで音声を再生することはできません.Pa_を呼び出すだけです.StartStream()の後、PAはユーザのコールバック関数を呼び出してオーディオ処理を行う.
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;

コールバック関数に伝達されるユーザデータブロックによってコールバック関数と対話することができ、他の対話方法はグローバル変数、IPCである.コールバック関数は、ユーザコードと同期しにくい割り込みで呼び出される可能性があることに注意してください.したがって、信号量の場合、双方向チェーンテーブルなどの破壊されやすい複雑なデータ構造をできるだけ避け、ロックの使用を避けなければならない.これらは、コールバック関数がブロックされ、オーディオデータが失われる可能性があります.一部のプラットフォームでは、デッドロックの問題もあります.
PAは、ストリームを停止するまでコールバック関数を呼び出し続け、いくつかの方法でストリームを停止することができます.時々待ちたいかもしれませんが、PAは関数Paを提供しています.Sleep()は呼び出し元の睡眠にミリ秒を指定しますが、指定した時間を超えるまで待つ可能性があり、時間精度が高い場合は推奨されません.
/* Sleep for several seconds. */
Pa_Sleep(NUM_SECONDS*1000);

最も簡単にストリームを停止する方法はPaを呼び出すことです.StopStream():
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;

Pa_StopStream()は、ユーザがコールバック関数で処理したバッファデータを再生することを保証し、いくつかの遅延をもたらす可能性がある.代わりにPa_を呼び出すことですAbortStream().一部のプラットフォームでは、ストリームの終了が速いが、コールバック関数で処理されたデータが再生されない可能性がある.
もう1つのストリームを停止する方法は、コールバック関数がpaCompleteまたはpaAbortを返すことです.paCompleteは最後のバッファが再生されることを保証し、paAbortはできるだけ早く停止します.この方法を使用する場合は、ストリームを再開する前にPa_を呼び出す必要があります.StopStream().
ステップ5:ストリームのクローズとPAの終了
ストリームを処理した後、ストリームを閉じ、リソースを解放する必要があります.
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;

忘れたらPAを中止するのを忘れないでください.
err = Pa_Terminate( );
if( err != paNoError ) goto error;

ユーティリティ関数
PAは、動的リンクライブラリを使用する場合に便利な2つの関数を提供します.
int             Pa_GetVersion (void)
const char *    Pa_GetVersionText (void)

エラーコードに基づいてエラーメッセージを取得します.
const char *    Pa_GetErrorText (PaError errorCode)

PAストリームには,アクティブ,停止,コールバック停止の3つの状態がある.ストリームがコールバックで停止している場合は、ストリームを停止してからストリームを再起動する必要があります.
PaError     Pa_IsStreamStopped (PaStream *stream)
PaError     Pa_IsStreamActive (PaStream *stream)

遅延、サンプリングレートなどのストリームの情報を取得します.
const PaStreamInfo *    Pa_GetStreamInfo (PaStream *stream)

同期に使用するタイムスタンプ:
PaTime  Pa_GetStreamTime (PaStream *stream)

コールバック関数で使用するCPU数:
double  Pa_GetStreamCpuLoad (PaStream *stream)

フォーマットのサンプリングサイズを指定します.
PaError Pa_GetSampleSize (PaSampleFormat format)

列挙クエリオーディオデバイス
オーディオデバイスを明示的に指定する必要がある場合は、システムのオーディオデバイスを問い合わせる必要があります.クエリーの前にPAライブラリを初期化する必要があります.
int numDevices;

numDevices = Pa_GetDeviceCount();
if( numDevices < 0 )
{
        printf( "ERROR: Pa_CountDevices returned 0x%x
", numDevices ); err = numDevices; goto error; }

各デバイスの情報を取得したい場合は、直接ループを作成します.
const   PaDeviceInfo *deviceInfo;
for( i=0; i<numDevices; i++ )
{
        deviceInfo = Pa_GetDeviceInfo( i );
        ...
}

PaDeviceInfo構造体には、デバイス名、デフォルト遅延など、豊富なデバイス情報が含まれています.次のメンバーがあります.
int     structVersion
const char *    name
PaHostApiIndex  hostApi
int     maxInputChannels
int     maxOutputChannels
PaTime  defaultLowInputLatency
PaTime  defaultLowOutputLatency
PaTime  defaultHighInputLatency
PaTime  defaultHighOutputLatency
double  defaultSampleRate

しかし、これらの情報には設備の採用率がなく、どうすれば得られるのでしょうか.サンプリングレートがない原因は、デバイスの多様性にあり、オーディオデバイスサポートの採用率の違いが大きく、一定の戻りサンプリングレートをサポートするものもあれば、一連のサンプリングポイントをサポートするものもあれば、デバイスサポートのサンプリングレートをサポートするものもあり、他のデバイスはサポートしないわけにはいかない.そのため、デバイスを使用する前に、デバイスの能力をテストすることができます.
    const PaStreamParameters *inputParameters;

    const PaStreamParameters *outputParameters;

    double desiredSampleRate;

    ...

    PaError err;

 

    err = Pa_IsFormatSupported( inputParameters, outputParameters, desiredSampleRate );
    if( err == paFormatIsSupported )
    {
       printf( "Hooray!
"); } else { printf("Too Bad.
"); }

デバイス構成を取得した後、またはデバイスによって構成がサポートされているかどうかをテストしたい場合は、PaStreamParameters構造体に情報を入力し、ストリームを開いてみます.
    double srate = ... ;
    PaStream *stream;
    unsigned long framesPerBuffer = ... ; //could be paFramesPerBufferUnspecified, in which case PortAudio will do its best to manage it for you, but, on some platforms, the framesPerBuffer will change in each call to the callback
    PaStreamParameters outputParameters;
    PaStreamParameters inputParameters;

    bzero( &inputParameters, sizeof( inputParameters ) ); //not necessary if you are filling in all the fields
    inputParameters.channelCount = inChan;
    inputParameters.device = inDevNum;
    inputParameters.hostApiSpecificStreamInfo = NULL;
    inputParameters.sampleFormat = paFloat32;
    inputParameters.suggestedLatency = Pa_GetDeviceInfo(inDevNum)->defaultLowInputLatency ;
    inputParameters.hostApiSpecificStreamInfo = NULL; //See you specific host&apos;s API docs for info on using this field

    bzero( &outputParameters, sizeof( outputParameters ) ); //not necessary if you are filling in all the fields
    outputParameters.channelCount = outChan;
    outputParameters.device = outDevNum;
    outputParameters.hostApiSpecificStreamInfo = NULL;
    outputParameters.sampleFormat = paFloat32;
    outputParameters.suggestedLatency = Pa_GetDeviceInfo(outDevNum)->defaultLowOutputLatency ;
    outputParameters.hostApiSpecificStreamInfo = NULL; //See you specific host&apos;s API docs for info on using this field

    err = Pa_OpenStream(
                    &stream,
                    &inputParameters,
                    &outputParameters,
                    srate,
                    framesPerBuffer,
                    paNoFlag, //flags that can be used to define dither, clip settings and more
                    portAudioCallback, //your callback function
                    (void *)this ); //data to be passed to callback. In C++, it is frequently (void *)this
    //don&apos;t forget to check errors!

ブロックリード/ライト
PAのブロック用法では、性能に問題があります.しかし、V 19が導入した新しい特性として、サードパーティシステムとの互換性がより自然になり、理解しやすい.興味のある方は参考にしてくださいhttp://portaudio.com/docs/v19-doxydocs/blocking_read_write.html.