Matlab parallel computing - Simultaneous play and record - matlab

I want to play a pure tone and record from the default microphone simultaneously using Matlab.
%% Main code
frequency = 500;
time = 5;
tic
parfor ii = 1:2
if ii==1
recording = test_record(time);
elseif ii==2
test_play(frequency,time);
end
end
toc
size(recording)
This all works fine except that recording variable is not available after the parfor loop ends and I need it for further analysis. I'd appreciate suggestions of how to make the recorded sample available after the parforloop or even better - I'll be happy to learn if a better design is preferable.
Thanks!
Here are the two additional functions used for running the above code :
%% Play the given frequency
function test_play(frequency,recording_time)
fs = 8000; % Samples per second
t = 0:(1/fs):recording_time;
y = sin(2*pi*frequency*t);
sound(y, fs);
end
%% Record from the microphone for given number of seconds
function recording = test_record(recording_time)
recObj = audiorecorder;
recordblocking(recObj, recording_time);
recording = getaudiodata(recObj);
end
Note - A related question's answer redirect to some program (Sox) that seem to do what I search for, however I want to write the Matlab code myself.

In your case, recording is a local variable which remains on the worker. This code creates a sliced cell array which should fix it:
%% Main code
frequency = 500;
time = 5;
recording=cell(2,1)
parfor ii = 1:2
if ii==1
recording{ii} = test_record(time);
elseif ii==2
test_play(frequency,time);
end
end
Obviously, your data is recording{1}

Related

How to do an animate plot in MATLAB from a sequence of matrices

I have code that uses Wolff's Algorithm to simulate the XY Model in MATLAB and I want to implement a pcolor/color map to demonstrate each spin according to their angles across the system. But I want it to be live and changing as the angles change.
Any idea how to do this?
This is an example of how I want it to look https://i.stack.imgur.com/aSp7s.png
If you save each snapshot of the lattice in a cell array A{t}, you can use the following function to view and save it as a video (if fileName is not empty, the function saves an mp4 video).
Another option is to adapt the function view_lattice to run your simulation (which, honestly, I wouldn't recommend, for performance issues). I will mark where you should edit for doing a "live" simulation
This is at least MATLAB R2019b (although it may be compatible with earlier versions, but no guarantee).
File view_lattice.m
function view_lattice(A,fileName)
% for a 'live' simulation, you will have to remove A from the input
% parameters and add the ones you need for the XY Wolff algorithm,
% which will be used to calculate each configuration A in the time loop below
% you will also need to remove the assert statements for 'live' simulation
%
% otherwise, you save snapshots from your simulation
% and use this function as is
%
% A -> A{k}[m,n] snapshot k containing the angles of spins in lattice site at row m and col n
% fileName -> if contains string, then records a video with the snapshots and name it with this string
assert(iscell(A) && all(cellfun(#(a)isnumeric(a) && ismatrix(a),A)),'A must be cell of numeric matrices');
assert(ischar(fileName),'fileName must be either an empty char or contain a file name');
recordVideo = ~isempty(fileName);
if recordVideo
vw = setup_video(fileName);
else
vw = [];
end
% setting some default axis properties to speed-up plotting
set(0,'DefaultAxesPlotBoxAspectRatio',[1 1 1],'DefaultAxesDataAspectRatioMode','manual','DefaultAxesDataAspectRatio',[1,1,1],'DefaultAxesNextPlot','replace');
fh = figure;
ax=axes;
for t = 1:numel(A) % for 'live' simulation, this loop should be the time loop
% here you calculate the new configuration A
% and call the function below with A instead of A{t}
vw = record_frame(vw,fh,ax,A{t},t,recordVideo);
end
% any video to close?
if recordVideo
vw.close();
end
end
function vw = record_frame(vw,fh,ax,A,t,recordVideo)
imagesc(ax,A);
title(ax,sprintf('snapshot %g',t)); % if you want, y
axis(ax,'square');
daspect(ax,[1,1,1]);
pause(0.01);
if recordVideo
vframe = getframe(fh);
vw.writeVideo(vframe);
end
end
function vw = setup_video(fileName)
vid_id = num2str(rand,'%.16g');
vid_id = vid_id(3:6);
vid_id = [fileName,'_',vid_id];
% Initialize video
vw = VideoWriter([vid_id,'.mp4'], 'MPEG-4'); %open video file
vw.Quality = 100;
vw.FrameRate = 16;
vw.open();
end
Test script: test.m
clearvars
close all
A = cell(1,30);
for t = 1:numel(A)
% creating a sequence of random snapshots only for illustration
A{t} = rand(20,20);
end
% viewing the animation and saving it as a video with name test
view_lattice(A,'test');
Output

Accessing parsim data in Matlab

Good evening, May I please get advice with the following Matlab code? Here it is:
%% CLEAR ALL
close all
clear all
clc
%% LOAD MODEL AND LHC FILE
tic %start the clock
idx=1;
model = 'PG_PN_basic_rev1'; %This is the simulink file you wish to run.
load_system(model);
load 'LHC_input.mat' %Call in the file created by LHC_Final.m
LHC = (LHC1_input);
k_dc = LHC((1:5),1);
k_r = LHC((1:5),2);
a_1 = LHC((1:5),3);
b_1 = LHC((1:5),4);
Kg_PG = LHC((1:5),5);
Kg_PN = LHC((1:5),6);
for i = length(k_dc):-1:1
in(i) = Simulink.SimulationInput('PG_PN_basic_rev1');
in(i) = in(i).setVariable('k_dc',k_dc(i));
for j = length(k_r):-1:1
in(j) = in(j).setVariable('k_r',k_r(j));
for k = length(a_1):-1:1
in(k) = in(k).setVariable('a_1',a_1(k));
for l = length(b_1):-1:1
in(l) = in(l).setVariable('b_1',b_1(l));
for m = length(Kg_PG):-1:1
in(m) = in(m).setVariable('Kg_PG',Kg_PG(m));
for n = length(Kg_PN):-1:1
in(n) = in(n).setVariable('Kg_PN',Kg_PN(n));
end
end
end
end
end
end
out = parsim(in, 'ShowProgress', 'on');
% eval(['PN_batch', int2str(idx),' =PN;']);
% data = eval(['PN_batch', int2str(idx)]);
% a{idx} = data;
% idx=idx+1;
% run = idx
timeElapsed = toc %How long did you code run for?
I wish to be able to generate an output file per parsim run (PN_batch1, PN_batch2,...etc.). However, the data often falls under just 1 output, and isn't divided up into readable workspace objects that I can read later with another script. Any advice would be greatly appreciated. Thank you.
out is a vector of length equal to the number of simulations with the data of a simulation stored in each entry. If you have to workspace blocks in your model, you can access that data per simulation using out(10).NameOftoWorkspaceData, in case you want to get the data of the 10th simulation. More info on the out variable can be found here on the Mathworks site.
Tip: run the model and check out the variable out, then you can explore its structure

Matlab: Working for-loop breaks in parfor while fitting curves

Hoping you may be able to assist me with this error. I am running some code to fit curves to ages using a cross validation regime. I iterate the curve fitting 1000 times to assess the best fit.
I define my models as:
linear_ft = fittype({'x', '1'});
monotonic_ft= fittype({'-1/x', '1'});
quadratic_ft = fittype('poly2');
I then run the following to iterate through different selections of data splitting, recording the residuals following the curve fit...
Data = randn(4,300,10,10);
Ages = randn(300,1);
for thisDim1 = 1:4
for thisDim2 = 1:10
for thisDim3 = 1:10
for nIts = 1:1000
RandomOrder = randperm(300,300);
Fit_Subs = RandomOrder(1:length(Ages)/2); % Take random subs to fit to
Test_Subs = RandomOrder(length(Ages)/2+1:300); % Take random subs to test fit to
Fit_Data = squeeze(Data(thisDim1,Fit_Subs,thisDim2,thisDim3)); % Take data to fit to
Test_Data = squeeze(Data(thisDim1,Test_Subs,thisDim2,thisDim3)); % Take data to test fit
Fit_Ages = Ages;
Fit_Ages(Fit_Subs) = []; %Take ages of Fit Subs only
Test_Ages = Ages;
Test_Ages(Test_Subs) = []; % Take ages of Test Subs only
Nsubs = (length(Ages)/2);
% Model Data using Curves
fFit_Lin = fit(Fit_Ages,Fit_Data',linear_ft);
fFit_Mon = fit(Fit_Ages,Fit_Data',monotonic_ft);
fFit_Quad = fit(Fit_Ages,Fit_Data',quadratic_ft);
% Fit Modelled Data to Test Data
tFit_Lin = fFit_Lin(Test_Ages);
tFit_Mon = fFit_Mon(Test_Ages);
tFit_Quad = fFit_Quad(Test_Ages);
% Calculate Median Residual
Lin_Med_Resid(nIts) = median(tFit_Lin - Test_Data');
Mon_Med_Resid(nIts) = median(tFit_Mon - Test_Data');
Quad_Med_Resid(nIts) = median(tFit_Quad - Test_Data');
end
end
end
end
If you run this with the fourth loop (nIts) as a for-loop it will run. If you run it as a parfor-loop it won't stating the error:
Error using fit>iFit (line 264)
The name 'lower' is not an accessible property for an instance of class
'llsqoptions'.
Error in fit (line 108) [fitobj, goodness, output, convmsg] = iFit(
xdatain, ydatain, fittypeobj, ...
Does anyone have any idea how to fix this? I would be most grateful for any advice!!
Thanks,
Ben
Try restarting MATLAB or typing clear all to see if it clears things up for you.
Your code works for me, but the parallel toolbox can be a bit finicky in my experience.

Take user input while running in Matlab

I have a function that I would like to take input for but only when the user wants to. For example if i have this code:
figure
amplitude = 10;
tic
i = 1;
while(1)
time = toc;
values(i) = amplitude*sin(time);
times(i) = time;
plot(times, values)
drawnow
i = i+1;
end
You will get a continually plotting sine wave (like a lame movie). What I want to do is allow the user to change the amplitude of the wave at any time. That is the program will continue to run but if the user types in 20 and Enter then the amplitude variable can be changed and the sine wave will change amplitude in the movie. Any pointers on how to achieve this?
You won't be able to do this by typing numbers into the console, but you can do it with a simple GUI. Do a google search for Matlab callbacks to find examples. When a GUI event occurs, it triggers a function that you can use to modify the variables in your loop.
It is probably best to do it with a GUI as mentioned, but if you just want something in the console here is what I can offer:
A script that periodically asks the user to input an amplitude and then continues the 'movie' with this amplitude. It can easily be expanded to allow the user to decide when he will be asked to input the next amplitude change.
clear
amplitude = 10;
i=1;
while(1)
time = i/1000;
values(i) = amplitude*sin(time);
times(i) = time;
plot(times, values)
drawnow
i = i+1;
if mod(i,3141) == 0
keyboard
end
end
Now this will run for a while and then ask you to input the next amplitude. Note that you can actually give multiple commands at once.
amplitude = 20; return
amplitude = 1; return
This will let the next amplitude be 20 and the one after that 1. Note that the upward arrow key is your friend here.

How to implement similar statements in matlab using a loop?

Look at the code snippet here in the mathworks documentation,
Digital Servo Control of a Hard-Disk Drive
Gf1 = tf(w1*[a1 b1*w1],[1 2*z1*w1 w1^2]); % first resonance
Gf2 = tf(w2*[a2 b2*w2],[1 2*z2*w2 w2^2]); % second resonance
Gf3 = tf(w3*[a3 b3*w3],[1 2*z3*w3 w3^2]); % third resonance
Gf4 = tf(w4*[a4 b4*w4],[1 2*z4*w4 w4^2]); % fourth resonance
my question is, how can I implement the above statements within a loop like,
% pseudo code
for i = 1:4
Gf%d = tf(w%d*[a%d b%d*w%d],[1 2*z%d*w%d w%d^2]); i
and then execute the result in matlab?
Here's one option:
w = [w1 w2 w3 w4];
%# same thing for a, b, d...
for i=1:4
Gf(i) = tf(w(i)*[a(i) b(i)*w(i)],[1 2*z(i)*w(i) w(i)^2]); % ith resonance
end
You could either use eval:
for i = 1:4
eval(sprintf('Gf%d = tf(w%d*[a%d b%d*w%d],[1 2*z%d*w%d w%d^2]);', i));
end
or you could turn your parameters into arrays. Arrays would be more efficient, if you have control over how you format your data.
The best way to do this is to use arrays.
for i = 1:n
trans(i) = % your stuff here %
end
Then just replace the different variables with the correct array indexes.