Filtering a single sample in MATLAB - matlab

I'm attempting to create a reverb function in MATLAB and would like to filter the repeats to make them sound more natural. I have the signal processing toolbox however I get an error telling me that I cannot use the lowpass() function on single samples and MATLAB wigs out. The idea is to filter the signal every time it enters the feedback loop to simulate the absorption of high frequencies every time the sound reflects off a surface. My code looks like this:
[x,Fs] = audioread('Clap.mp3');
%Calculate delay length
secOfDelay = 0.03; %units of seconds
samplesOfDelay = secOfDelay * Fs; %sec * samples/sec = samples
%Gain for wet mix
g = 0.9;
for n = 1:length(x)
if (n - samplesOfDelay) < 1 %For no past samples, don't add the delay yet.
out(n,1) = x(n,1);
else
a(n,1) = x(n,1);
b(n,1) = x(n,1) + (g * b(n-samplesOfDelay,1));
end
end
%Add wet mix and dry mix together
out = a+b;
sound(out,Fs);
Any help would be greatly appreciated!
JDB

Related

Matlab sound with a function for a keyboard [duplicate]

Hello I have to build a program that is able to play some piano sounds from beethoven elise
Now I have implemented the function for the frequency and the sound but it sounds strange and not at all like piano notes.
Could someone give me advice on how to fix this?
createWaveform(440,8000,2,1);
function [sinusoid] = createWaveform(frequency,fs,duration,A)
n = linspace(0,duration-1/fs,duration*fs);
sinusoid = 1:duration*fs;
for i = 1:duration*fs
sinusoid(i) = A*cos(2*pi*frequency*n(i)*(1/fs)+(2*pi).*rand(1,1));
end
end
end
Here is the part where I am attempting to build tones to the right piano note
%function [tone] = note(keynum,relDuration,fullDuration,fs)
%basetone = 440;
% frequency = basetone * nthroot(2,12)^(keynum-49);
%[tone]=createWaveform(frequency,fs,relDuration*fullDuration,1);
%end
Edit: I've deleted all my old code and wrote the following that did the job
%sound(createWaveform(1000,8000,1,1));
sound(note(24,1/2,1,8000));
function [sinusoid] = createWaveform(frequency,fs,duration,A)
Fs = fs; % samples per second
dt = 1/Fs; % seconds per sample % seconds
t = (0:dt:duration)'; % seconds
%%Sine wave:
Fc = frequency; % hertz
sinusoid = A.*cos(2*pi*Fc*t+(2*pi).*rand(1,1));
% Plot the signal versus time:
end
function [tone] = note(keynum,relDuration,fullDuration,fs)
basetone = 440;
frequency = basetone * nthroot(2,12)^(keynum-49);
tone=createWaveform(frequency,fs,relDuration*fullDuration,0.5);
end
function [E] = envel(relDuration,fullDuration,fs)
param = 0;
for i = 0:fs:relDuration*fullDuration
E(i)=relDuration*fullDuration;
i=i+1;
end
end
As mentioned in the Comments, the sound that a piano makes is quite complicated. As such this is not a Matlab challenge, just a math problem.
Now that you know this, you 'just' need to come up with a reasonable mathematical approximation, and from here it should be straight forward to implement in Matlab.
Here is something to get you started with the math:
https://dsp.stackexchange.com/questions/46598/mathematical-equation-for-the-sound-wave-that-a-piano-makes

glitch when I try to crossfade between two sinusoidal signals back and forth in Matlab

I'm trying to create some functions in Matlab that is going to record a tone and then play it back semi-continuous. For example I want the user to be able to whistle for 2 seconds and the whistle to be dragged out to 10 seconds. I want to avoid using fancy time stretch algorithms but just use this through repetitions. I know there's issues with this but I'm doing this for experiment.
To do this, I need to crossfade the beginning and end of the sound to make it seem continuous. I have written a crossfade function that takes two signals and fades between them.
The idea is to have a sigmoid volume function that goes from 1 to 0 for the old sound and 0 to 1 for the new. I then just multiply the signal by this volume function and add the result. My understanding of the sigmoid function is that the resulting volume should be an average of the two input signals, with weight slowly moving from input signal a to input signal b, and the output amplitude should be the same as the input ones.
When I try this with two simple sinusoid input signals, however, and go back and forth between a low and high tone, I get a small "glitch" in the section just where it transfers from a low-to-high section, to a high-to-low sliding section. Where does this come from? I'm also not sure if this is only on my speakers, and I tried watching the plotted waveforms in time domain, but couldn't see anything weird happening at the specific time.
The end of the first section, and beginning of the last should have exactly the same frequency component, and nothing is moved in time, so I'm not sure where this glitch comes from.
Sample code:
(SLIGHT WARNING: This output some tones, one of which is a little bit high pitch, so maybe not play this with earphones on or with full volume).
function test()
Fs=44100;
x=1:1/Fs:2;
x=x'; %time vector
freq1 = 440; %low tone
freq2 = freq1*2^(7/12) % high tone (harmonic fifth)
Fs=44100;
x=1:1/Fs:2;
x=x'; %time vector
freq1 = 440; %low tone
freq2 = freq1*2^(7/12); % high tone (harmonic fifth)
y1 = sin(2*pi*x*freq1)
y2 = sin(2*pi*x*freq2)
res = stitchandrepeat(y1,y2, Fs);
end
function [out] = stitchandrepeat(in, in2, Fs)
lowtohigh = crossfade(in,in2,1);
hightolow = crossfade(in2,in,1);
out = [lowtohigh;hightolow];
out = [out;out];
out = [out;out];
out = [out;out];
end
function [out] = crossfade(a, b, sigmoid)
if not(size(a) == size(b))
error('Different sizes');
end
index = (1:size(a,1))';
factor = index/size(a,1);
if(sigmoid)
steepness = 5;
factor = logsig((factor-0.5)*steepness*2);
end
plot(index, factor, 'o');
out = bsxfun(#times, a, 1-factor)+bsxfun(#times, b, factor);
end
I even tried doing a second crossfade where I stitched together the end and beginning of the first and second signal, but it still sounded a little bit weird just in the transition. Any idea on how to make the transition smooth?
function [out] = stitchandrepeat2(in, in2, Fs)
lowtohigh = crossfade(in,in2,1);
hightolow = crossfade(in2,in,1);
secstofade = 0.1;
len = size(lowtohigh,1);
partsize = secstofade*Fs;
lthpart = lowtohigh((len-partsize+1):len,:);
htlpart = hightolow(1:partsize,:);
lthcropped = lowtohigh(1:len-partsize,:);
htlcropped = hightolow(partsize+1:len,:);
highfade = crossfade(htlpart, lthpart,1);
out = [lthcropped;highfade;htlcropped];
out = [out;out];
out = [out;out];
out = [out;out];
end
And how come I don't get this glitch sound in the low part of the sound? (Two different sound waves are also just concatenated there.)

adjusting swept signal equation

When I do a spectrogram in matlab / octave I can create a swept signal that looks like the RED plot line below. But how can I create a swept signal like the BLUE line in the 1st plot using the equation below.
thanks to Daniel and David for getting me this far with the code is below
startfreq=200;
fs=44100;
endfreq=20;
dursec= 10;%duration of signal in seconds
t=(0:dursec*fs)/fs; %Time vector
alpha=log(startfreq/endfreq)/dursec;
sig = exp(-j*2*pi*startfreq/alpha*exp(-alpha*t));
sig=(sig/max(abs(sig))*.8); %normalize signal
wavwrite([sig'] ,fs,32,strcat('/tmp/del.wav')); %export file
specgram(sig,150,400);
1st plot
2nd plot
How can I fix the the equation in the variable sig to get it to look like the BLUE line in the 1st plot?
3rd plot
This question is almost an month old, so you might have figured this out by now. Here's an answer in case you are still interested.
It appears that your current model for the frequency is
freq(t) = b*exp(-alpha*t)
with
freq(0) = b = startfreq
freq(dursec) = b*exp(-alpha*dursec) = endfreq
There are two free parameters (b and alpha), and two equations. The first equation, b = startfreq, gives us b (trivially).
Solving the last equation for alpha gives
alpha = -log(endfreq/startfreq)/dursec
= log(startfreq/endfreq)/dursec
So
freq(t) = startfreq * exp(-alpha*t)
To use this as the instantaneous frequency of a frequency-swept signal,
we need the integral, which I'll call phase(t):
phase(t) = -(startfreq/alpha) * exp(-alpha*t)
The (complex) frequency-swept signal is then
sig(t) = exp(2*pi*j * phase(t))
The real part of this signal is
sig(t) = cos(2*pi*phase(t))
That explains your current code. To generate a chirp whose frequency varies like the blue curve, you need a different model for the frequency. A more general model than the one used above is
freq(t) = a + b*exp(-alpha*t)
The requirements at t=0 and t=dursec are
freq(0) = a + b = startfreq
freq(dursec) = a + b*exp(-alpha*dursec) = endfreq
That's two equation, but we now have three parameters: a, b, and alpha. I'll use the two equations to determine a and b, and leave alpha as a free parameter. Solving gives
b = (startfreq - endfreq)/(1 - exp(-alpha*dursec))
a = startfreq - b
Integrating the model gives
phase(t) = a*t - (b/alpha)*exp(-alpha*t)
alpha is an arbitrary parameter. Following the formula from the first model, I'll use:
alpha = abs(log(startfreq/endfreq))/dursec
The following is a complete script. Note that I also changed the use of exp(-j*2*pi*...) to cos(2*pi*...). The factor 0.8 is there to match your code.
startfreq = 20;
endfreq = 200;
fs = 44100;
dursec = 10; % duration of signal in seconds
t = (0:dursec*fs)/fs; % Time vector
if (startfreq == endfreq)
phase = startfreq * t;
else
alpha = abs(log(endfreq/startfreq))/dursec;
b = (startfreq - endfreq)/(1 - exp(-alpha*dursec));
a = startfreq - b;
phase = a*t - (b/alpha)*exp(-alpha*t);
endif
sig = 0.8 * cos(2*pi*phase);
wavwrite([sig'] ,fs,32,strcat('del.wav')); % export file
specgram(sig,150,400);

How can I output a composite signal made of two signals with different time period at the same time on one audio channel in matlab code?

%-------------------------------------------------------------------
% Function to Generate ECG of 1 heart beat signal
function [Heartbeat,t1] = ECG_Gen (HR,pulse_width,Amp)
Fs = 48000;
delay = ((60/HR)/2)-(0.5*pulse_width);
t1 = -delay:(1/Fs):delay;
Heartbeat = Amp*tripuls (t1,pulse_width);
%-------------------------------------------------------------------
%Test Circuit configuration
function [FECG_MECG,Mixed_ECG,fastTime] = Test_Circuit (FHR,MHR)
Fs = 48000;
%FHR = 150;
%MHR = 60;
Fpulse_width = 30e-3;
Mpulse_width = 60e-3;
FAmp = 0.2;
MAmp = 0.5;
% Fetal ECG Gen
%------------------------------------------------
[FECG,FHR_Delay]= ECG_Gen (FHR,Fpulse_width,FAmp);
% Maternal ECG Gen
%------------------------------------------------
[MECG,MHR_Delay]= ECG_Gen (MHR,Mpulse_width,MAmp);
% Composite signal implementation
%------------------------------------------------
% Set parameters for Composite signal Algorithms
if length (MECG) > length (FECG) % Check for time sequences for both ECG signal
slowECG = FECG; % Set interpolation to slower rate
fastECG = MECG;
timeSeg = length(MECG);
fastTime = MHR_Delay; % Set sampling times
slowTime = FHR_Delay;
else
slowECG = MECG;
fastECG = FECG;
timeSeg = length(FECG);
fastTime = FHR_Delay;
slowTime = MHR_Delay;
end
FECG_MECG = zeros (timeSeg,2); % To hold stereo output
FECG_MECG(:,2) = fastECG(1,:); % Assign higher rate signal to one channel
% Interpolation on the slower rater sampled ECG
slowECGInterp = interp1 (slowTime,slowECG,fastTime);
slowECG = num2cell(slowECGInterp); % Conversion to cell Array in order to remove NaN
slowECG(cellfun(#(slowECG) any(isnan(slowECG)),slowECG)) = [];
slowECG = cell2mat(slowECG);
j = 1;
for i = 1:timeSeg
FECG_MECG(i,1) = slowECG(1,j);
if j == length(slowECG)
j = 0;
end
j = j+1;
end
Mixed_ECG = FECG_MECG(:,1) + FECG_MECG(:,2); % to hold mono output
%while (1)
%sound(Mixed_ECG ,Fs);
%end
%-------------------------------------------------------------------
% Test Wave script
clear all
%clc
clc
Fs = 48000;
%for i = 1:3
%toc
MHR = 60;
FHR = 200;
% Obtain ECG interpolated signal and composite
[FECG_MECG,Mixed_ECG,fastTime] = Test_Circuit (FHR,MHR);
% for test purposes
[MECG,MHR_Delay]= ECG_Gen (60,60e-3,0.5);
%t = timer ('TimerFcn','stat=false','Period',2.0);
wavwrite (FECG_MECG(:,2),Fs,'ECGwav.wav');
i = 0;
a = 1;
tic
while (1)
while (toc < 20*a)
sound (MECG,Fs);
end
toc
a = a+1;
[MECG,MHR_Delay]= ECG_Gen (60*a,60e-3,0.5);
if a > 4
break
end
end
%start(t)
%tic
%t = cputime;
%y = wavread('ECGwav.wav');
%while (1)
% sound(y,Fs);
%end
%toc
Hey Thank you very much for getting back to me, I have made use of your interpolation but still have minor problems from the reading obtained from the Monitor. Fist of all, say I have a constant signal with fixed time period say, 0.8s and I want to add composite signal of say 0.3s, I have managed to use your interpolation method to sample the 0.3s signal at the rate of my 0.8s signal. I think I have solved this issue. Second issue deals with how to combine the two signals which I have managed to do somehow but when I use a while loop in order to repeat the composite signal say over 20s, the signals obtained from the sound output isn't quite what I expected since its sounding the array of stored composite signal which contain ( signal with 0.8s = slowInterp signal of 0.3s ). I have include all the codes and functions. Basically, I need the sound output in while loop to sync with the composite signal for example: if I have a signal that repeats every 1s, I would expect to hear a beep in a while loop that runs for 10s to produce 10 beeps, etc
It is hard to tell exactly what you need, but probably doing interpolation on the sequence that is sampled at the slower rate would work for your application.
If t1s are your times from your faster sequence, and t2s are times from your slower sequence, and slow is your slower sequence then do:
slowInterp = interp1(t2s, slow, t1s);
now you will have the sequency slow sampled at the faster rate.
This is only useful for displaying the time series. If you are doing any spectral analysis, this will introduce artifacts, but that is a more advanced topic.
Using the resample function in the signal processing toolbox could also be useful.

Setting sampling rate in MATLAB for Arduino

I'm having trouble getting consistent results with the code I am using. I want to run my Arduino for a specific amount of time (say 20 seconds) and collect data from the analog pin with a specific sampling rate (say four samples a second). The code is as follows.
a_pin = 0;
tic;
i = 0;
while toc < 20
i = i + 1;
time(i) = toc;
v(i) = a.analogRead(a_pin);
pause(.25);
end
Is there a way to set the loop to run a specific time and then in the loop sample at a different rate?
You can try this:
a_pin = 0;
fs = 4; % sampling frequency (samplings per second)
mt = 20; % time for measurements
ind = 1;
nind = 1;
last_beep = 0;
tic;
while toc < mt
time(ind) = toc;
v(ind) = a.analogRead(a_pin);
% wait for appropriate time for next measurement
while( nind == ind )
nind = floor(toc*fs) + 1;
end
ind = nind;
% beep every second
if (ceil(toc) > last_beep)
beep(); % don't know if this one exist, check docs
last_beep = ceil(toc);
end
end
Maximal sampling time for a single Arduino analog read command is around 0.04 s, in practice I'd go minimally 0.05. Adding two read operations is in the order of 2*0.04, in practice more like 0.1 s. I think it is mainly limited by the USB communication speeds.
I am also new at arduino, but having implemented a real time analysis for EEG using it, on practice, I was able to sample 2 analog channels with a samplinf frequency between 57 and 108Hz. It was very variable (calculated through tic/toc), but it is still proper for realtime processing in my case.
My code uses a While loop, a series of memory updates, digital pin manipulations, plot of trace (drawnow) and seems to run smoothly enough
My answer is simply here : 0.0283 sec for sampling 2 analog inputs in my case.
Cheers