MATLABでGUI付きアプリ開発 ~ App Designer を使おう(2)~ストリーミングオーディオをフィルター処理
MATLABでGUI付きアプリ開発 ~ App Designer を使おう(1)~フィルター特性を変えてグラフ表示
MATLABでGUI付きアプリ開発 ~ App Designer を使おう(3)~タイマー割り込みでスペクトル表示(非同期FIFOを使う)
MATLABでGUI付きアプリ開発 ~ App Designer を使おう(4)~ フィルター周波数・バンド幅をマウスで操作
まえがき
今回は前回のアプリに、オーディオファイル読み込み・再生機能を追加してみます。フィルター効果の ON/OFF 切替も追加します。
最後に、実行結果動画とスクリプトがあります。
前回同様、Audio Toolbox を使用します。
DSP System Toolbox だけあれば動くかもしれませんが未確認です。
こちらも合わせてご覧ください。
MATLAB/Simulink で気軽に始める音響信号処理 ~ App Designer で GUIアプリも簡単開発!
ストリーミング・オーディオ
MATLAB でのオーディオファイルの取り扱いはとても簡単です。OSで扱える形式はそのまま読み込めます。ビデオの音声部分も直接読み込めます。
まずは標準のファイル選択 UI を使って、オーディオファイルを選択します。
[FileName, PathName, ~] = uigetfile({'*.mp3;*.m4a;*.aac;*.wav;*.mp4;*.flac;*.ogg;*.webm','Music Files';'*.*','All Files'},'Select Song', 'MultiSelect', 'off');
inputFilename = [PathName FileName];
次に、ストリーミングオブジェクトを定義します。
ストリーミングオブジェクトは、ファイル全体を読み込むことなく、あるフレームサイズ毎に連続して処理するとこができます。それぞれ、ファイル読み込み、音声出力用です。
hAudioSource = dsp.AudioFileReader(inputFilename);
hAudioOut = audioDeviceWriter('SampleRate', hAudioSource.SampleRate);
後は、
while ~isDone(hAudioSource)
in = hAudioSource();
hAudioOut(in);
end
で最後まで再生されます。
フィルター処理を入れる場合は、
octFilt = octaveFilter();
while ~isDone(hAudioSource)
in = hAudioSource();
hAudioOut(octFilt(in));
end
とするだけです。
使い終わったら、
release(hAudioSource)
release(hAudioOut)
で解放しておきましょう。
アプリ
では、実際のコードについてです。
GUIコンポーネントの追加
前回のアプリで置いたコンポーネントを移動してスペースを空け、以下のコンポーネントを追加します。
・ボタン(LoadFileButton)
読み込むオーディオファイル選択用に使います。
Text:LoadFile にします。
・ドロップダウン(OutputDeviceDropDown)
オーディオ出力デバイス設定に使います。
ラベル:OutputDevice にします。
後はコード中で設定します
・ボタン(RSTDevButton)
オーディオインターフェースの状態が変わったときに、クリックすると再設定します。
アプリ再立ち上げの必要がなくなります。
Text:RSTDev にします。
・状態ボタン(PLAYButton)
押す度にON/OFF状態が切り替わるボタンです(トグルボタン)。
再生・停止に使います。
Text:PLAY とします。
コード中で、表示するテキスト、背景色を切り替えます。
・状態ボタン(BYPASSButton)
Text:BYPASS とします。
フィルター効果のON/OFFを切り替えます。
・テキストエリア
読み込んだファイル名を表示するのに使います。
ラベル:Track
Value:最初にオーディオファイルを読み込んでください
コンポーネントは、Ctrl+C / Ctrl+V でコピーができますが、DropDownなどは実はラベル部分と分かれています。選択済みのを再クリックすると個別選択になってしまうので、全体を選択する場合は中央付近をクリックするなどしてください。
プロパティの追加
以下を追加します。
FrameSize = 256; % processing frame size
BufferSize = 1024; % PC buffer size
inputFilename
Output_Device
PLAY = false;
BYPASS = false;
setFile = false; % File is set?
RSTDev = false;
もし再生時に音が途切れる場合は、FrameSize を大きくしてみてください。
レイテンシー(GUI操作してから音が変化するまでのディレイ)も増加するので~4096程度までが良いとは思います。
逆に音切れしない範囲で小さくもできます。
FrameSize を大きくしても音切れする場合は BufferSize を大きくしてみてください。
コールバックの追加
・LoadFileButton
オーディオファイルが選択されたらファイル情報を取得し、PLAYボタンの背景を白に変更、Track に曲名を表示します。
PLAY 状態で呼ばれた場合は強制的に STOP 状態にします。
function LoadFileButtonPushed(app, event)
[FileName, PathName, ~] = uigetfile({['*.mp3;*.m4a;*.aac;*.wav;* ...' ...
'.mp4;*.flac;*.ogg;*.webm'],'Music Files';'*.*','All Files'}, ...
'Select Song', 'MultiSelect', 'off');
if FileName ~= 0
app.inputFilename = [PathName FileName];
m_info = audioinfo(app.inputFilename);
app.fs = m_info.SampleRate;
app.setFile = true;
app.PLAYButton.BackgroundColor = [1 1 1];
app.TrackTextArea.Value = FileName;
if app.PLAY % if so force stop
app.PLAYButton.Value = false;
PLAYButtonValueChanged(app, event);
end
end
end
・OutputDeviceDropDown
出力デバイスを選択します。
再生中は変更できないようにしています。
function OutputDeviceDropDownValueChanged(app, event)
value = app.OutputDeviceDropDown.Value;
if ~app.PLAY % while STOP state only
app.Output_Device = value;
else
app.OutputDeviceDropDown.Value = app.Output_Device;
end
end
・RSTDevButton
オーディオデバイスリストの更新を行い、startupFcn を呼び出して再度設定を行います。
function RSTDevButtonPushed(app, event)
app.RSTDev = true;
audiodevreset
startupFcn(app);
end
・PLAYButton
ファイル選択済みであれば(ループ)再生を行います。
PLAYがクリックされれば表示テキストを "STOP" に、再度押されれば "PLAY" に戻しています。
BYPASS時は入力をそのまま、そうでなければフィルター処理をして出力します。
フィルターオブジェクト等は途中で列サイズを変更できないため、入力がモノラルであった場合はステレオにして列サイズを揃えています。
drawnow で GUI からの割り込みを受け付けます。
その後、PLAY 中にウィンドウが閉じられた場合のチェックを入れています。
function PLAYButtonValueChanged(app, event)
value = app.PLAYButton.Value;
if ~app.setFile % file isn't set
app.PLAYButton.Value = false;
return;
end
app.PLAY = value;
if app.PLAY
app.PLAYButton.Text = 'STOP';
else
app.PLAYButton.Text = 'PLAY';
end
if app.PLAY
hAudioSource = dsp.AudioFileReader(app.inputFilename,'SamplesPerFrame',app.FrameSize);
hAudioOut = audioDeviceWriter('DeviceName', app.Output_Device, 'SampleRate', hAudioSource.SampleRate,...
'SupportVariableSizeInput', true, 'BufferSize', app.BufferSize);
while app.PLAY
u = step(hAudioSource);
if size(u,2) >= 2
u_st = u(:,1:2);
else % mono
u_st = [u(:,1) u(:,1)];
end
if isDone(hAudioSource) % Loop Play
reset(hAudioSource);
end
if app.BYPASS
out = u_st;
else
out = app.octFilt(u_st);
end
hAudioOut(out); % sound out
drawnow limitrate % for GUI interrupt
if ~isvalid(app); break; end % check unexpected force termination by user
end
reset(hAudioSource);
end
end
startupFcnの追加
出力デバイス一覧の OutputDeviceDropDown への追加、PLAYButton の初期背景色設定を追加します。RSTDev がクリックされたときは出力デバイスリストの再設定のみを行います。
function startupFcn(app)
% set output devices
info = audiodevinfo;
dev_n = length(info.output);
sa = struct2cell(info.output);
sa_Name = sa(1,:,:);
sa_Name_s = squeeze(sa_Name);
for i=1:dev_n
idx = strfind(sa_Name_s{i},'(');
if isempty(idx)
idx = length(sa_Name_s{i});
else
idx = idx(end) - 1;
end
sa_Name_s{i} = strtrim(sa_Name_s{i}(1:idx));
end
app.OutputDeviceDropDown.Items = sa_Name_s;
app.Output_Device = sa_Name_s{1};
app.OutputDeviceDropDown.Value = app.Output_Device;
if ~app.RSTDev
app.PLAYButton.BackgroundColor = [0.75 0.75 0.75];
app.BandwidthDropDown.Items = {'1 octave', '2/3 octave', '1/2 octave', ...
'1/3 octave', '1/6 octave', '1/12 octave', '1/24 octave', '1/48 octave'};
app.BandwidthDropDown.Value = '1 octave';
app.octFilt = octaveFilter; % octave filter object
drawFrequencyResponse(app);
else
app.RSTDev = false;
end
end
終了処理の追加
オーディオ入出力オブジェクトのリリースを追加します。
function UIFigureCloseRequest(app, event)
release(app.octFilt)
if exist('hAudioSource','var'); release(hAudioSource); end
if exist('hAudioOut','var'); release(hAudioOut); end
delete(app)
end
実行結果
動画でご覧ください(音付き)。
スクリプト
まとめ
前回から比べれば少し複雑に見えるかもしれませんが、オーディオ信号処理一般に共通に使えると思いますので、1回作ってしまえばコピペで使い回しができます。ぜひご活用ください!
(3)があるかどうかは未定です。(¬_¬)