wolffd@0: function varargout = miraudio(orig,varargin) wolffd@0: % a = miraudio('filename') loads the sound file 'filename' (in WAV or AU wolffd@0: % format) into a miraudio object. wolffd@0: % a = miraudio('Folder') loads all the sound files in the CURRENT folder wolffd@0: % into a miraudio object. wolffd@0: % a = miraudio(v,sr), where v is a column vector, translates the vector v wolffd@0: % into a miraudio object. The sampling frequency is set to sr Hertz. wolffd@0: % Default value for sr: 44100 Hz. wolffd@0: % a = miraudio(b, ...), where b is already a miraudio object, performs wolffd@0: % operations on b specified by the optional arguments (see below). wolffd@0: % wolffd@0: % Transformation options: wolffd@0: % miraudio(...,'Mono',0) does not perform the default summing of wolffd@0: % channels into one single mono track, but instead stores each wolffd@0: % channel of the initial soundfile separately. wolffd@0: % miraudio(...,'Center') centers the signals. wolffd@0: % miraudio(...,'Sampling',r) resamples at sampling rate r (in Hz). wolffd@0: % (Requires the Signal Processing Toolbox.) wolffd@0: % miraudio(...,'Normal') normalizes with respect to RMS energy. wolffd@0: % Extraction options: wolffd@0: % miraudio(...,'Extract',t1,t2,u,f) extracts the signal between dates wolffd@0: % t1 and t2, expressed in the unit u. wolffd@0: % Possible values for u: wolffd@0: % 's' (seconds, by default), wolffd@0: % 'sp' (sample index, starting from 1). wolffd@0: % The additional optional argument f indicates the referential wolffd@0: % origin of the temporal positions. Possible values for f: wolffd@0: % 'Start' (by default) wolffd@0: % 'Middle' (of the sequence) wolffd@0: % 'End' of the sequence wolffd@0: % When using 'Middle' or 'End', negative values for t1 or t2 wolffd@0: % indicate values before the middle or the end of the audio wolffd@0: % sequence. wolffd@0: % miraudio(...,'Trim') trims the pseudo-silence beginning and end off wolffd@0: % the audio file. Silent frames are frames with RMS below t times wolffd@0: % the medium RMS of the whole audio file. wolffd@0: % Default value: t = 0.06 wolffd@0: % instead of 'Trim': wolffd@0: % 'TrimStart' only trims the beginning of the audio file, wolffd@0: % 'TrimEnd' only trims the end. wolffd@0: % miraudio(...,'TrimThreshold',t) specifies the trimming threshold t. wolffd@0: % miraudio(...,'Channel',c) or miraudio(...,'Channels',c) selects the wolffd@0: % channels indicated by the (array of) integer(s) c. wolffd@0: % Labeling option: wolffd@0: % miraudio(...,'Label',l) labels the audio signal(s) following the wolffd@0: % label(s) l. wolffd@0: % If l is a (series of) number(s), the audio signal(s) are wolffd@0: % labelled using the substring of their respective file name of wolffd@0: % index l. If l=0, the audio signal(s) are labelled using the wolffd@0: % whole file name. wolffd@0: wolffd@0: wolffd@0: if isnumeric(orig) wolffd@0: if size(orig,2) > 1 || size(orig,3) > 1 wolffd@0: mirerror('MIRAUDIO','Only column vectors can be imported into mirtoolbox.'); wolffd@0: end wolffd@0: if nargin == 1 wolffd@0: f = 44100; wolffd@0: else wolffd@0: f = varargin{1}; wolffd@0: end wolffd@0: b = 32; wolffd@0: if size(orig,1) == 1 wolffd@0: orig = orig'; wolffd@0: end wolffd@0: tp = (0:size(orig,1)-1)'/f; wolffd@0: t = mirtemporal([],'Time',{{tp}},'Data',{{orig}},... wolffd@0: 'FramePos',{{tp([1 end])}},'Sampling',{f},... wolffd@0: 'Name',{inputname(1)},'Label',{{}},'Clusters',{{}},... wolffd@0: 'Channels',[],'Centered',0,'NBits',{b},... wolffd@0: 'Title','Audio signal',... wolffd@0: 'PeakPos',{{{}}},'PeakVal',{{{}}},'PeakMode',{{{}}}); wolffd@0: aa.fresh = 1; wolffd@0: varargout = {class(aa,'miraudio',t)}; wolffd@0: return wolffd@0: end wolffd@0: wolffd@0: wolffd@0: center.key = 'Center'; wolffd@0: center.type = 'Boolean'; wolffd@0: center.default = 0; wolffd@0: center.when = 'After'; wolffd@0: option.center = center; wolffd@0: wolffd@0: normal.key = 'Normal'; wolffd@0: normal.type = 'Boolean'; wolffd@0: normal.default = 0; wolffd@0: normal.when = 'After'; wolffd@0: option.normal = normal; wolffd@0: wolffd@0: extract.key = {'Extract','Excerpt'}; wolffd@0: extract.type = 'Integer'; wolffd@0: extract.number = 2; wolffd@0: extract.default = []; wolffd@0: extract.unit = {'s','sp'}; wolffd@0: extract.defaultunit = 's'; wolffd@0: extract.from = {'Start','Middle','End'}; wolffd@0: extract.defaultfrom = 'Start'; wolffd@0: option.extract = extract; wolffd@0: wolffd@0: trim.type = 'String'; wolffd@0: trim.choice = {'NoTrim','Trim','TrimBegin','TrimStart','TrimEnd'}; wolffd@0: trim.default = 'NoTrim'; wolffd@0: trim.when = 'After'; wolffd@0: option.trim = trim; wolffd@0: wolffd@0: trimthreshold.key = 'TrimThreshold'; wolffd@0: trimthreshold.type = 'Integer'; wolffd@0: trimthreshold.default = .06; wolffd@0: trimthreshold.when = 'After'; wolffd@0: option.trimthreshold = trimthreshold; wolffd@0: wolffd@0: label.key = 'Label'; wolffd@0: label.default = ''; wolffd@0: label.when = 'After'; wolffd@0: option.label = label; wolffd@0: wolffd@0: sampling.key = 'Sampling'; wolffd@0: sampling.type = 'Integer'; wolffd@0: sampling.default = 0; wolffd@0: sampling.when = 'Both'; wolffd@0: option.sampling = sampling; wolffd@0: wolffd@0: % segment.key = 'Segment'; wolffd@0: % segment.type = 'Integer'; wolffd@0: % segment.default = []; wolffd@0: % segment.when = 'After'; wolffd@0: % option.segment = segment; wolffd@0: wolffd@0: reverse.key = 'Reverse'; wolffd@0: reverse.type = 'Boolean'; wolffd@0: reverse.default = 0; wolffd@0: reverse.when = 'After'; wolffd@0: option.reverse = reverse; wolffd@0: wolffd@0: mono.key = 'Mono'; wolffd@0: mono.type = 'Boolean'; wolffd@0: mono.default = NaN; wolffd@0: mono.when = 'After'; wolffd@0: option.mono = mono; wolffd@0: wolffd@0: separate.key = 'SeparateChannels'; wolffd@0: separate.type = 'Boolean'; wolffd@0: separate.default = 0; wolffd@0: option.separate = separate; wolffd@0: wolffd@0: Ch.key = {'Channel','Channels'}; wolffd@0: Ch.type = 'Integer'; wolffd@0: Ch.default = []; wolffd@0: Ch.when = 'After'; wolffd@0: option.Ch = Ch; wolffd@0: wolffd@0: specif.option = option; wolffd@0: wolffd@0: specif.beforechunk = {@beforechunk,'normal'}; wolffd@0: specif.eachchunk = @eachchunk; wolffd@0: specif.combinechunk = @combinechunk; wolffd@0: wolffd@0: if nargin > 1 && ischar(varargin{1}) && strcmp(varargin{1},'Now') wolffd@0: if nargin > 2 wolffd@0: extract = varargin{2}; wolffd@0: else wolffd@0: extract = []; wolffd@0: end wolffd@0: para = []; wolffd@0: varargout = {main(orig,[],para,[],extract)}; wolffd@0: else wolffd@0: varargout = mirfunction(@miraudio,orig,varargin,nargout,specif,@init,@main); wolffd@0: end wolffd@0: if isempty(varargout) wolffd@0: varargout = {{}}; wolffd@0: end wolffd@0: wolffd@0: wolffd@0: function [x type] = init(x,option) wolffd@0: if isa(x,'mirdesign') wolffd@0: if option.sampling wolffd@0: x = setresampling(x,option.sampling); wolffd@0: end wolffd@0: end wolffd@0: type = 'miraudio'; wolffd@0: wolffd@0: wolffd@0: function a = main(orig,option,after,index,extract) wolffd@0: if iscell(orig) wolffd@0: orig = orig{1}; wolffd@0: end wolffd@0: if ischar(orig) wolffd@0: if nargin < 5 wolffd@0: extract = []; wolffd@0: end wolffd@0: [d{1},tp{1},fp{1},f{1},b{1},n{1},ch{1}] = mirread(extract,orig,1,0); wolffd@0: t = mirtemporal([],'Time',tp,'Data',d,'FramePos',fp,'Sampling',f,... wolffd@0: 'Name',n,'Label',cell(1,length(d)),... wolffd@0: 'Clusters',cell(1,length(d)),... wolffd@0: 'Channels',ch,'Centered',0,'NBits',b); wolffd@0: t = set(t,'Title','Audio waveform'); wolffd@0: a.fresh = 1; wolffd@0: a = class(a,'miraudio',t); wolffd@0: else wolffd@0: if not(isempty(option)) && not(isempty(option.extract)) wolffd@0: if not(isstruct(after)) wolffd@0: after = struct; wolffd@0: end wolffd@0: after.extract = option.extract; wolffd@0: end wolffd@0: if isa(orig,'miraudio') wolffd@0: a = orig; wolffd@0: else wolffd@0: a.fresh = 1; wolffd@0: a = class(a,'miraudio',orig); wolffd@0: end wolffd@0: end wolffd@0: if not(isempty(after)) wolffd@0: a = post(a,after); wolffd@0: end wolffd@0: wolffd@0: wolffd@0: function a = post(a,para) wolffd@0: if a.fresh && isfield(para,'mono') wolffd@0: a.fresh = 0; wolffd@0: if isnan(para.mono) wolffd@0: para.mono = 1; wolffd@0: end wolffd@0: end wolffd@0: if isfield(para,'mono') && para.mono == 1 wolffd@0: a = mirsum(a,'Mean'); wolffd@0: end wolffd@0: d = get(a,'Data'); wolffd@0: t = get(a,'Time'); wolffd@0: ac = get(a,'AcrossChunks'); wolffd@0: f = get(a,'Sampling'); wolffd@0: cl = get(a,'Clusters'); wolffd@0: for h = 1:length(d) wolffd@0: for k = 1:length(d{h}) wolffd@0: tk = t{h}{k}; wolffd@0: dk = d{h}{k}; wolffd@0: if isfield(para,'extract') && not(isempty(para.extract)) wolffd@0: t1 = para.extract(1); wolffd@0: t2 = para.extract(2); wolffd@0: if para.extract(4) wolffd@0: if para.extract(4) == 1 wolffd@0: shift = round(size(tk,1)/2); wolffd@0: elseif para.extract(4) == 2 wolffd@0: shift = size(tk,1); wolffd@0: end wolffd@0: if para.extract(3) wolffd@0: shift = tk(shift,1,1); wolffd@0: end wolffd@0: t1 = t1+shift; wolffd@0: t2 = t2+shift; wolffd@0: end wolffd@0: if para.extract(3) % in seconds wolffd@0: ft = find(tk>=t1 & tk<=t2); wolffd@0: else % in samples wolffd@0: if not(t1) wolffd@0: warning('WARNING IN MIRAUDIO: Extract sample positions should be real positive integers.') wolffd@0: display('Positions incremented by one.'); wolffd@0: t1 = t1+1; wolffd@0: t2 = t2+1; wolffd@0: end wolffd@0: ft = t1:t2; wolffd@0: end wolffd@0: tk = tk(ft,:,:); wolffd@0: dk = dk(ft,:,:); wolffd@0: end wolffd@0: if isfield(para,'Ch') && not(isempty(para.Ch)) wolffd@0: dk = dk(:,:,para.Ch); wolffd@0: end wolffd@0: if isfield(para,'center') && para.center wolffd@0: dk = center(dk); wolffd@0: a = set(a,'Centered',1); wolffd@0: end wolffd@0: if isfield(para,'normal') && para.normal wolffd@0: nl = size(dk,1); wolffd@0: nc = size(dk,3); wolffd@0: if isempty(ac) wolffd@0: ee = 0; wolffd@0: for j = 1:nc wolffd@0: ee = ee+sum(dk(:,:,j).^2); wolffd@0: end wolffd@0: ee = sqrt(ee/nl/nc); wolffd@0: else wolffd@0: ee = sqrt(sum(ac.sqrsum.^2)/ac.samples); wolffd@0: end wolffd@0: dk = dk./repmat(ee,[nl,1,nc]); wolffd@0: end wolffd@0: if isfield(para,'trim') && not(isequal(para.trim,0)) ... wolffd@0: && not(strcmpi(para.trim,'NoTrim')) wolffd@0: if not(para.trimthreshold) wolffd@0: para.trimthreshold = 0.06; wolffd@0: end wolffd@0: trimframe = 100; wolffd@0: trimhop = 10; wolffd@0: nframes = floor((length(tk)-trimframe)/trimhop)+1; wolffd@0: rms = zeros(1,nframes); wolffd@0: for j = 1:nframes wolffd@0: st = floor((j-1)*trimhop)+1; wolffd@0: for z = 1:size(dk,3) wolffd@0: rms(1,j,z) = norm(dk(st:st+trimframe-1,1,z))/sqrt(trimframe); wolffd@0: end wolffd@0: end wolffd@0: rms = (rms-repmat(min(rms),[1,size(rms,2),1]))... wolffd@0: ./repmat(max(rms)-min(rms),[1,size(rms,2),1]); wolffd@0: nosil = find(rms>para.trimthreshold); wolffd@0: if strcmpi(para.trim,'Trim') || strcmpi(para.trim,'TrimStart') ... wolffd@0: || strcmpi(para.trim,'TrimBegin') wolffd@0: nosil1 = min(nosil); wolffd@0: if nosil1 > 1 wolffd@0: nosil1 = nosil1-1; wolffd@0: end wolffd@0: n1 = floor((nosil1-1)*trimhop)+1; wolffd@0: else wolffd@0: n1 = 1; wolffd@0: end wolffd@0: if strcmpi(para.trim,'Trim') || strcmpi(para.trim,'TrimEnd') wolffd@0: nosil2 = max(nosil); wolffd@0: if nosil2 < length(rms) wolffd@0: nosil2 = nosil2+1; wolffd@0: end wolffd@0: n2 = floor((nosil2-1)*trimhop)+1; wolffd@0: else wolffd@0: n2 = length(tk); wolffd@0: end wolffd@0: wh = ones(n2-n1+1,1); wolffd@0: dt = round(.02*f{h}); wolffd@0: ha = hann(dt*2); wolffd@0: wh(1:dt) = ha(1:dt); wolffd@0: wh(end-dt+1:end) = ha(dt+1:end); wolffd@0: tk = tk(n1:n2); wolffd@0: dk = dk(n1:n2,1,:);%.*repmat(wh,[1 1 size(dk,3)]); wolffd@0: end wolffd@0: if isfield(para,'sampling') && para.sampling wolffd@0: if and(f{k}, not(f{k} == para.sampling)) wolffd@0: for j = 1:size(dk,3) wolffd@0: rk(:,:,j) = resample(dk(:,:,j),para.sampling,f{k}); wolffd@0: end wolffd@0: dk = rk; wolffd@0: tk = repmat((0:size(dk,1)-1)',[1 1 size(tk,3)])... wolffd@0: /para.sampling + tk(1,:,:); wolffd@0: end wolffd@0: f{k} = para.sampling; wolffd@0: end wolffd@0: d{h}{k} = dk; wolffd@0: t{h}{k} = tk; wolffd@0: %if isfield(para,'reverse') && para.reverse wolffd@0: % d{h}{k} = flipdim(d{h}{k},1); wolffd@0: %end wolffd@0: end wolffd@0: end wolffd@0: a = set(a,'Data',d,'Time',t,'Sampling',f,'Clusters',cl); wolffd@0: if isfield(para,'label') wolffd@0: if isnumeric(para.label) wolffd@0: n = get(a,'Name'); wolffd@0: l = cell(1,length(d)); wolffd@0: for k = 1:length(d) wolffd@0: if para.label wolffd@0: l{k} = n{k}(para.label); wolffd@0: else wolffd@0: l{k} = n{k}; wolffd@0: end wolffd@0: end wolffd@0: a = set(a,'Label',l); wolffd@0: elseif iscell(para.label) wolffd@0: idx = mod(get(a,'Index'),length(para.label)); wolffd@0: if not(idx) wolffd@0: idx = length(para.label); wolffd@0: end wolffd@0: a = set(a,'Label',para.label{idx}); wolffd@0: elseif ischar(para.label) wolffd@0: l = cell(1,length(d)); wolffd@0: for k = 1:length(d) wolffd@0: l{k} = para.label; wolffd@0: end wolffd@0: a = set(a,'Label',l); wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: wolffd@0: function [new orig] = beforechunk(orig,option,missing) wolffd@0: option.normal = 0; wolffd@0: a = miraudio(orig,option); wolffd@0: d = get(a,'Data'); wolffd@0: old = get(orig,'AcrossChunks'); wolffd@0: if isempty(old) wolffd@0: old.sqrsum = 0; wolffd@0: old.samples = 0; wolffd@0: end wolffd@0: new = mircompute(@crossum,d); wolffd@0: new = new{1}{1}; wolffd@0: new.sqrsum = old.sqrsum + new.sqrsum; wolffd@0: new.samples = old.samples + new.samples; wolffd@0: wolffd@0: wolffd@0: function s = crossum(d) wolffd@0: s.sqrsum = sum(d.^2); wolffd@0: s.samples = length(d); wolffd@0: wolffd@0: wolffd@0: function [y orig] = eachchunk(orig,option,missing) wolffd@0: y = miraudio(orig,option); wolffd@0: wolffd@0: wolffd@0: function y = combinechunk(old,new) wolffd@0: do = get(old,'Data'); wolffd@0: to = get(old,'Time'); wolffd@0: dn = get(new,'Data'); wolffd@0: tn = get(new,'Time'); wolffd@0: y = set(old,'Data',{{[do{1}{1};dn{1}{1}]}},... wolffd@0: 'Time',{{[to{1}{1};tn{1}{1}]}});