Skip to content

Instantly share code, notes, and snippets.

@naoh16
Last active November 4, 2020 10:25
Show Gist options
  • Save naoh16/dce0b5ad587f310d82c7b174f7b20756 to your computer and use it in GitHub Desktop.
Save naoh16/dce0b5ad587f310d82c7b174f7b20756 to your computer and use it in GitHub Desktop.
Audio Recording tool for MATLAB/Octave
%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.
% <sunao.hara@gmail.com>
%
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