【visual studio】PortAudioのBlocking Read/Write関数

# まとめ
* callback関数を使いません
* c言語のscanf関数やprintf関数に似た記述になります．


# なぜBlocking APIなのか

PortAudioを使ってASIOドライバから音声を入出力をしようとすると，通常callback関数を使ってデータを受け渡しします．しかし，c++でcallback関数を使うと，発火する（callback関数が呼び出される）スレッドと，main関数のスレッドが違うため，データを受け渡しするために，共有メモリを用意したり特殊なデータ処理が必要になります．これはcallback処理に詳しい方ならば，実装できると思いますが，音響処理のビギナーにとってはハードルが高いです．特に大学の授業でcallback処理を扱う事は無いので，理解がしにくいと思います．

そこで，PortAudioではcallbackを使わずにデータを受け渡す，blocking r/w functionというものがあります．
普通にPortAudioのドキュメントに書いてあるのですが，英語だけで日本語で書かれたものがないので，なかなか使う人がいないような気がします．

# 準備

* DLL/Libファイルは各自コンパイルしてください．(URL参考）
* Hファイルとlibファイルをプロジェクトに使いしてください．
* 
https://qiita.com/nicolai_twi/items/db77bfe802d959c14ba4#_reference-3ca3d793553fd56d1398


# Example

１秒ごとに入力からデータを取得して出力にそのまま流すだけの処理

CLRコンソールアプリケーションのプロジェクトで作成しています．

```c++:portaudioBlockngAPI.cpp

#include "stdafx.h"

#include "portaudio.h"//the header file of portaudio

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace System;


#define 	SAMPLE_RATE   (44100)	//sampling rate to open portaudio
#define 	FRAMES_PER_BUFFER   (44100) // number of frames per buffer. In this portaudio, 'frames' are used like our samples. 
#define 	NUM_CHANNELS   (2)  // 1: mono 2:stereo. Roland octa-capture is not accept more than 2ch.
	
#define 	PRINTF_S_FORMAT   "%.8f"

#define 	PA_SAMPLE_TYPE   paInt16	//16bit
#define		SAMPLE_SIZE (2)				//16bit = 2 * 8bit(=byte)

//macros
#define CLEAR(a) memset( (a), 0, FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE )
#define Lch(a,i) *(a+2*i)
#define Rch(a,i) *(a+2*i+1)


void save_text(int N, __int16* wave,FILE *fp);
void filterExecute(int N, __int16* wave);

int main(array<System::String ^> ^args)
{
	FILE *fp;
	PaStreamParameters inputParameters, outputParameters;
	PaStream *stream;
    PaError err;
	__int16 *sampleBlock;
	int i;
	int numBytes;

	/* Initalize Buffer*/
	numBytes = FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE ;
	sampleBlock = (__int16 *) malloc( numBytes );
    if( sampleBlock == NULL )
    {
        printf("Could not allocate record array.\n");
        exit(1);
    }
    CLEAR( sampleBlock );

	/* Initalize PortAudio */
	err = Pa_Initialize();
	if( err != paNoError ) {
		Console::WriteLine(L"Error");
		return 1;
	}

	/* Initalize Save File Point*/
	if((fp = fopen("output.txt","w"))==NULL){
		printf("Could not open 'output.txt'. \n");
		exit(1);
	}

	/* -- setup input and output -- */
	inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
		
	printf( "Input device # %d.\n", inputParameters.device );
	printf( "Input device max channels %d.\n", Pa_GetDeviceInfo(inputParameters.device)->maxInputChannels );

	inputParameters.channelCount = NUM_CHANNELS;
	inputParameters.sampleFormat = PA_SAMPLE_TYPE;
	inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency ;
	inputParameters.hostApiSpecificStreamInfo = NULL;
	
	outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
	outputParameters.channelCount = NUM_CHANNELS;
	outputParameters.sampleFormat = PA_SAMPLE_TYPE;
	outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
	outputParameters.hostApiSpecificStreamInfo = NULL;
	
	printf( "Output device # %d.\n", outputParameters.device );
	printf( "Output device max channels %d.\n", Pa_GetDeviceInfo(outputParameters.device)->maxInputChannels );


	/* -- setup stream -- */
	err = Pa_OpenStream(
			  &stream,
			  &inputParameters,
			  &outputParameters,
			  SAMPLE_RATE,
			  FRAMES_PER_BUFFER,
			  paClipOff,      /* we won't output out of range samples so don't bother clipping them */
			  NULL, /* no callback, use blocking API */
			  NULL ); /* no callback, so no callback userData */
	if( err != paNoError ) {printf("OpenErr");goto error;};
	/* -- start stream -- */
	err = Pa_StartStream( stream );
	if( err != paNoError ) goto error;
	printf("Wire on. Will run one minute.\n"); fflush(stdout);
	/* -- Here's the loop where we pass data from input to output -- */
	for( i=0; i<(60*SAMPLE_RATE)/FRAMES_PER_BUFFER; ++i )
	{
		printf("%3d sec passed.\n",i);
	   err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER );
	   if( err ) goto xrun;

	   filterExecute(FRAMES_PER_BUFFER,sampleBlock);

	   err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
	   if( err ) goto xrun;

	   //save_text(FRAMES_PER_BUFFER,sampleBlock,fp);
	  
	}	
	printf("Wire off."); fflush(stdout);
	/* -- Now we stop the stream -- */
	err = Pa_StopStream( stream );
	if( err != paNoError ) goto error;
	/* -- don't forget to cleanup! -- */
	err = Pa_CloseStream( stream );
	if( err != paNoError ) goto error;
	Pa_Terminate();

	/* --cleanup buffer --*/
	free( sampleBlock );
	/* --cleanup file pointer */
	fclose(fp);

    return 0;

error: return 1;
xrun:	return 2;
}

void save_text(int N, __int16* wave,FILE *fp){
	int i;
	for(i=0;i<N;i++){
		fprintf(fp, "%d\t %d\n",*(wave+2*i),*(wave+2*i+1));
		//printf("%d\n",*(wave+i));
	}
}
void filterExecute(int N, __int16* wave){
	int i;

	for(i=0;i<N;i++){
		Lch(wave,i) = Lch(wave,i);
		Rch(wave,i) = Rch(wave,i);

	}
}

```

本来エラー処理時にメモリ解放を書くべきなのですが，省略します．


### open時にcallbackにはNULLを指定する．
``` 
err = Pa_OpenStream(
			  &stream,
			  &inputParameters,
			  &outputParameters,
			  SAMPLE_RATE,
			  FRAMES_PER_BUFFER,
			  paClipOff,      /* we won't output out of range samples so don't bother clipping them */
			  NULL, /* no callback, use blocking API */
			  NULL ); /* no callback, so no callback userData */
```


### バッファは16bit整数型を指定する．
```
#define     PA_SAMPLE_TYPE   paInt16    //16bit

.....

__int16 *sampleBlock;
```
今回はサンプリングレートが44.1Hz，16bitで読み出すので，上記のようになります．shortでもいいですが，__int16の方がわかりやすいです．（Visual Studio用の型）


### forループでバッファに読み書きする．

```
for( i=0; i<(60*SAMPLE_RATE)/FRAMES_PER_BUFFER; ++i )
	{
	   err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER );
	   if( err ) goto xrun;

	   err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
	   if( err ) goto xrun;
	}	
```

### Definition
http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html#a075a6efb503a728213bdae24347ed27d

```
PaError Pa_WriteStream	(
	PaStream * 	stream,
	const void * 	buffer,
	unsigned long 	frames 
)

PaError Pa_WriteStream	(
	PaStream * 	stream,
	const void * 	buffer,
	unsigned long 	frames 
)	
		
```

C言語のscanfやprintfに近い書き方で音声の入出力の処理がかけると思います．

#　まとめ２
* 日本語で書かれたドキュメントが少ないの書きました．（単純に需要がないだけなのかもしれません・・）
* blocking apiとても楽です．

#　未実施事項
* 24bit時の検証
* 96kHz時の確認