Last active
November 4, 2020 10:25
-
-
Save naoh16/dce0b5ad587f310d82c7b174f7b20756 to your computer and use it in GitHub Desktop.
Audio Recording tool for MATLAB/Octave
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%ARAYURU(vargin) | |
% This is very simple audio recording tool. You can extract short audio | |
% segment by mouse clicking (right-click, then select 'save' menu). | |
%<OPTIONS> | |
% varname : Variable name for segments (DEFAULT: 'arayuru_segments') | |
% sampling_rate : Sampling rate of audio (DEFAULT: 16000 [Hz]) | |
% record_len : Length of recording window (DEFAULT: 2.0 [sec]) | |
% segment_len : Length of segmentation window (DEFAULT: 0.6 [sec]) | |
%<USAGE> | |
% arayuru('varname', 'mydata'); | |
% arayuru('sampling_rate', 8000, 'record_len', 10.0); | |
% | |
% Sunao Hara, Okayama University, 2020. | |
% <[email protected]> | |
% | |
function arayuru(varargin) | |
%% Argument parsing | |
arg = inputParser; | |
addParameter(arg, 'varname', 'arayuru_segments'); | |
addParameter(arg, 'sampling_rate', 16000, @(x)isnumeric(x)&&isscalar(x)); | |
addParameter(arg, 'record_len', 2.0, @(x)isnumeric(x)&&isscalar(x)); | |
addParameter(arg, 'segment_len', 0.6, @(x)isnumeric(x)&&isscalar(x)); | |
parse(arg, varargin{:}); | |
%% Debug output | |
disp('*** Arguments ***'); | |
disp(arg.Results); | |
%% Prepare parameters | |
ud.output_varname = arg.Results.varname; | |
ud.sampling_rate = arg.Results.sampling_rate; | |
ud.max_length = arg.Results.record_len * ud.sampling_rate; | |
ud.segment_length = arg.Results.segment_len * ud.sampling_rate; | |
ud.audio_obj = []; | |
ud.timer_obj = []; | |
ud.signals = zeros(ud.max_length, 1); | |
ud.segments = {}; | |
ud.curX = 0; | |
ud.curLine = []; | |
%% Construct UI | |
ud.f = figure; | |
ud.ax = axes('Position', [0.1 0.2 0.8 0.7], 'Box', 'on', ... | |
'Tag', 'AXES_WAVEFORM', 'XLim', [0 ud.max_length], 'YLim', [-1 1]); | |
ud.waveplot = plot(ud.ax, ud.signals); | |
setup_waveform_figure(ud.ax, ud.max_length); | |
c = uicontextmenu; | |
uimenu(c, 'Label', 'save', 'Callback', @save_segment); | |
set(gca, 'UIContextMenu', c); | |
ud.btn_start = uicontrol('Style', 'pushbutton', 'Tag', 'BTN_START', ... | |
'String', 'START',... | |
'Position', [20 20 100 40], ... | |
'Callback', @click_button_start); | |
ud.btn_stop = uicontrol('Style', 'pushbutton', 'Tag', 'BTN_STOP', ... | |
'String', 'STOP',... | |
'Position', [140 20 100 40], ... | |
'Callback', @click_button_stop); | |
if ~isOctave() | |
set(gcf, 'WindowButtonMotionFcn', @mouse_move); % Avoid tracing mousemoves on Octave. | |
else | |
set(gcf, 'WindowButtonDownFcn', @mouse_move); | |
end | |
%% Save variables for another GUI threads | |
guidata(gcf, ud); | |
end | |
function save_segment(source, event) | |
ud = guidata(source); | |
varname = ud.output_varname; | |
ud.segments{end+1} = ud.signals(ud.curX:ud.curX+ud.segment_length-1, 1); | |
guidata(source, ud); | |
% Attach variables in 'global' workspaces | |
assignin('base', 'TMP_segment', ud.segments); | |
evalin('base', [varname ' = TMP_segment; clear TMP_segment;']); | |
% Display message | |
Fs = ud.sampling_rate; pt_b = ud.curX; pt_e = ud.curX+ud.segment_length-1; | |
str_1 = ['save samples in "' varname '{' num2str(length(ud.segments)) '}"'] | |
str_2 = [' [Time: ' num2str(double(pt_b)/Fs, '%.3f') '-' num2str(double(pt_e)/Fs, '%.3f') ' s' ]; | |
str_3 = [' (' num2str(pt_b) '-' num2str(pt_e) ' pt)]' ]; | |
disp([str_1 str_2 str_3]); | |
end | |
function mouse_move(source, event) | |
% Ignore right click: ('alt' == right click) | |
if strcmp('alt', get(gcf, 'SelectionType')) | |
return; | |
end | |
ud = guidata(source); | |
C = get (ud.ax, 'CurrentPoint'); | |
% Calculate line that position the mouse cursor as center of the lines | |
x1 = int32(C(1,1)-0.5*ud.segment_length); % cf. y = C(1,2) | |
x1 = min(max(x1, 0), ud.max_length-ud.segment_length); | |
x2 = x1 + ud.segment_length - 1; | |
% Delete current(old) line | |
if ~isempty(ud.curLine) | |
delete(ud.curLine); | |
end | |
% Append new line | |
X = [x1 x1; x2 x2]'; | |
Y = [-1 1; -1 1]'; | |
h = line(X, Y, 'LineWidth', 2); | |
title(ud.ax, ['X = (', num2str(x1), ', ',num2str(x2), ')']) | |
drawnow expose; | |
ud.curX = x1; | |
ud.curLine = h; | |
guidata(source, ud); | |
end | |
function click_button_start(source, event) | |
ud = guidata(source); | |
Fs = ud.sampling_rate; | |
nBits = 16; | |
nChannels = 1; | |
audio_obj = audiorecorder(Fs, nBits, nChannels); | |
if ~isOctave() | |
audio_obj.TimerFcn = {@callback_audiorecorded, source.Parent}; | |
end | |
record(audio_obj); | |
ud.audio_obj = audio_obj; | |
guidata(source, ud); | |
if isOctave() | |
OCTAVE_audio_loop(source); | |
end | |
end | |
function OCTAVE_audio_loop(source) | |
ud = guidata(source); | |
while isrecording(ud.audio_obj) | |
callback_audiorecorded(ud.audio_obj, [], source); | |
pause(0.2); | |
drawnow; | |
end | |
end | |
function click_button_stop(source, event) | |
ud = guidata(source); | |
stop(ud.audio_obj); | |
end | |
function callback_audiorecorded(src_audiorec, event, hFigure) | |
% Fetch audio samples | |
% If there are no samples, `getaudiodata` will raise error. | |
try | |
x = getaudiodata(src_audiorec); | |
catch | |
pause(0.01); | |
return ; | |
end | |
% Update waveform samples | |
ud = guidata(hFigure); | |
if length(x) < ud.max_length | |
ud.signals = x; | |
else | |
ud.signals = x((end-ud.max_length):end, 1); | |
end | |
guidata(hFigure, ud); | |
% Update waveform plot | |
set(ud.waveplot, 'YData', ud.signals); | |
drawnow expose; | |
end | |
function setup_waveform_figure(hAxes, max_length) | |
lower_level_indicator = 0.4; | |
upper_level_indicator = 0.8; | |
xlim(hAxes, [0 max_length]); | |
ylim(hAxes, [-1 1]); % The signal is normalized. | |
ylabel('Signal value'); | |
xlabel('Time index'); | |
line(hAxes, [0 max_length], [lower_level_indicator lower_level_indicator], 'Color', 'blue', 'LineWidth', 1, 'LineStyle', ':'); | |
line(hAxes, [0 max_length], [-lower_level_indicator -lower_level_indicator], 'Color', 'blue', 'LineWidth', 1, 'LineStyle', ':'); | |
line(hAxes, [0 max_length], [upper_level_indicator upper_level_indicator], 'Color', 'red', 'LineWidth', 1, 'LineStyle', '--'); | |
line(hAxes, [0 max_length], [-upper_level_indicator -upper_level_indicator], 'Color', 'red', 'LineWidth', 1, 'LineStyle', '--'); | |
grid(hAxes, 'on'); | |
end | |
function isOctave = isOctave() | |
isOctave = exist('OCTAVE_VERSION', 'builtin') > 0; | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment