Skip to content

Commit

Permalink
Updates to adding sounds together
Browse files Browse the repository at this point in the history
  • Loading branch information
jpeelle committed Dec 23, 2014
1 parent e63e320 commit 5e750d7
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 2 deletions.
2 changes: 1 addition & 1 deletion jp_addnoise.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function jp_addnoise(soundfiles, cfg)
%
% jp_addnoise(inDir, cfg);
%
% From https://github.com/jpeelle/jp_matlab
% From https://github.com/jpeelle/jp_matlab


if ~isfield(cfg, 'prestim') || isempty(cfg.prestim)
Expand Down
2 changes: 1 addition & 1 deletion jp_addsounds.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function y3 = jp_addsounds(y1, y2, fs, cfg)
% Add sounds
%JP_ADDSOUNDS Add two sounds (one target, one distractor)
%
% y1 target
% y2 distractor
Expand Down
24 changes: 24 additions & 0 deletions jp_crossfade.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
function [y, fs] = jp_crossfade(y1, y2, fs, rampSec)
%JP_CROSSFADE Very basic crossfade for two sounds.
%
% JP_CROSSFADE(Y1, Y2, FS) fades sound Y1 to sound Y2 using a linear ramp.
%
% JP_CROSSFADE(Y1, Y2, FS, RAMPSEC) uses a specified ramp duration (default
% is 0.1 seconds).
%
% From https://github.com/jpeelle/jp_matlab

if nargin < 4 || isempty(rampSec)
rampSec = 0.1;
end

rampSamples = rampSec*fs;
fadeIn = [1:rampSamples]'/rampSamples;
fadeOut = flipud(fadeIn);

y1(end-rampSamples+1:end) = y1(end-rampSamples+1:end) .* fadeOut;
y2(1:rampSamples) = y2(1:rampSamples) .* fadeIn;

y = zeros(length(y1)+length(y2)-rampSamples, 1);
y(1:length(y1)) = y1;
y(length(y1)+1-rampSamples:end) = y(length(y1)+1-rampSamples:end) + y2;
3 changes: 3 additions & 0 deletions jp_mag2db.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function dB = jp_mag2db(y)
%JP_MAG2DB Convert magnitude (e.g., RMS) to dB
dB = 20*log10(y);
110 changes: 110 additions & 0 deletions jp_stringtogethersounds.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
function [y, fs] = jp_stringtogethersounds(files, Cfg)
%JP_STRINGTOGETHERSOUNDS Make one long sound file from shorter ones.
%
% JP_STRINGTOGETHERSOUNDS(FILES) takes individual sound files and puts
% them togehter into a long sound (e.g., for presenting during an
% experiment). FILES is assumed to be a cell array of sound file names (all
% with the same sampling rate).
%
% Cfg has the following fields to set options:
%
% pauseBetweenSec Pause between the end of one sound and the beginning of the next (sec) (default 2)
% noiseFile If not empty, add consistent noise throughout (will concatenate if too short)
% SNR SNR to use for noise
% noisePreSoundSec How long noise plays before sounds start
% noisePostSoundSec How long noise plays after sounds stop
%
%
% From https://github.com/jpeelle/jp_matlab

if nargin < 2
Cfg = [];
end

if ~isfield(Cfg, 'pauseBetweenSec') || isempty(Cfg.pauseBetweenSec)
Cfg.pauseBetweenSec = 2;
end

if ~isfield(Cfg, 'noiseFile')
Cfg.noiseFile = '';
end

if ~isfield(Cfg, 'noisePreSoundSec') || isempty(Cfg.noisePreSoundSec)
Cfg.noisePreSoundSec = 1;
end


if ~isfield(Cfg, 'noisePostSoundSec') || isempty(Cfg.noisePostSoundSec)
Cfg.noisePostSoundSec = 1;
end

% Read in the first file just to get the sampling rate to make sure this is
% consistent across everything
[y, fs] = audioread(files{1});

% If we are using noise, read it in
if ~isempty(Cfg.noiseFile)
assert(exist(Cfg.noiseFile, 'file')>0, 'Specified noise file %s not found.', Cfg.noiseFile);
[yNoise, fsNoise] = audioread(Cfg.noiseFile);
assert(fsNoise==fs, 'Sampling rate of the noise (%i) does not match the sounds (%i).', fsNoise, fs);
end

% Read in sounds but don't do anything
for fileInd = 1:length(files)
thisFile = files{fileInd};
[ySounds{fileInd}, fsSound] = audioread(thisFile);
assert(fsSound==fs, 'Sampling rate of sound %s (%i) does not match that of the first sound (%i).', thisFile, fsSound, fs);
end


% String together the sounds
pauseSamp = zeros(Cfg.pauseBetweenSec * fs, 1);

y = [];
for soundInd = 1:length(ySounds)
y = [y; ySounds{soundInd}; pauseSamp];
end

% If we are adding noise, do that
if ~isempty(Cfg.noiseFile)
% Make sure the noise is long enough. If not, repeat it.
while length(yNoise) < length(y)
warning('The noise file was not long enough to cover all the sounds; repeating it with a little bit of crossfade.');
yNoise = jp_crossfade(yNoise, yNoise, fs, 0.1);
end

% If the noise is TOO long, trim it. (Doing so now helps make sure that
% the SNR calculations only take into account the important parts of
% the noise.
noisePreSoundSamples = Cfg.noisePreSoundSec*fs;
noisePostSoundSamples = Cfg.noisePostSoundSec*fs;
totalLengthSamples = length(y) + noisePreSoundSamples + noisePostSoundSamples;
yNoise = yNoise(1:totalLengthSamples);

% get level for noise file
rmsNoise = jp_rms(yNoise);
dbNoise = jp_mag2db(rmsNoise);

% get level for sounds
dbSignal = zeros(1,length(ySounds));
for soundInd = 1:length(ySounds)
dbSignal(soundInd) = jp_mag2db(jp_rms(ySounds{soundInd}));
end

dbSignalAvg = mean(dbSignal);

% Scale noise to get right SNR
targetDb = dbSignalAvg - Cfg.SNR; % target for noise dB
targetRMS = 10^(targetDb/20);
scaleFactor = targetRMS/rmsNoise;
yNoise = yNoise * scaleFactor;

y = [zeros(noisePreSoundSamples,1); y; zeros(noisePostSoundSamples,1)] + yNoise;
end


end % main function

% rms function


Loading

0 comments on commit 5e750d7

Please sign in to comment.