Code covered by the BSD License  

Highlights from
MIRtoolbox

image thumbnail
from MIRtoolbox by Olivier Lartillot
An innovative environment, on top of Matlab, for music and audio analysis

miraudio(orig,varargin)
function varargout = miraudio(orig,varargin)
%   a = miraudio('filename') loads the sound file 'filename' (in WAV or AU
%       format) into a miraudio object.
%   a = miraudio('Folder') loads all the sound files in the CURRENT folder
%       into a miraudio object.
%   a = miraudio(v,sr), where v is a column vector, translates the vector v
%       into a miraudio object. The sampling frequency is set to sr Hertz.
%           Default value for sr: 44100 Hz.
%   a = miraudio(b, ...), where b is already a miraudio object, performs 
%       operations on b specified by the optional arguments (see below).
%
%   Transformation options:
%       miraudio(...,'Mono',0) does not perform the default summing of
%           channels into one single mono track, but instead stores each 
%           channel of the initial soundfile separately.       
%       miraudio(...,'Center') centers the signals.
%       miraudio(...,'Sampling',r) resamples at sampling rate r (in Hz).
%           (Requires the Signal Processing Toolbox.)
%       miraudio(...,'Normal') normalizes with respect to RMS energy.
%   Extraction options:
%       miraudio(...,'Extract',t1,t2,u,f) extracts the signal between dates
%           t1 and t2, expressed in the unit u.
%           Possible values for u:
%               's' (seconds, by default),
%               'sp' (sample index, starting from 1).
%           The additional optional argument f indicates the referential
%               origin of the temporal positions. Possible values for f:
%               'Start' (by default)
%               'Middle' (of the sequence)
%               'End' of the sequence
%               When using 'Middle' or 'End', negative values for t1 or t2
%               indicate values before the middle or the end of the audio
%               sequence.
%       miraudio(...,'Trim') trims the pseudo-silence beginning and end off
%           the audio file. Silent frames are frames with RMS below t times
%           the medium RMS of the whole audio file.
%               Default value: t = 0.06
%           instead of 'Trim':
%              'TrimStart' only trims the beginning of the audio file,
%              'TrimEnd' only trims the end.
%           miraudio(...,'TrimThreshold',t) specifies the trimming threshold t.
%       miraudio(...,'Channel',c) or miraudio(...,'Channels',c) selects the
%           channels indicated by the (array of) integer(s) c.
%   Labeling option:
%       miraudio(...,'Label',l) labels the audio signal(s) following the 
%           label(s) l.
%           If l is a (series of) number(s), the audio signal(s) are
%           labelled using the substring of their respective file name of 
%           index l. If l=0, the audio signal(s) are labelled using the
%           whole file name.


if isnumeric(orig)
    if size(orig,2) > 1 || size(orig,3) > 1
        mirerror('MIRAUDIO','Only column vectors can be imported into mirtoolbox.');
    end
    if nargin == 1
        f = 44100;
    else
        f = varargin{1};
    end
    b = 32;
    if size(orig,1) == 1
        orig = orig';
    end
    tp = (0:size(orig,1)-1)'/f;
    l = (size(orig,1)-1)/f;
    t = mirtemporal([],'Time',{{tp}},'Data',{{orig}},'Length',{{l}},...
                    'FramePos',{{tp([1 end])}},'Sampling',{f},...
                    'Name',{inputname(1)},'Label',{{}},'Clusters',{{}},...
                    'Channels',[],'Centered',0,'NBits',{b},...
                    'Title','Audio signal',...
                    'PeakPos',{{{}}},'PeakVal',{{{}}},'PeakMode',{{{}}});
    aa.fresh = 1;
    aa.extracted = 0;
    varargout = {class(aa,'miraudio',t)};
    return
end


        center.key = 'Center';
        center.type = 'Boolean';
        center.default = 0;
        center.when = 'After';
    option.center = center;
        
        normal.key = 'Normal';
        normal.type = 'Boolean';
        normal.default = 0;
        normal.when = 'After';
    option.normal = normal;
    
        extract.key = {'Extract','Excerpt'};
        extract.type = 'Integer';
        extract.number = 2;
        extract.default = [];
        extract.unit = {'s','sp'};
        extract.defaultunit = 's';
        extract.from = {'Start','Middle','End'};
        extract.defaultfrom = 'Start';
    option.extract = extract;
        
        trim.type = 'String';
        trim.choice = {'NoTrim','Trim','TrimBegin','TrimStart','TrimEnd'};
        trim.default = 'NoTrim';
        trim.when = 'After';
    option.trim = trim;
        
        trimthreshold.key = 'TrimThreshold';
        trimthreshold.type = 'Integer';
        trimthreshold.default = .06;
        trimthreshold.when = 'After';
    option.trimthreshold = trimthreshold;
        
        label.key = 'Label';
        label.default = '';
        label.when = 'After';
    option.label = label;
        
        sampling.key = 'Sampling';
        sampling.type = 'Integer';
        sampling.default = 0;
        sampling.when = 'Both';
    option.sampling = sampling;
        
   %     segment.key = 'Segment';
   %     segment.type = 'Integer';
   %     segment.default = [];
   %     segment.when = 'After';
   % option.segment = segment;

        reverse.key = 'Reverse';
        reverse.type = 'Boolean';
        reverse.default = 0;
        reverse.when = 'After';
    option.reverse = reverse;

        mono.key = 'Mono';
        mono.type = 'Boolean';
        mono.default = NaN;
        mono.when = 'After';
    option.mono = mono;    

        separate.key = 'SeparateChannels';
        separate.type = 'Boolean';
        separate.default = 0;
    option.separate = separate;    
    
        Ch.key = {'Channel','Channels'};
        Ch.type = 'Integer';
        Ch.default = [];
        Ch.when = 'After';
    option.Ch = Ch;
        
specif.option = option;

specif.beforechunk = {@beforechunk,'normal'};
specif.eachchunk = @eachchunk;
specif.combinechunk = @combinechunk;

if nargin > 1 && ischar(varargin{1}) && strcmp(varargin{1},'Now')
    if nargin > 2
        extract = varargin{2};
    else
        extract = [];
    end
    para = [];
    varargout = {main(orig,[],para,[],extract)};
else
    varargout = mirfunction(@miraudio,orig,varargin,nargout,specif,@init,@main);
end
if isempty(varargout)
    varargout = {{}};
end


function [x type] = init(x,option)
if isa(x,'mirdesign')
    if option.sampling
        x = setresampling(x,option.sampling);
    end
end
type = 'miraudio';


function a = main(orig,option,after,index,extract)
if iscell(orig)
    orig = orig{1};
end
if ischar(orig)
    if nargin < 5
        extract = [];
    end
    [d{1},tp{1},fp{1},f{1},l{1},b{1},n{1},ch{1}] = mirread(extract,orig,1,0);
    t = mirtemporal([],'Time',tp,'Data',d,'FramePos',fp,'Sampling',f,...
                       'Name',n,'Label',cell(1,length(d)),...
                       'Clusters',cell(1,length(d)),'Length',l,...
                       'Channels',ch,'Centered',0,'NBits',b);
    t = set(t,'Title','Audio waveform');
    a.fresh = 1;
    a.extracted = 1;
    a = class(a,'miraudio',t);
else
    if not(isempty(option)) && not(isempty(option.extract))
        if not(isstruct(after))
            after = struct;
        end
        after.extract = option.extract;
    end
    if isa(orig,'miraudio')
        a = orig;
    else
        a.fresh = 1;
        a.extracted = 0;
        a = class(a,'miraudio',orig);
    end
end      
if not(isempty(after))
    a = post(a,after);
end    


function a = post(a,para)
if a.fresh && isfield(para,'mono')
    a.fresh = 0;
    if isnan(para.mono)
        para.mono = 1;
    end
end
if isfield(para,'mono') && para.mono == 1
    a = mirsum(a,'Mean');
end
d = get(a,'Data');
t = get(a,'Time');
ac = get(a,'AcrossChunks');
f = get(a,'Sampling');
cl = get(a,'Clusters');
for h = 1:length(d)
    for k = 1:length(d{h})
        tk = t{h}{k};
        dk = d{h}{k};
        if isfield(para,'extract') && not(isempty(para.extract)) ...
                && ~a.extracted
            t1 = para.extract(1);
            t2 = para.extract(2);
            if para.extract(4)
                if para.extract(4) == 1
                    shift = round(size(tk,1)/2);
                elseif para.extract(4) == 2
                    shift = size(tk,1);
                end
                if para.extract(3)
                    shift = tk(shift,1,1);
                end
                t1 = t1+shift;
                t2 = t2+shift;
            end                
            if para.extract(3) % in seconds
                ft = find(tk>=t1 & tk<=t2);
            else               % in samples
                if not(t1)
                    warning('WARNING IN MIRAUDIO: Extract sample positions should be real positive integers.')
                    display('Positions incremented by one.');
                    t1 = t1+1;
                    t2 = t2+1;
                end
                ft = t1:t2;
            end
            tk = tk(ft,:,:);
            dk = dk(ft,:,:);
        end
        if isfield(para,'Ch') && not(isempty(para.Ch))
            dk = dk(:,:,para.Ch);
        end
        if isfield(para,'center') && para.center
            dk = center(dk);
            a = set(a,'Centered',1);
        end
        if isfield(para,'normal') && para.normal
            nl = size(dk,1);
            nc = size(dk,3);
            if isempty(ac)
                ee = 0;
                for j = 1:nc
                    ee = ee+sum(dk(:,:,j).^2);
                end
                ee = sqrt(ee/nl/nc);
            else
                ee = sqrt(sum(ac.sqrsum.^2)/ac.samples);
            end
            if ee
                dk = dk./repmat(ee,[nl,1,nc]);
            end
        end
        if isfield(para,'trim') && not(isequal(para.trim,0)) ...
                && not(strcmpi(para.trim,'NoTrim'))
            if not(para.trimthreshold)
                para.trimthreshold = 0.06;
            end
            trimframe = 100;
            trimhop = 10;
            nframes = floor((length(tk)-trimframe)/trimhop)+1;
            rms = zeros(1,nframes);
            for j = 1:nframes
                st = floor((j-1)*trimhop)+1;
                for z = 1:size(dk,3)
                    rms(1,j,z) = norm(dk(st:st+trimframe-1,1,z))/sqrt(trimframe);
                end
            end
            rms = (rms-repmat(min(rms),[1,size(rms,2),1]))...
                     ./repmat(max(rms)-min(rms),[1,size(rms,2),1]);
            nosil = find(rms>para.trimthreshold);
            if strcmpi(para.trim,'Trim') || strcmpi(para.trim,'TrimStart') ...
                                         || strcmpi(para.trim,'TrimBegin')
                nosil1 = min(nosil);
                if nosil1 > 1
                    nosil1 = nosil1-1;
                end
                n1 = floor((nosil1-1)*trimhop)+1;
            else
                n1 = 1;
            end
            if strcmpi(para.trim,'Trim') || strcmpi(para.trim,'TrimEnd')
                nosil2 = max(nosil);
                if nosil2 < length(rms)
                    nosil2 = nosil2+1;
                end
                n2 = floor((nosil2-1)*trimhop)+1;
            else
                n2 = length(tk);
            end
            tk = tk(n1:n2);
            dk = dk(n1:n2,1,:);
        end
        if isfield(para,'sampling') && para.sampling
            if and(f{k}, not(f{k} == para.sampling))
                for j = 1:size(dk,3)
                    rk(:,:,j) = resample(dk(:,:,j),para.sampling,f{k});
                end
                dk = rk;
                tk = repmat((0:size(dk,1)-1)',[1 1 size(tk,3)])...
                            /para.sampling + tk(1,:,:);
            end
            f{k} = para.sampling;
        end
        d{h}{k} = dk;
        t{h}{k} = tk;
        %if isfield(para,'reverse') && para.reverse
        %    d{h}{k} = flipdim(d{h}{k},1);
        %end
    end
end
a = set(a,'Data',d,'Time',t,'Sampling',f,'Clusters',cl);
a = set(a,'Extracted',0);
if isfield(para,'label') 
    if isnumeric(para.label)
        n = get(a,'Name');
        l = cell(1,length(d));
        for k = 1:length(d)
            if para.label
                l{k} = n{k}(para.label);
            else
                l{k} = n{k};
            end
        end
        a = set(a,'Label',l);
    elseif iscell(para.label)
        idx = mod(get(a,'Index'),length(para.label));
        if not(idx)
            idx = length(para.label);
        end
        a = set(a,'Label',para.label{idx});
    elseif ischar(para.label) && ~isempty(para.label)
        l = cell(1,length(d));
        for k = 1:length(d)
            l{k} = para.label;
        end
        a = set(a,'Label',l);
    end
end


function [new orig] = beforechunk(orig,option,missing)
option.normal = 0;
a = miraudio(orig,option);
d = get(a,'Data');
old = get(orig,'AcrossChunks');
if isempty(old)
    old.sqrsum = 0;
    old.samples = 0;
end
new = mircompute(@crossum,d);
new = new{1}{1};
new.sqrsum = old.sqrsum + new.sqrsum;
new.samples = old.samples + new.samples;


function s = crossum(d)
s.sqrsum = sum(d.^2);
s.samples = length(d);


function [y orig] = eachchunk(orig,option,missing)
y = miraudio(orig,option);


function y = combinechunk(old,new)
do = get(old,'Data');
to = get(old,'Time');
dn = get(new,'Data');
tn = get(new,'Time');
y = set(old,'Data',{{[do{1}{1};dn{1}{1}]}},...
            'Time',{{[to{1}{1};tn{1}{1}]}});

Contact us at files@mathworks.com