#概要
JUCEを使って、LOOPERのVSTプラグインを作成します。
1つのボタンで、録音とループ再生を行うシンプルなVSTを作成します。
処理の仕組みは、録音時には入力のバッファを全て保存しておき、ループ再生時には、バッファに保存したデータを書き出力するだけです。
1バッファは512サンプルとしたとき、processeblock()で入力からの1バッファを取り込み、それを加工して出力へと吐き出されます。何もしなければバイパスとなります。
具体的には、録音時には、最初の1バッファは1~512サンプル、次のバッファは513〜1024・・・を順次、float型の値を格納するarrayに入力バッファを取り込みます。(バッファ自体は書き変わらないため、バイパスと同じ。)
ループ再生時には、1~512サンプルをバッファへ書き込む、次のバッファは513〜1024を書き込む・・・を行い、録音開始から終了までのバッファ数まできたら1サンプル目に戻ります。
#ファイルの構成
projucerで新規プロジェクトを立ち上げ、Audio Plug-inを選択し、プロジェクト名(ここでは"looper")を入れて、Create...を押してください。なお、今回はXcodeでの作成になります。
Xcodeの画面がでると、左側にファイルの一覧がでます。
プログラムを書く必要があるファイルは、
・PluginProcessor.cpp(主にバッファ毎に音声処理したいプログラムを書きます。)
・PluginProcessor.h
・PluginEditor.cpp(GUIを作成します。)
・PluginEditor.h
の4つに加え、以下2つを新規に作成します。
・loop.cpp(録音のためのメソッドとループ再生を行うメソッドを作成します。)
・loop.h
上記のファイルに必要な記載について、1ファイル毎に説明していきます。
##1. PluginProcessor.cpp
録音からループ再生とその逆の切り替え時には、looplengthもしくはlooptimeを初期値に戻す必要があります。
onoffは録音中かループ再生中かを表すフラグで、前者が1,後者が0としています。0→1の場合は録音へ切り替え、1→0の場合はループ再生へ切り替えます。preonoffはonoffの1バッファ前の状態を保存しています。
(前略)
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;//録音中なら1、ループ再生中なら0
int preonoff=1;//1つ前のバッファ取り込み時のonoffを保存するためのもの。前後で10か01なら録音からループ再生
またはその逆に切り替わる時点とする。
(中略)
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を作成する手順を記載しました。以外と簡単にできる!と思っていてもちょっとしたことがわからないと、調べるのに時間がかかったり、すぐわかるはずのことに気がつかないことも多々ありました。本記事は、過程で気づいたことを1つでも多くと思って書きました。全体的にメモ書きっぽくなり申し訳ないと思う一方で、ちょっとした気づきの一助でも担えれば幸いです。