LOOPERを作ってみた。

#概要
JUCEを使って、LOOPERのVSTプラグインを作成します。
１つのボタンで、録音とループ再生を行うシンプルなVSTを作成します。

![スクリーンショット 2017-12-19 4.40.24.png](https://qiita-image-store.s3.amazonaws.com/0/214835/551f5779-6535-c3da-c0c0-b004c9aa31fb.png)

処理の仕組みは、録音時には入力のバッファを全て保存しておき、ループ再生時には、バッファに保存したデータを書き出力するだけです。
１バッファは512サンプルとしたとき、processeblock()で入力からの１バッファを取り込み、それを加工して出力へと吐き出されます。何もしなければバイパスとなります。

具体的には、録音時には、最初の１バッファは1~512サンプル、次のバッファは５１３〜１０２４・・・を順次、float型の値を格納するarrayに入力バッファを取り込みます。（バッファ自体は書き変わらないため、バイパスと同じ。）
ループ再生時には、1~512サンプルをバッファへ書き込む、次のバッファは５１３〜１０２４を書き込む・・・を行い、録音開始から終了までのバッファ数まできたら１サンプル目に戻ります。

![仕組み.001.png](https://qiita-image-store.s3.amazonaws.com/0/214835/43ee2609-480f-062a-d9d2-00e5142d80cf.png)





#ファイルの構成
projucerで新規プロジェクトを立ち上げ、Audio Plug-inを選択し、プロジェクト名（ここでは"looper"）を入れて、Create...を押してください。なお、今回はXcodeでの作成になります。

Xcodeの画面がでると、左側にファイルの一覧がでます。
プログラムを書く必要があるファイルは、
・PluginProcessor.cpp（主にバッファ毎に音声処理したいプログラムを書きます。）
・PluginProcessor.h
・PluginEditor.cpp（GUIを作成します。）
・PluginEditor.h
の４つに加え、以下２つを新規に作成します。
・loop.cpp（録音のためのメソッドとループ再生を行うメソッドを作成します。）
・loop.h

上記のファイルに必要な記載について、１ファイル毎に説明していきます。

##1. PluginProcessor.cpp
録音からループ再生とその逆の切り替え時には、looplengthもしくはlooptimeを初期値に戻す必要があります。
onoffは録音中かループ再生中かを表すフラグで、前者が1,後者が0としています。0→1の場合は録音へ切り替え、1→0の場合はループ再生へ切り替えます。preonoffはonoffの１バッファ前の状態を保存しています。

```
（前略）
void LooperAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
（中略）
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        MidiBuffer processedMidi;
        MidiMessage m;
            
            if (onoff==1 && preonoff==0)//録音へ切り替わったらlooplengthを決め直すため、1に戻す。
            {
                LOOP.looplength = 1;
            }
            else if (onoff==0 && preonoff==1)//ループ再生へ切り替わったらstoredloop[]を頭から再生するため、looptimeを0にする。
            {
                LOOP.looptime = 0;
            }

        if(onoff == 1)
            LOOP.setloop(buffer.getWritePointer(channel),buffer.getNumSamples());//録音の関数を呼び出す
        else if(onoff == 0)
            LOOP.getloop(buffer.getWritePointer(channel),buffer.getNumSamples());//ループ再生の関数を呼び出す。

    }
    preonoff=onoff;//onnoffの一つ前の状態をpreonoffに保存。
}

```

##2. PluginProcessor.h
processBlockの処理過程で必要な関数は、
同LooperAudioProcessorクラスに書きましょう。
また、GUI側からも参照されるので、publicに書きましょう。

```
#include "../JuceLibraryCode/JuceHeader.h"
#include "loop.h"//録音・ループ再生を処理する関数に関するloopクラスのヘッダーファイル

class LooperAudioProcessor  : public AudioProcessor
{
public:
    int onoff=1;//録音中なら１、ループ再生中なら０
    int preonoff=1;//１つ前のバッファ取り込み時のonoffを保存するためのもの。前後で１０か０１なら録音からループ再生
またはその逆に切り替わる時点とする。
(中略)
loop LOOP;//loopクラスの実装
private:
(後略)
```

##3. PluginEditor.cpp
GUIはボタンの設計だけを行なっています。
GUIのボタンを押した時に録音(setloop())またはループ再生(getloop())の関数を呼び出します。
ただし、ボタンを押す度に、int型の変数onoffは1→0→1→0→・・・と順番に変化するようにし、これに変化があったとき、録音とループ再生の切り替えが行われるようにします。

```
LooperAudioProcessorEditor::LooperAudioProcessorEditor (LooperAudioProcessor& p)
    : AudioProcessorEditor (&p), processor (p)
{   
    addAndMakeVisible (button1);//GUIにボタンを表示
    button1.setButtonText("looper");//GUIのボタン内に表示するワードをセット
    button1.addListener (this);//いつ押したかを監視している。 
    setSize (400, 300);
}

（中略）

void LooperAudioProcessorEditor::resized()
{
    button1.setBounds (getWidth() * 0.1, getHeight() * 0.1, getWidth()*0.8, getHeight() * 0.1);//ボタンの大きさを設定
}
void LooperAudioProcessorEditor::buttonClicked(Button* button){//ボタンを押したら1onoffを切り替え
    if (processor.onoff==0)  processor.onoff = 1;
    else  processor.onoff=0;
}
```

##4. PluginEditor.h
buttonClicked(Button* button)には、GUI上のボタンを押した際に行う処理を記載します。
ただし、これを使えるようにするには、juce::Button::Listenerの継承をお忘れなく。
ここでは、ボタン押下時にonoffを切り替えます。

```
class LooperAudioProcessorEditor  : public AudioProcessorEditor,private juce::Button::Listener//下のTextButtonを使えるように、追加を忘れずに！
{
(中略)
private:    
    void buttonClicked(Button* button) override;    //ボタンを押した時にonoffを切り替える関数
    LooperAudioProcessor& processor; //
    TextButton button1; //
（後略）
```


##5. loop.cpp
録音のためのメソッドとループ再生を行うメソッドを作成します。
概要でも説明の通り、録音の時間がloopの長さ（＝バッファ数（＝looplength）*512サンプル）で、
ループ再生時には、storedloop[]からバッファへ順次書き込みます。looptimeでstoredloop[]の何バッファ目を書き込むか指定します。


```
#include "loop.h"
#include <math.h>

loop::loop()
:looplength(1),storedloop{0},looptime(0)
{}

loop::~loop(){}

void loop::setloop(float* bufferPtr, int bufferSize) //録音
{
    for(int i = 0 ; i < bufferSize;i++){
        storedloop[(looplength - 1) * bufferSize + i] = bufferPtr[i];
    }
    if(looplength < 900){//storedloop[]の大きさを超えないように制御
    looplength = looplength + 1 ;
    }
};



void loop::getloop(float* bufferPtr, int bufferSize){//ループ再生
    for(int i = 0 ; i < bufferSize;i++){
        bufferPtr[i] = storedloop[looptime * bufferSize + i];
    }
      looptime = (looptime + 1) % looplength;  //looptimeは、looplengthを超えないで繰り返します。
};
```

##6. loop.h

バッファはfloat型のサンプルを512個持ったarray[]ですので、録音先もfloatで数値を保存できるようにする必要があります。

```
#ifndef LOOP_H_INCLUDEDED
#define LOOP_H_INCLUDEDED

class loop
{
public:
    loop();
    ~loop();
    
    void setloop(float* bufferPtr, int bufferSize);
    void getloop(float* bufferPtr, int bufferSize);
    
    int looplength;//録音再生から終了までのバッファ数
    float storedloop[500000]; //ひとまず今回は、10秒ぐらいまで録音できるようにする。
    int looptime;//ループ再生時に何バッファ目かを示す。

};

#endif
```

#まとめ・感想
今回はシンプルなlooperを作成する手順を記載しました。以外と簡単にできる！と思っていてもちょっとしたことがわからないと、調べるのに時間がかかったり、すぐわかるはずのことに気がつかないことも多々ありました。本記事は、過程で気づいたことを１つでも多くと思って書きました。全体的にメモ書きっぽくなり申し訳ないと思う一方で、ちょっとした気づきの一助でも担えれば幸いです。