nback_matlab.m 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371
  1. function handles = nback_matlab
  2. %% INFORMATION:
  3. %
  4. % -Keep the 'nback_matlab.m' function within the same folder as the subfolder
  5. % 'Audio', which contains audio files required for the program to run. As
  6. % long as 'nback_matlab.m' is on Matlab's path, the program will handle
  7. % adding the audio files to the path automatically.
  8. %
  9. % -A 'UserData' subfolder will be written to the same location as
  10. % 'nback_matlab.m the first time the program is run. User performance
  11. % data will be stored in a .mat file within this folder
  12. % ('user_data.mat'). Also, whenever the program closes, the
  13. % 'nback_matlab_settings.txt' file will be updated with the user's most
  14. % recent preferences (this will be loaded upon program reboot).
  15. % -the user_data.mat file contains a variable 'summaryStats' that
  16. % summarizes A' (see below), hit rate, false alarm rate, and lure error
  17. % rate for each stimuli type (position, sound, color); it also contains a
  18. % 'trialData' cell array that shows actual user responses for every trial
  19. % (rows of table) of each round of n-back played (cells)
  20. %
  21. % *Scoring: A' (Discrimination Index), similar to d'
  22. % H = Hit Rate (hits / # signal trials),
  23. % F = False-positive Rate (false pos / # noise trials)
  24. % aPrime = .5 + sign(H - F) * ((H - F)^2 + abs(H - F)) / (4 * max(H, F) - 4 * H * F);
  25. %
  26. % A' references:
  27. % Snodgrass, J. G., & Corwin, J. (1988). Pragmatics of measuring recognition
  28. % memory: applications to dementia and amnesia. Journal of Experimental
  29. % Psychology: General, 117(1), 34.
  30. %
  31. % Stanislaw, H., & Todorov, N. (1999). Calculation of signal detection theory
  32. % measures. Behavior research methods, instruments, & computers, 31(1),
  33. % 137-149.
  34. %
  35. % *For mathematical formula, see Stanislaw & Todorov (1999, p. 142)
  36. %
  37. % *Default Settings:
  38. % nback = 2; % nBack level
  39. % percLure = .2; % percentage of non-match trials to be converted to
  40. % "lure" or interference trials (a lure is n-back +/- 1, requiring
  41. % engagement of executive control to avoid errors)
  42. % sound_on = 1; % sound-type nBack: 1 (on), 0 (off)
  43. % position_on = 1; % position-type nBack: 1 (on), 0 (off)
  44. % color_on = 0; % color-type nBack: 1 (on), 0 (off)
  45. % completely_random = 0; % off (0), on (1) (if on, next 3 settings are irrelevant)
  46. % n_positionHits = 4; % control # of matches for position
  47. % n_soundHits = 4; % control # of matches for sound
  48. % n_colorHits = 4; % control # of matches for color
  49. % advance_thresh = .9; % minimum score (A') required to advance automatically to next nBack level;
  50. % fallback_thresh = .75; % score (A') below which one automatically regresses to previous nBack level;
  51. % nTrials = nback + 20; % # trials
  52. % trial_time = 2.4; % seconds
  53. % volume_factor = 1; % Default: 1 (no change); >1 = increase volume; <1 = decrease volume
  54. % sound_type = 1; % use Numbers-Female (1), Numbers-Male (2), Letters-Female1 (3), or Letters-Female2 (4)
  55. % enable_applause = 1; % 1 (on) or 0 (off); applause on advance to next n-back level
  56. % enable_boos = 0; % 1 (on) or 0 (off); comical boos if fallback to previous n-back level
  57. % realtime_feedback = 1; % 1 (on) or 0 (off); highlight labels red/green based on accuracy
  58. % figure_window_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in figure window upon completion
  59. % command_line_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in command window upon completion
  60. % show_remaining = 1; % 1 (on) or 0 (off); show remaining trials
  61. % background_color_scheme = 1; % black (1), white (2)
  62. %
  63. % *Author: Elliot A. Layden (2017-18),
  64. % at The Environmental Neuroscience Laboratory, The University of Chicago
  65. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  66. %% Hotkey Designations:
  67. startSessionKey = 'space';
  68. positionMatchKey = 'a';
  69. soundMatchKey = 'l';
  70. colorMatchKey = 'j';
  71. stopRunningKey = 'escape';
  72. %% ------------------------ Begin ------------------------
  73. customizable = {'nback','percLure','sound_on','position_on','color_on','completely_random',...
  74. 'n_soundHits','n_positionHits','n_colorHits','advance_thresh',...
  75. 'fallback_thresh','nTrials','trial_time','volume_factor',...
  76. 'sound_type','enable_applause','enable_boos','realtime_feedback',...
  77. 'command_line_feedback','figure_window_feedback','show_remaining',...
  78. 'background_color_scheme'};
  79. % Initialize Global Variables:
  80. curr_running = 0; stop_running = 0; ix = 1; canResize = false;
  81. position_mem = []; color_mem = []; sound_mem = [];
  82. position_match_vec = []; sound_match_vec = []; color_match_vec = [];
  83. user_position_match = []; user_sound_match = []; user_color_match = [];
  84. position_lures_vec = []; sound_lures_vec = []; color_lures_vec = [];
  85. advance_thresh = []; fallback_thresh = [];
  86. figure_window_feedback = []; command_line_feedback = []; volume_factor = [];
  87. h_pos_feedback1_pos = []; h_pos_feedback2_pos = [];
  88. h_color_feedback1_pos = []; h_color_feedback2_pos = [];
  89. h_sound_feedback1_pos = []; h_sound_feedback2_pos = [];
  90. percLure = .2; timerVal = 0; times = []; extraKeys = {};
  91. % Initialize object handles:
  92. h_title = 10.384372; h_volume = 28.1717839;
  93. h_pos_feedback1 = 10.384372; h_pos_feedback2 = 10.384372;
  94. h_sound_feedback1 = 10.384372; h_sound_feedback2 = 10.384372;
  95. h_color_feedback1 = 10.384372; h_color_feedback2 = 10.384372;
  96. square_width = .2;
  97. positions = repmat([.05,.05,square_width,square_width],9,1);
  98. % Get script path and subfolders:
  99. script_fullpath = mfilename('fullpath');
  100. [script_path,~,~] = fileparts(script_fullpath);
  101. addpath(genpath(script_path))
  102. audio_path = fullfile(script_path,'Audio');
  103. audio_subfolders = {fullfile(audio_path,'numbers-female'),fullfile(audio_path,'numbers-male'),...
  104. fullfile(audio_path,'letters-female1'),fullfile(audio_path,'letters-female2')};
  105. % Get Settings:
  106. get_settings;
  107. % Calculate Parameters
  108. [~,nBack_spec] = min(abs((1:20)-nback));
  109. poss_lures = 0:.05:1;
  110. [~,lure_spec] = min(abs(poss_lures-percLure));
  111. trial_num_opts = 15:5:100; % nTrials
  112. n_trial_num_opts = length(trial_num_opts);
  113. if nTrials==nback+20
  114. trial_num_spec = 1;
  115. else trial_num_spec = 1+min(abs(trial_num_opts-nTrials));
  116. end
  117. trial_time_opts = 1:.2:4; % seconds;
  118. n_trial_time_opts = length(trial_time_opts);
  119. [~,trial_time_spec] = min(abs(trial_time_opts-trial_time));
  120. % Appearance Customization:
  121. if background_color_scheme==1
  122. txt_color = ones(1,3);
  123. background_color = zeros(1,3);
  124. colors = {'y','m','c','r','g','b','w'};
  125. elseif background_color_scheme==2
  126. txt_color = zeros(1,3);
  127. background_color = ones(1,3);
  128. colors = {'y','m','c','r','g','b','k'};
  129. end
  130. % Load Main Audio:
  131. audio_dat = struct('sound',cell(1,4),'Fs',cell(1,4),'nSound',cell(1,4));
  132. for i = 1:4
  133. listing = dir(audio_subfolders{i});
  134. nSound = length(listing)-2;
  135. audio_dat(i).sound = cell(1,nSound);
  136. audio_dat(i).Fs = cell(1,nSound);
  137. audio_dat(i).nSound = nSound;
  138. for j = 1:nSound
  139. [audio_dat(i).sound{j},audio_dat(i).Fs{j}] = audioread(fullfile(audio_subfolders{i},listing(j+2).name));
  140. end
  141. end
  142. % Load Reaction Sounds (Applause/Boos):
  143. [applause_dat, Fs_applause] = audioread(fullfile(audio_path,'applause.mp3'));
  144. [boo_dat, Fs_boo] = audioread(fullfile(audio_path,'boo.mp3'));
  145. % Initialize Figure:
  146. handles.figure = figure('menubar','none','color',background_color,'numbertitle',...
  147. 'off','name',['nback_matlab: (',num2str(nback),'-Back)'],'units','norm',...
  148. 'Position',[.271,.109,.488,.802],'ResizeFcn',@resizeScreen);
  149. handles.axes1 = gca; set(handles.axes1,'Position',[0,0,1,1],'XLim',[0,1],...
  150. 'YLim',[0,1],'Visible','off');
  151. h_title = annotation('textbox','String','','FontName','Helvetica','FontSize',14,...
  152. 'Position',[.4,.955,.2,.05],'Color',txt_color,'HorizontalAlignment','center',...
  153. 'FontWeight','bold','EdgeColor','none');
  154. %% Menus:
  155. % File Menu:
  156. file_menu = uimenu(handles.figure,'Label','File');
  157. % uimenu(file_menu,'Label','Progress Report','Callback',@show_progress);
  158. uimenu(file_menu,'Label','Exit','Callback',@close_nback_matlab);
  159. % Session Menu:
  160. session_menu = uimenu(handles.figure,'Label','Session');
  161. % N-Back Level:
  162. nBack_num_menu = uimenu(session_menu,'Label','N-Back Level');
  163. h_nBack = zeros(1,20);
  164. for i = 1:20
  165. h_nBack(i) = uimenu(nBack_num_menu,'Label',sprintf('%02g',i),'Callback',{@change_nBack,i});
  166. end; set(h_nBack(nBack_spec),'Checked','on')
  167. % Percent Lures:
  168. percLure_menu = uimenu(session_menu,'Label','Percent Lures/Interference');
  169. h_lures = zeros(1,21);
  170. for i = 1:21
  171. h_lures(i) = uimenu(percLure_menu,'Label',sprintf('%02g',poss_lures(i)),'Callback',{@change_lures,i});
  172. end; set(h_lures(lure_spec),'Checked','on')
  173. % Stimuli Type(s) Menu:
  174. nBack_type_menu = uimenu(session_menu,'Label','Stimuli Type(s)');
  175. if position_on
  176. types_menu(1) = uimenu(nBack_type_menu,'Label','Position','Checked','on','Callback',{@change_types,1});
  177. else types_menu(1) = uimenu(nBack_type_menu,'Label','Position','Checked','off','Callback',{@change_types,1});
  178. end
  179. if sound_on
  180. types_menu(2) = uimenu(nBack_type_menu,'Label','Sound','Checked','on','Callback',{@change_types,2});
  181. else types_menu(2) = uimenu(nBack_type_menu,'Label','Sound','Checked','off','Callback',{@change_types,2});
  182. end
  183. if color_on
  184. types_menu(3) = uimenu(nBack_type_menu,'Label','Color','Checked','on','Callback',{@change_types,3});
  185. else types_menu(3) = uimenu(nBack_type_menu,'Label','Color','Checked','off','Callback',{@change_types,3});
  186. end
  187. trial_num_menu = uimenu(session_menu,'Label','# Trials');
  188. h_trial_num = zeros(1,n_trial_num_opts+1);
  189. h_trial_num(1) = uimenu(trial_num_menu,'Label','20 + N (default)','Callback',{@change_trial_num,1});
  190. for i = 2:n_trial_num_opts+1
  191. h_trial_num(i) = uimenu(trial_num_menu,'Label',sprintf('%1g',trial_num_opts(i-1)),'Callback',{@change_trial_num,i});
  192. end; set(h_trial_num(trial_num_spec),'Checked','on')
  193. trial_length_menu = uimenu(session_menu,'Label','Trial Length');
  194. h_trial_times = zeros(1,n_trial_time_opts);
  195. for i = 1:n_trial_time_opts
  196. h_trial_times(i) = uimenu(trial_length_menu,'Label',sprintf('%1.1f seconds',trial_time_opts(i)),'Callback',{@change_trial_time,i});
  197. end; set(h_trial_times(trial_time_spec),'Checked','on')
  198. advanced_menu = uimenu(session_menu,'Label','Advanced');
  199. num_hits_menu = uimenu(advanced_menu,'Label','# Matches');
  200. sound_matches_menu = uimenu(num_hits_menu,'Label','# Sound Matches');
  201. n_poss = round(.6*nTrials);
  202. h_n_sound_matches = zeros(1,n_poss);
  203. for i = 1:n_poss
  204. h_n_sound_matches(i) = uimenu(sound_matches_menu,'Label',sprintf('%02g',i),'Callback',{@change_sound_matches,i});
  205. end
  206. set(h_n_sound_matches(n_soundHits),'Checked','on')
  207. position_matches_menu = uimenu(num_hits_menu,'Label','# Position Matches');
  208. h_n_position_matches = zeros(1,n_poss);
  209. for i = 1:n_poss
  210. h_n_position_matches(i) = uimenu(position_matches_menu,'Label',sprintf('%02g',i),'Callback',{@change_position_matches,i});
  211. end
  212. set(h_n_position_matches(n_positionHits),'Checked','on')
  213. color_matches_menu = uimenu(num_hits_menu,'Label','# Color Matches');
  214. h_n_color_matches = zeros(1,n_poss);
  215. for i = 1:n_poss
  216. h_n_color_matches(i) = uimenu(color_matches_menu,'Label',sprintf('%02g',i),'Callback',{@change_color_matches,i});
  217. end
  218. set(h_n_color_matches(n_colorHits),'Checked','on')
  219. if completely_random
  220. uimenu(num_hits_menu,'Label',...
  221. 'Completely Random','Checked','on','Callback',@change_completely_random);
  222. else
  223. uimenu(num_hits_menu,'Label',...
  224. 'Completely Random','Checked','off','Callback',@change_completely_random);
  225. end
  226. advance_fallback_menu = uimenu(advanced_menu,'Label','Advance/Fallback');
  227. advance_menu = uimenu(advance_fallback_menu,'Label','Advance Threshold');
  228. advance_thresholds = 1:-0.02:0.80;
  229. h_advances = zeros(1,length(advance_thresholds));
  230. for i = 1:length(advance_thresholds)
  231. h_advances(i) = uimenu(advance_menu,'Label',sprintf('%g%',advance_thresholds(i)),'Callback',{@change_advance_thresh,i});
  232. end
  233. [~,which_advance] = min(abs(advance_thresholds-advance_thresh));
  234. set(h_advances(which_advance),'Checked','on')
  235. set(h_advances(6),'Label',[sprintf('%g%',advance_thresholds(6)),' (default)']);
  236. fallback_menu = uimenu(advance_fallback_menu,'Label','Fallback Threshold');
  237. fallback_thresholds = .80:-.05:.50;
  238. h_fallbacks = zeros(1,length(fallback_thresholds));
  239. for i = 1:length(fallback_thresholds)
  240. h_fallbacks(i) = uimenu(fallback_menu,'Label',sprintf('%g%',fallback_thresholds(i)),'Callback',{@change_fallback_thresh,i});
  241. end
  242. [~,which_fallback] = min(abs(fallback_thresholds-fallback_thresh));
  243. set(h_fallbacks(which_fallback),'Checked','on')
  244. set(h_fallbacks(2),'Label',[sprintf('%g%',fallback_thresholds(2)),' (default)']);
  245. feedback_menu = uimenu(handles.figure,'Label','Feedback');
  246. if show_remaining
  247. uimenu(feedback_menu,'Label','Show Remaining Trials','Checked','on','Callback',@change_show_remaining);
  248. else uimenu(feedback_menu,'Label','Show Remaining Trials','Checked','off','Callback',@change_show_remaining);
  249. end
  250. if realtime_feedback
  251. uimenu(feedback_menu,'Label','Realtime Feedback','Checked','on','Callback',@change_realtime_feedback);
  252. else uimenu(feedback_menu,'Label','Realtime Feedback','Checked','off','Callback',@change_realtime_feedback);
  253. end
  254. if figure_window_feedback
  255. uimenu(feedback_menu,'Label','Results in GUI','Checked','on','Callback',@change_figure_feedback);
  256. else uimenu(feedback_menu,'Label','Results in GUI','Checked','off','Callback',@change_figure_feedback);
  257. end
  258. if command_line_feedback
  259. uimenu(feedback_menu,'Label','Command Line Results','Checked','on','Callback',@change_cmd_feedback);
  260. else uimenu(feedback_menu,'Label','Command Line Results','Checked','off','Callback',@change_cmd_feedback);
  261. end
  262. sound_settings_menu = uimenu(handles.figure,'Label','Sounds');
  263. uimenu(sound_settings_menu,'Label','Volume','Callback',@change_volume);
  264. sound_type_menu = uimenu(sound_settings_menu,'Label','Type');
  265. h_letters = uimenu(sound_type_menu,'Label','Letters');
  266. h_sound_types(3) = uimenu(h_letters,'Label','Female Voice 1','Callback',{@change_sound_types,3});
  267. h_sound_types(4) = uimenu(h_letters,'Label','Female Voice 2','Callback',{@change_sound_types,4});
  268. h_numbers = uimenu(sound_type_menu,'Label','Numbers');
  269. h_sound_types(1) = uimenu(h_numbers,'Label','Female Voice','Callback',{@change_sound_types,1});
  270. h_sound_types(2) = uimenu(h_numbers,'Label','Male Voice','Callback',{@change_sound_types,2});
  271. set(h_sound_types(sound_type),'Checked','on')
  272. reactions_menu = uimenu(sound_settings_menu,'Label','Reactions');
  273. if enable_applause
  274. uimenu(reactions_menu,'Label','Applause','Checked','on','Callback',@change_applause_setting);
  275. else uimenu(reactions_menu,'Label','Applause','Checked','off','Callback',@change_applause_setting);
  276. end
  277. if enable_boos
  278. uimenu(reactions_menu,'Label','Boos','Checked','on','Callback',@change_boos_setting);
  279. else uimenu(reactions_menu,'Label','Boos','Checked','off','Callback',@change_boos_setting);
  280. end
  281. appearance_menu = uimenu(handles.figure,'Label','Appearance');
  282. background_menu = uimenu(appearance_menu,'Label','Background');
  283. if background_color_scheme==1
  284. h_background_menu1 = uimenu(background_menu,'Label','Black','Checked','on','Callback',{@change_background,1});
  285. h_background_menu2 = uimenu(background_menu,'Label','White','Callback',{@change_background,2});
  286. txt_color = ones(1,3);
  287. elseif background_color_scheme==2
  288. h_background_menu1 = uimenu(background_menu,'Label','Black','Callback',{@change_background,1});
  289. h_background_menu2 = uimenu(background_menu,'Label','White','Checked','on','Callback',{@change_background,2});
  290. txt_color = zeros(1,3);
  291. else warning('''background_color'' should be either integer 1 (black) or 2 (white)')
  292. h_background_menu1 = uimenu(background_menu,'Label','Black','Checked','on','Callback',{@change_background,1});
  293. h_background_menu2 = uimenu(background_menu,'Label','White','Callback',{@change_background,2});
  294. txt_color = ones(1,3);
  295. end
  296. %% Add other display elements (gridlines, rectangle, etc.):
  297. % Gridlines:
  298. minMeasure = .05; maxMeasure = .95; one_third = .3497; two_thirds = .6503;
  299. h_grid_lines = zeros(1,4);
  300. h_grid_lines(1) = annotation('line','X',[one_third,one_third],'Y',[minMeasure,maxMeasure],'Color',txt_color,'LineWidth',1);
  301. h_grid_lines(2) = annotation('line','X',[two_thirds,two_thirds],'Y',[minMeasure,maxMeasure],'Color',txt_color,'LineWidth',1);
  302. h_grid_lines(3) = annotation('line','X',[minMeasure,maxMeasure],'Y',[two_thirds,two_thirds],'Color',txt_color,'LineWidth',1);
  303. h_grid_lines(4) = annotation('line','X',[minMeasure,maxMeasure],'Y',[one_third,one_third],'Color',txt_color,'LineWidth',1);
  304. % Initialize Match Button Text and Rectangle:
  305. handles.h_rect = rectangle('Parent',handles.axes1,'Position',...
  306. [.05,.05,square_width,square_width],'Curvature',[.2,.2],'FaceColor',...
  307. 'blue','Visible','off');
  308. h_txt_begin = annotation('textbox',[.37,.48,.26,.04],'String',...
  309. ['Press ',startSessionKey,sprintf(' \nto begin')],...
  310. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment',...
  311. 'middle','FontSize',14,'FontWeight','bold','EdgeColor','none');
  312. h_txt_pos = text('position',[.04,.01],'String',['Position Match: ''',positionMatchKey,''''],...
  313. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment','top',...
  314. 'FontSize',13,'FontWeight','normal','Visible','off','EdgeColor','none');
  315. h_txt_color = text('position',[.35,.01],'String',['Color Match: ''',colorMatchKey,''''],...
  316. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment','top',...
  317. 'FontSize',13,'FontWeight','normal','Visible','off','EdgeColor','none');
  318. h_txt_sound = text('position',[.658,.01],'String',['Sound Match: ''',soundMatchKey,''''],...
  319. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment','top',...
  320. 'FontSize',13,'FontWeight','normal','Visible','off','EdgeColor','none');
  321. if position_on; set(h_txt_pos,'Visible','on'); end
  322. if color_on; set(h_txt_color,'Visible','on'); end
  323. if sound_on; set(h_txt_sound,'Visible','on'); end
  324. % Declare Figure Callbacks:
  325. set(handles.figure,'WindowKeyPressFcn',@keypress_callback);
  326. set(handles.figure,'CloseRequestFcn',@close_nback_matlab)
  327. % Resize Figure function:
  328. canResize = true;
  329. resizeScreen;
  330. %% Run Function:
  331. function run_session
  332. extraKeys = cell(nTrials,1); curr_running = 1;
  333. % Get Trial Times:
  334. targetTimes = linspace(1,trial_time * (nTrials-1) + 1, nTrials);
  335. pause(1 - (toc(timerVal)-0));
  336. disableMenus; % disallow users from toggling settings during play
  337. if ishandle(h_pos_feedback1)
  338. delete(h_pos_feedback1); delete(h_pos_feedback2)
  339. end
  340. if ishandle(h_sound_feedback1)
  341. delete(h_sound_feedback1); delete(h_sound_feedback2)
  342. end
  343. if ishandle(h_color_feedback1)
  344. delete(h_color_feedback1); delete(h_color_feedback2)
  345. end
  346. % START TRIALS:
  347. ix = 0; times = zeros(nTrials,1);
  348. while ix<nTrials && ~stop_running
  349. ix = ix + 1; times(ix) = toc(timerVal);
  350. if realtime_feedback
  351. set(h_txt_pos,'Color',txt_color)
  352. set(h_txt_sound,'Color',txt_color)
  353. set(h_txt_color,'Color',txt_color)
  354. end
  355. if show_remaining
  356. set(h_title,'String',num2str(nTrials-ix))
  357. end
  358. tic
  359. if position_on
  360. set(handles.h_rect,'Position',positions(position_mem(ix),:),'Visible','on')
  361. end
  362. if color_on
  363. set(handles.h_rect,'FaceColor',colors{color_mem(ix)})
  364. end
  365. if sound_on
  366. sound(audio_dat(sound_type).sound{sound_mem(ix)}*volume_factor,audio_dat(sound_type).Fs{sound_mem(ix)})
  367. end
  368. if abs(toc(timerVal)-targetTimes(ix)) < .5
  369. pause(trial_time - (toc(timerVal)-targetTimes(ix)));
  370. else
  371. pause(trial_time - sign(toc(timerVal)-targetTimes(ix))*.5);
  372. end
  373. end
  374. curr_running = 0; set(handles.h_rect,'Visible','off')
  375. set(h_txt_begin,'Visible','on'); set(h_title,'String','')
  376. set(h_txt_pos,'Color',txt_color); set(h_txt_sound,'Color',txt_color)
  377. set(h_txt_color,'Color',txt_color)
  378. % Calculate Score:
  379. if ~stop_running
  380. % Position Stats:
  381. position_hits = sum((position_match_vec+user_position_match)==2);
  382. position_false = sum((position_match_vec-user_position_match)==-1);
  383. position_lure_errors = sum((position_lures_vec+user_position_match)==2)/sum(position_lures_vec);
  384. H_pos = position_hits / n_positionHits; F_pos = position_false / (nTrials - n_positionHits);
  385. pos_score = .5 + sign(H_pos - F_pos) * ((H_pos - F_pos)^2 + abs(H_pos - F_pos)) / (4 * max(H_pos, F_pos) - 4 * H_pos * F_pos);
  386. position_stats = cell(5,2);
  387. position_stats(1:5,1) = {'Position','A'':','Hits:','False-Alarms:','Lure Errors:'};
  388. position_stats{2,2} = num2str(round(pos_score,2));
  389. position_stats{3,2} = [num2str(round(100*H_pos)),'%'];
  390. position_stats{4,2} = [num2str(round(100*F_pos)),'%'];
  391. position_stats{5,2} = [num2str(round(100*position_lure_errors)),'%'];
  392. % Sound Stats:
  393. sound_hits = sum((sound_match_vec+user_sound_match)==2);
  394. sound_false = sum((sound_match_vec-user_sound_match)==-1);
  395. sound_lure_errors = sum((sound_lures_vec+user_sound_match)==2)/sum(sound_lures_vec);
  396. H_sound = sound_hits / n_soundHits; F_sound = sound_false / (nTrials - n_soundHits);
  397. sound_score = .5 + sign(H_sound - F_sound) * ((H_sound - F_sound)^2 + abs(H_sound - F_sound)) / (4 * max(H_sound, F_sound) - 4 * H_sound * F_sound);
  398. sound_stats = cell(5,2);
  399. sound_stats(1:5,1) = {'Sound','A'':','Hits:','False-Alarms:','Lure Errors:'};
  400. sound_stats{2,2} = num2str(round(sound_score,2));
  401. sound_stats{3,2} = [num2str(round(100*H_sound)),'%'];
  402. sound_stats{4,2} = [num2str(round(100*F_sound)),'%'];
  403. sound_stats{5,2} = [num2str(round(100*sound_lure_errors)),'%'];
  404. % Color Stats:
  405. color_hits = sum((color_match_vec+user_color_match)==2);
  406. color_false = sum((color_match_vec-user_color_match)==-1);
  407. color_lure_errors = sum((color_lures_vec+user_color_match)==2)/sum(color_lures_vec);
  408. H_color = color_hits / n_colorHits; F_color = color_false / (nTrials - n_colorHits);
  409. color_score = .5 + sign(H_color - F_color) * ((H_color - F_color)^2 + abs(H_color - F_color)) / (4 * max(H_color, F_color) - 4 * H_color * F_color);
  410. color_stats = cell(5,2);
  411. color_stats(1:5,1) = {'Color','A'':','Hits:','False-Alarms:','Lure Errors:'};
  412. color_stats{2,2} = num2str(round(color_score,2));
  413. color_stats{3,2} = [num2str(round(100*H_color,1)),'%'];
  414. color_stats{4,2} = [num2str(round(100*F_color,1)),'%'];
  415. color_stats{5,2} = [num2str(round(100*color_lure_errors)),'%'];
  416. % Command-line Feedback:
  417. if command_line_feedback
  418. if position_on && sound_on && color_on
  419. session_stats = [position_stats,sound_stats,color_stats];
  420. elseif position_on && sound_on && ~color_on
  421. session_stats = [position_stats,sound_stats];
  422. elseif position_on && ~sound_on && ~color_on
  423. session_stats = position_stats;
  424. elseif ~position_on && sound_on && ~color_on
  425. session_stats = sound_stats;
  426. elseif ~position_on && sound_on && color_on
  427. session_stats = [sound_stats,color_stat];
  428. elseif position_on && ~sound_on && color_on
  429. session_stats = [position_stats,color_stats];
  430. elseif ~position_on && ~sound_on && color_on
  431. session_stats = color_stats;
  432. else return;
  433. end
  434. disp(session_stats)
  435. end
  436. % Write data:
  437. user_position_match = user_position_match==1;
  438. user_sound_match = user_sound_match==1;
  439. user_color_match = user_color_match==1;
  440. writeData(pos_score, position_lure_errors, H_pos, F_pos, ...
  441. sound_score, sound_lure_errors, H_sound, F_sound, ...
  442. color_score, color_lure_errors, H_color, F_color, ...
  443. (1:nTrials)', times, position_match_vec', position_lures_vec', ...
  444. user_position_match', position_mem', sound_match_vec', sound_lures_vec', ...
  445. user_sound_match', sound_mem', color_match_vec', color_lures_vec', ...
  446. user_color_match', color_mem',extraKeys)
  447. % Check if Advance:
  448. advance_vec = [(pos_score >= advance_thresh),(sound_score >= advance_thresh),(color_score >= advance_thresh)];
  449. fallback_vec = [(pos_score <= fallback_thresh),(sound_score <= fallback_thresh),(color_score <= fallback_thresh)];
  450. if all(advance_vec(logical([position_on,sound_on,color_on])))
  451. if command_line_feedback
  452. disp('Congratulations, you have been advanced to the next nBack level!')
  453. end
  454. nback = nback+1;
  455. if get(h_trial_num(1),'checked')
  456. nTrials = nTrials + 1;
  457. end
  458. set(handles.figure,'name',['nback_matlab: (',num2str(nback),'-Back)'])
  459. set(h_nBack(nback-1),'Checked','off'); set(h_nBack(nback),'Checked','on');
  460. if enable_applause; sound(applause_dat,Fs_applause); end
  461. if figure_window_feedback
  462. figure_feedback_color = 'g';
  463. end
  464. elseif any(fallback_vec(logical([position_on,sound_on,color_on])))
  465. if nback>1;
  466. nback = nback-1;
  467. if get(h_trial_num(1),'checked')
  468. nTrials = nTrials - 1;
  469. end
  470. if command_line_feedback
  471. disp('Good effort - nBack level lowered.')
  472. end
  473. set(handles.figure,'name',['nback_matlab: (',num2str(nback),'-Back)'])
  474. set(h_nBack(nback+1),'Checked','off'); set(h_nBack(nback),'Checked','on');
  475. else if command_line_feedback; disp('Good effort.'); end
  476. end
  477. if enable_boos; sound(boo_dat,Fs_boo); end
  478. if figure_window_feedback
  479. figure_feedback_color = 'r';
  480. end
  481. else
  482. if command_line_feedback; disp('Good score - keep training!'); end
  483. if figure_window_feedback
  484. if background_color_scheme==1
  485. figure_feedback_color = 'w';
  486. elseif background_color_scheme==2
  487. figure_feedback_color = 'k';
  488. end
  489. end
  490. end
  491. if figure_window_feedback
  492. if position_on
  493. main_char = char('Position','A'':','Hits:','False-Alarms:','Lure Errors:');
  494. char2 = char('',position_stats{2,2},position_stats{3,2},position_stats{4,2},position_stats{5,2});
  495. h_pos_feedback1 = text('String',main_char,'FontName','Helvetica','FontSize',13,...
  496. 'Position',h_pos_feedback1_pos,'Color',figure_feedback_color,...
  497. 'FontWeight','bold','EdgeColor','none','HorizontalAlignment','left');
  498. h_pos_feedback2 = text('String',char2,'FontName','Helvetica','FontSize',13,...
  499. 'Position',h_pos_feedback2_pos,'Color',figure_feedback_color,'HorizontalAlignment','right',...
  500. 'FontWeight','normal','EdgeColor','none');
  501. end
  502. if sound_on
  503. main_char = char('Sound','A'':','Hits:','False-Alarms:','Lure Errors:');
  504. char2 = char('',sound_stats{2,2},sound_stats{3,2},sound_stats{4,2},sound_stats{5,2});
  505. h_sound_feedback1 = text('String',main_char,'FontName','Helvetica','FontSize',13,...
  506. 'Position',h_sound_feedback1_pos,'Color',figure_feedback_color,...
  507. 'FontWeight','bold','EdgeColor','none');
  508. h_sound_feedback2 = text('String',char2,'FontName','Helvetica','FontSize',13,...
  509. 'Position',h_sound_feedback2_pos,'Color',figure_feedback_color,'HorizontalAlignment','right',...
  510. 'FontWeight','normal','EdgeColor','none');
  511. end
  512. if color_on
  513. main_char = char('Color','A'':','Hits:','False-Alarms:','Lure Errors:');
  514. char2 = char('',color_stats{2,2},color_stats{3,2},color_stats{4,2},color_stats{5,2});
  515. h_color_feedback1 = text('String',main_char,'FontName','Helvetica','FontSize',13,...
  516. 'Position',h_color_feedback1_pos,'Color',figure_feedback_color,...
  517. 'FontWeight','bold','EdgeColor','none');
  518. h_color_feedback2 = text('String',char2,'FontName','Helvetica','FontSize',13,...
  519. 'Position',h_color_feedback2_pos,'Color',figure_feedback_color,'HorizontalAlignment','right',...
  520. 'FontWeight','normal','EdgeColor','none');
  521. end
  522. end
  523. else stop_running = 0;
  524. end
  525. enableMenus;
  526. end
  527. % Key Press Callback:
  528. function keypress_callback(~,which_key,~)
  529. switch which_key.Key
  530. case startSessionKey
  531. if ~curr_running
  532. timerVal = tic;
  533. curr_running = 1; set(h_txt_begin,'Visible','off')
  534. if completely_random
  535. position_mem = randi(9,[1,nTrials]);
  536. color_mem = randi(7,[1,nTrials]);
  537. sound_mem = randi(audio_dat(sound_type).nSound,[1,nTrials]);
  538. else
  539. % Generate Idx:
  540. wasSuccess = false;
  541. while ~wasSuccess
  542. [position_mem, position_match_vec, position_lures_vec, wasSuccess] = generateIdx(nTrials, nback, n_positionHits, percLure, 9);
  543. end; wasSuccess = false;
  544. while ~wasSuccess
  545. [sound_mem, sound_match_vec, sound_lures_vec, wasSuccess] = generateIdx(nTrials, nback, n_soundHits, percLure, audio_dat(sound_type).nSound);
  546. end; wasSuccess = false;
  547. while ~wasSuccess
  548. [color_mem, color_match_vec, color_lures_vec, wasSuccess] = generateIdx(nTrials, nback, n_colorHits, percLure, 7);
  549. end
  550. user_position_match = nan(1, nTrials);
  551. user_sound_match = nan(1, nTrials);
  552. user_color_match = nan(1, nTrials);
  553. end
  554. run_session
  555. end
  556. case positionMatchKey
  557. if curr_running
  558. user_position_match(ix) = 1;
  559. if realtime_feedback
  560. if position_match_vec(ix)
  561. set(h_txt_pos,'Color',[0,1,0])
  562. else
  563. set(h_txt_pos,'Color',[1,0,0])
  564. end
  565. end
  566. end
  567. case colorMatchKey
  568. if curr_running
  569. user_color_match(ix) = 1;
  570. if realtime_feedback
  571. if color_match_vec(ix)
  572. set(h_txt_color,'Color',[0,1,0])
  573. else
  574. set(h_txt_color,'Color',[1,0,0])
  575. end
  576. end
  577. end
  578. case soundMatchKey
  579. if curr_running
  580. user_sound_match(ix) = 1;
  581. if realtime_feedback
  582. if sound_match_vec(ix)
  583. set(h_txt_sound,'Color',[0,1,0])
  584. else
  585. set(h_txt_sound,'Color',[1,0,0])
  586. end
  587. end
  588. end
  589. case stopRunningKey
  590. stop_running = 1;
  591. otherwise
  592. if curr_running
  593. extraKeys{ix} = [extraKeys{ix}, ',', which_key.Key];
  594. end
  595. end
  596. end
  597. % Change nBack Types:
  598. function change_types(~,~,which_type)
  599. switch which_type
  600. case 1,
  601. switch position_on
  602. case 1, set(types_menu(1),'Checked','off'); position_on = false;
  603. set(h_txt_pos,'Visible','off')
  604. case 0, set(types_menu(1),'Checked','on'); position_on = true;
  605. set(h_txt_pos,'Visible','on')
  606. end
  607. case 2,
  608. switch sound_on
  609. case 1, set(types_menu(2),'Checked','off'); sound_on = false;
  610. set(h_txt_sound,'Visible','off')
  611. case 0, set(types_menu(2),'Checked','on'); sound_on = true;
  612. set(h_txt_sound,'Visible','on')
  613. end
  614. case 3,
  615. switch color_on
  616. case 1, set(types_menu(3),'Checked','off'); color_on = false;
  617. set(h_txt_color,'Visible','off')
  618. set(handles.h_rect,'FaceColor','b')
  619. case 0, set(types_menu(3),'Checked','on'); color_on = true;
  620. set(h_txt_color,'Visible','on')
  621. end
  622. end
  623. end
  624. % Change nBack Level:
  625. function change_nBack(~,~,nBack_spec)
  626. nback = nBack_spec;
  627. for ix1 = 1:20
  628. set(h_nBack(ix1),'Checked','off');
  629. end
  630. set(h_nBack(nback),'Checked','on')
  631. set(handles.figure,'name',['nback_matlab: (',num2str(nback),'-Back)'])
  632. end
  633. function change_lures(~,~,lure_spec)
  634. percLure = poss_lures(lure_spec);
  635. for ix1 = 1:21
  636. set(h_lures(ix1),'Checked','off');
  637. end
  638. set(h_lures(lure_spec),'Checked','on')
  639. end
  640. % Change N Position Matches:
  641. function change_position_matches(~,~,change_n)
  642. n_positionHits = change_n;
  643. for ixx = 1:n_poss
  644. set(h_n_position_matches(ixx),'Checked','off');
  645. end
  646. set(h_n_position_matches(n_positionHits),'Checked','on')
  647. end
  648. % Change N Sound Matches:
  649. function change_sound_matches(~,~,change_n)
  650. n_soundHits = change_n;
  651. for ixx = 1:n_poss
  652. set(h_n_sound_matches(ixx),'Checked','off');
  653. end
  654. set(h_n_sound_matches(n_soundHits),'Checked','on')
  655. end
  656. % Change N Color Matches:
  657. function change_color_matches(~,~,change_n)
  658. n_colorHits = change_n;
  659. for ixx = 1:n_poss
  660. set(h_n_color_matches(ixx),'Checked','off');
  661. end
  662. set(h_n_color_matches(n_colorHits),'Checked','on')
  663. end
  664. % Change Completely Random:
  665. function change_completely_random(hObject,~,~)
  666. switch completely_random
  667. case 1, set(hObject,'Checked','off'); completely_random = 0;
  668. case 0, set(hObject,'Checked','on'); completely_random = 1;
  669. end
  670. end
  671. % Change Advance Threshold:
  672. function change_advance_thresh(~,~,advance_spec)
  673. advance_thresh = advance_thresholds(advance_spec);
  674. for ixx = 1:length(advance_thresholds)
  675. set(h_advances(ixx),'Checked','off')
  676. end
  677. set(h_advances(advance_spec),'Checked','on')
  678. end
  679. % Change Advance Threshold:
  680. function change_fallback_thresh(~,~,fallback_spec)
  681. fallback_thresh = fallback_thresholds(fallback_spec);
  682. for ixx = 1:length(fallback_thresholds)
  683. set(h_fallbacks(ixx),'Checked','off')
  684. end
  685. set(h_fallbacks(fallback_spec),'Checked','on')
  686. end
  687. % Change # Trials:
  688. function change_trial_num(~,~,trial_num_spec)
  689. if (trial_num_spec > 1)
  690. nTrials = trial_num_opts(trial_num_spec-1);
  691. else
  692. nTrials = nback + 20;
  693. end
  694. for ixx = 1:n_trial_num_opts+1
  695. set(h_trial_num(ixx),'Checked','off')
  696. end
  697. set(h_trial_num(trial_num_spec),'Checked','on')
  698. end
  699. % Change Trial Time:
  700. function change_trial_time(~,~,trial_time_spec)
  701. trial_time = trial_time_opts(trial_time_spec);
  702. for ixx = 1:n_trial_time_opts
  703. set(h_trial_times(ixx),'Checked','off')
  704. end
  705. set(h_trial_times(trial_time_spec),'Checked','on')
  706. end
  707. % Change Show Remaining Trials:
  708. function change_show_remaining(hObject,~,~)
  709. switch show_remaining
  710. case 1, set(hObject,'Checked','off'); show_remaining = 0;
  711. case 0, set(hObject,'Checked','on'); show_remaining = 1;
  712. end
  713. end
  714. function change_realtime_feedback(hObject,~,~)
  715. switch realtime_feedback
  716. case 1, set(hObject,'Checked','off'); realtime_feedback = 0;
  717. case 0, set(hObject,'Checked','on'); realtime_feedback = 1;
  718. end
  719. end
  720. function change_figure_feedback(hObject,~,~)
  721. switch figure_window_feedback
  722. case 1, set(hObject,'Checked','off'); figure_window_feedback = 0;
  723. case 0, set(hObject,'Checked','on'); figure_window_feedback = 1;
  724. end
  725. end
  726. function change_cmd_feedback(hObject,~,~)
  727. switch command_line_feedback
  728. case 1, set(hObject,'Checked','off'); command_line_feedback = 0;
  729. case 0, set(hObject,'Checked','on'); command_line_feedback = 1;
  730. end
  731. end
  732. % Change Sound Settings:
  733. function change_volume(~,~,~)
  734. if ~ishandle(h_volume)
  735. h_volume = figure('menubar','none','color',background_color,'numbertitle',...
  736. 'off','name','Adjust Volume','units','norm','Position',[.4729,.8646,.2174,.0352]);
  737. uicontrol(h_volume,'Style','slider','units','normalized','Position',...
  738. [0,.2,1,.6],'Min',0,'Max',1.2,'Value',volume_factor,'Callback',@change_volume_slider);
  739. else figure(h_volume)
  740. end
  741. end
  742. function change_volume_slider(hObject,~,~)
  743. volume_factor = hObject.Value;
  744. end
  745. function change_sound_types(~,~,which_type)
  746. for ixx = 1:4; set(h_sound_types(ixx),'Checked','off'); end
  747. set(h_sound_types(which_type),'Checked','on')
  748. sound_type = which_type;
  749. end
  750. function change_applause_setting(hObject,~,~)
  751. switch enable_applause
  752. case 1, set(hObject,'Checked','off'); enable_applause = 0;
  753. case 0, set(hObject,'Checked','on'); enable_applause = 1;
  754. end
  755. end
  756. function change_boos_setting(hObject,~,~)
  757. switch enable_boos
  758. case 1, set(hObject,'Checked','off'); enable_boos = 0;
  759. case 0, set(hObject,'Checked','on'); enable_boos = 1;
  760. end
  761. end
  762. % Appearance:
  763. function change_background(~,~,which_background)
  764. switch which_background
  765. case 1,
  766. if strcmp(h_background_menu1.Checked,'on')
  767. background_color_scheme = 2;
  768. colors = {'y','m','c','r','g','b','k'};
  769. set(h_background_menu1,'Checked','off')
  770. set(h_background_menu2,'Checked','on')
  771. elseif strcmp(h_background_menu1.Checked,'off')
  772. background_color_scheme = 1;
  773. colors = {'y','m','c','r','g','b','w'};
  774. set(h_background_menu1,'Checked','on')
  775. set(h_background_menu2,'Checked','off')
  776. end
  777. case 2,
  778. if strcmp(h_background_menu2.Checked,'on')
  779. background_color_scheme = 1;
  780. colors = {'y','m','c','r','g','b','w'};
  781. set(h_background_menu1,'Checked','on')
  782. set(h_background_menu2,'Checked','off')
  783. elseif strcmp(h_background_menu2.Checked,'off')
  784. background_color_scheme = 2;
  785. colors = {'y','m','c','r','g','b','k'};
  786. set(h_background_menu1,'Checked','off')
  787. set(h_background_menu2,'Checked','on')
  788. end
  789. end
  790. if background_color_scheme==1
  791. txt_color = ones(1,3);
  792. set(handles.figure,'Color',zeros(1,3))
  793. elseif background_color_scheme==2
  794. txt_color = zeros(1,3);
  795. set(handles.figure,'Color',ones(1,3))
  796. end
  797. for ixxx = 1:4
  798. set(h_grid_lines(ixxx),'Color',txt_color)
  799. end
  800. set(h_title,'Color',txt_color); set(h_txt_begin,'Color',txt_color)
  801. set(h_txt_pos,'Color',txt_color); set(h_txt_color,'Color',txt_color)
  802. set(h_txt_sound,'Color',txt_color)
  803. end
  804. % Write Current Trial Data:
  805. function writeData(pos_score, pos_lure_errors, H_pos, F_pos, ...
  806. sound_score, sound_lure_errors, H_sound, F_sound, ...
  807. color_score, color_lure_errors, H_color, F_color, trials, times, ...
  808. position_matches, pos_lures, position_user, position_stimuli,...
  809. sound_matches, sound_lures, sound_user, sound_stimuli, ...
  810. color_matches, color_lures, color_user, color_stimuli, extraKeys)
  811. if ~isdir(fullfile(script_path,'UserData')); mkdir(fullfile(script_path,'UserData')); end
  812. dataPath = fullfile(script_path,'UserData','user_data.mat');
  813. if exist(dataPath,'file')
  814. load(dataPath);
  815. summaryStats2 = table({char(datetime)}, nback, pos_score, ...
  816. pos_lure_errors, H_pos, F_pos, sound_score, ...
  817. sound_lure_errors, H_sound, F_sound, color_score, ...
  818. color_lure_errors, H_color, F_color,'VariableNames',...
  819. {'date_time','nback_level',...
  820. 'A_pos','lure_errors_pos','hits_pos','false_alarms_pos',...
  821. 'A_sound','lure_errors_sound','hits_sound','false_alarms_sound',...
  822. 'A_color','lure_errors_color','hits_color','false_alarms_color'});
  823. summaryStats = [summaryStats; summaryStats2]; %#ok
  824. trialData{length(trialData)+1} = table(trials, times, ...
  825. position_matches,pos_lures, position_user, position_stimuli, ...
  826. sound_matches, sound_lures, sound_user, sound_stimuli, ...
  827. color_matches, color_lures, color_user, color_stimuli, extraKeys,...
  828. 'VariableNames',{'trial_num','seconds_from_start',...
  829. 'position_matches','position_lures','user_position_matches','position_stimuli',...
  830. 'sound_matches','sound_lures','user_sound_matches','sound_stimuli',...
  831. 'color_matches','color_lures','user_color_matches','color_stimuli','extra_keys'}); %#ok
  832. else
  833. summaryStats = table({char(datetime)}, nback, pos_score, ...
  834. pos_lure_errors, H_pos, F_pos, sound_score, ...
  835. sound_lure_errors, H_sound, F_sound, color_score, ...
  836. color_lure_errors, H_color, F_color,'VariableNames',...
  837. {'date_time','nback_level',...
  838. 'A_pos','lure_errors_pos','hits_pos','false_alarms_pos',...
  839. 'A_sound','lure_errors_sound','hits_sound','false_alarms_sound',...
  840. 'A_color','lure_errors_color','hits_color','false_alarms_color'}); %#ok
  841. trialData{1} = table(trials, times, ...
  842. position_matches,pos_lures, position_user, position_stimuli, ...
  843. sound_matches, sound_lures, sound_user, sound_stimuli, ...
  844. color_matches, color_lures, color_user, color_stimuli, extraKeys,...
  845. 'VariableNames',{'trial_num','seconds_from_start',...
  846. 'position_matches','position_lures','user_position_matches','position_stimuli',...
  847. 'sound_matches','sound_lures','user_sound_matches','sound_stimuli',...
  848. 'color_matches','color_lures','user_color_matches','color_stimuli','extra_keys'}); %#ok
  849. end
  850. save(dataPath,'summaryStats','trialData')
  851. end
  852. % Get Default Settings:
  853. function get_defaults
  854. nback = 2; % nBack level
  855. percLure = .2; % proportion of non-match trials to become lures
  856. nTrials = nback + 20;
  857. trial_time = 2.4; % seconds
  858. sound_on = true; % sound-type nBack: 1 (on), 0 (off)
  859. position_on = true; % position-type nBack: 1 (on), 0 (off)
  860. color_on = false; % color-type nBack: 1 (on), 0 (off)
  861. completely_random = 0; % off (0), on (1) (if on, next 3 settings are irrelevant)
  862. n_positionHits = 4; % control # of matches for position
  863. n_soundHits = 4; % control # of matches for sound
  864. n_colorHits = 4; % control # of matches for color
  865. advance_thresh = .9; % A' default
  866. fallback_thresh = .75; % A' default
  867. volume_factor = 1; % Default: 1 (no change); >1 = increase volume; <1 = decrease volume
  868. sound_type = 3; % use Numbers-Female (1), Numbers-Male (2), Letters-Female1 (3), or Letters-Female2 (4)
  869. enable_applause = 1; % 1 (on) or 0 (off)
  870. enable_boos = 0; % 1 (on) or 0 (off); comical boos if fail to advance to next nBack
  871. realtime_feedback = 1; % 1 (on) or 0 (off); highlight labels red/green based on accuracy
  872. figure_window_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in figure window upon completion
  873. command_line_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in command window upon completion
  874. show_remaining = 1; % 1 (on) or 0 (off); show remaining trials as title
  875. background_color_scheme = 1; % black (1), white (2)
  876. end
  877. % Get Custom Settings:
  878. function success = get_settings
  879. if ~isdir(fullfile(script_path,'UserData')); mkdir(fullfile(script_path,'UserData')); end
  880. listing = dir(fullfile(script_path,'UserData','nback_matlab_settings.txt'));
  881. if isempty(listing);
  882. success = 0;
  883. get_defaults
  884. return;
  885. end
  886. nback_matlab_settings = importdata(fullfile(script_path,'UserData',listing(1).name));
  887. for ix1 = 1:length(customizable); eval(nback_matlab_settings{ix1}); end
  888. success = 1;
  889. end
  890. % Write Custom Settings:
  891. function write_settings
  892. % Change Settings Data:
  893. nback_matlab_settings = cell(length(customizable),1);
  894. for ix1 = 1:length(customizable)
  895. setting1 = eval(customizable{ix1});
  896. nback_matlab_settings{ix1} = [customizable{ix1},'=',num2str(setting1),';'];
  897. end
  898. nback_matlab_settings = char(nback_matlab_settings);
  899. % Write Text File:
  900. if ~isdir(fullfile(script_path,'UserData')); mkdir(fullfile(script_path,'UserData')); end
  901. fileID = fopen(fullfile(script_path,'UserData','nback_matlab_settings.txt'),'w');
  902. for ix1 = 1:length(customizable)
  903. fprintf(fileID,'%s\r\n',nback_matlab_settings(ix1,:));
  904. end
  905. fclose(fileID);
  906. end
  907. % Close Requests:
  908. function close_nback_matlab(varargin)
  909. % Save Custom Settings:
  910. write_settings
  911. % Close:
  912. delete(handles.figure)
  913. end
  914. function enableMenus
  915. set(handles.figure,'resize','on')
  916. set(file_menu,'Enable','on'); set(session_menu,'Enable','on')
  917. set(nBack_type_menu,'Enable','on'); set(feedback_menu,'Enable','on')
  918. set(sound_settings_menu,'Enable','on'); set(appearance_menu,'Enable','on')
  919. end
  920. function disableMenus
  921. clear sound % stop any sound if playing
  922. set(handles.figure,'resize','off')
  923. set(file_menu,'Enable','off')
  924. set(session_menu,'Enable','off')
  925. set(nBack_type_menu,'Enable','off')
  926. set(feedback_menu,'Enable','off')
  927. set(sound_settings_menu,'Enable','off')
  928. set(appearance_menu,'Enable','off')
  929. end
  930. function resizeScreen(varargin)
  931. if canResize
  932. % Determine which dimension needs shrinking to become square:
  933. for ixx = 1:4; set(h_grid_lines(ixx),'units','norm'); end
  934. set(h_grid_lines(1),'Y',[minMeasure,maxMeasure])
  935. set(h_grid_lines(2),'Y',[minMeasure,maxMeasure])
  936. set(h_grid_lines(3),'X',[minMeasure,maxMeasure])
  937. set(h_grid_lines(4),'X',[minMeasure,maxMeasure])
  938. % Change units to points and shrink width or height to match:
  939. for ixx = 1:4; set(h_grid_lines(ixx),'units','points'); end
  940. ySpec = get(h_grid_lines(1),'Y'); xSpec = get(h_grid_lines(3),'X');
  941. height = diff(ySpec); width = diff(xSpec);
  942. axesRatio = height/width;
  943. diffPixels = abs(height - width);
  944. if height > width % shrink height to match
  945. set(h_grid_lines(1),'Y',[ySpec(1) + diffPixels/2, ySpec(2) - diffPixels/2])
  946. set(h_grid_lines(2),'Y',[ySpec(1) + diffPixels/2, ySpec(2) - diffPixels/2])
  947. elseif height < width % shrink width to match
  948. set(h_grid_lines(3),'X',[xSpec(1) + diffPixels/2, xSpec(2) - diffPixels/2])
  949. set(h_grid_lines(4),'X',[xSpec(1) + diffPixels/2, xSpec(2) - diffPixels/2])
  950. end
  951. % Change units back to normalized, and adjust grid spacing:
  952. for ixx = 1:4; set(h_grid_lines(ixx),'units','norm'); end
  953. xSpec = get(h_grid_lines(3),'X'); ySpec = get(h_grid_lines(1),'Y');
  954. oneThirdX = (diff(xSpec)/3) + xSpec(1);
  955. twoThirdsX = xSpec(2) - (diff(xSpec)/3);
  956. oneThirdY = (diff(ySpec)/3) + ySpec(1);
  957. twoThirdsY = ySpec(2) - (diff(ySpec)/3);
  958. set(h_grid_lines(1),'X',[oneThirdX, oneThirdX])
  959. set(h_grid_lines(2),'X',[twoThirdsX, twoThirdsX])
  960. set(h_grid_lines(3),'Y',[oneThirdY, oneThirdY])
  961. set(h_grid_lines(4),'Y',[twoThirdsY, twoThirdsY])
  962. % Square Positions:
  963. ylim(handles.axes1,[0,axesRatio]); % adjust Y-axes limits based on its ratio w/ X
  964. square_width = .95*(twoThirdsX-oneThirdX);
  965. useGapX = ((twoThirdsX-oneThirdX)-square_width)/2;
  966. oneThirdY = oneThirdY*axesRatio; twoThirdsY = twoThirdsY*axesRatio;
  967. useGapY = ((twoThirdsY-oneThirdY)-square_width)/2;
  968. positions = repmat([.05,.05,square_width,square_width],9,1);
  969. positions(1,1:2) = [xSpec(1)+useGapX,twoThirdsY+useGapY];
  970. positions(2,1:2) = [oneThirdX+useGapX,twoThirdsY+useGapY];
  971. positions(3,1:2) = [twoThirdsX+useGapX,twoThirdsY+useGapY];
  972. positions(4,1:2) = [xSpec(1)+useGapX,oneThirdY+useGapY];
  973. positions(5,1:2) = [oneThirdX+useGapX,oneThirdY+useGapY];
  974. positions(6,1:2) = [twoThirdsX+useGapX,oneThirdY+useGapY];
  975. positions(7,1:2) = [xSpec(1)+useGapX,ySpec(1)*axesRatio+useGapY];
  976. positions(8,1:2) = [oneThirdX+useGapX,ySpec(1)*axesRatio+useGapY];
  977. positions(9,1:2) = [twoThirdsX+useGapX,ySpec(1)*axesRatio+useGapY];
  978. % Adjust match button text positions:
  979. set(h_txt_pos,'position',[median([xSpec(1),oneThirdX]),ySpec(1)*axesRatio])
  980. set(h_txt_color,'position',[median([oneThirdX, twoThirdsX]),ySpec(1)*axesRatio])
  981. set(h_txt_sound,'position',[median([twoThirdsX,xSpec(2)]),ySpec(1)*axesRatio])
  982. % Adjust Feedback Positions:
  983. h_pos_feedback1_pos = [xSpec(1)+.005*xSpec(1), median([ySpec(1)*axesRatio, oneThirdY])];
  984. h_pos_feedback2_pos = [oneThirdX-.005*oneThirdX, median([ySpec(1)*axesRatio, oneThirdY])];
  985. h_color_feedback1_pos = [oneThirdX+.005*oneThirdX, median([ySpec(1)*axesRatio, oneThirdY])];
  986. h_color_feedback2_pos = [twoThirdsX-.005*twoThirdsX, median([ySpec(1)*axesRatio, oneThirdY])];
  987. h_sound_feedback1_pos = [twoThirdsX+.005*twoThirdsX, median([ySpec(1)*axesRatio, oneThirdY])];
  988. h_sound_feedback2_pos = [xSpec(2)-.005*xSpec(2), median([ySpec(1)*axesRatio, oneThirdY])];
  989. if ishandle(h_pos_feedback1); set(h_pos_feedback1,'position',h_pos_feedback1_pos); end
  990. if ishandle(h_pos_feedback2); set(h_pos_feedback2,'position',h_pos_feedback2_pos); end
  991. if ishandle(h_color_feedback1); set(h_color_feedback1,'position',h_color_feedback1_pos); end
  992. if ishandle(h_color_feedback2); set(h_color_feedback2,'position',h_color_feedback2_pos); end
  993. if ishandle(h_sound_feedback1); set(h_sound_feedback1,'position',h_sound_feedback1_pos); end
  994. if ishandle(h_sound_feedback2); set(h_sound_feedback2,'position',h_sound_feedback2_pos); end
  995. end
  996. end
  997. function [idx, matches, lures, countMatches, countLures, wasSuccess] = generateIdx(nTrials, nback, nMatches, percLure, nStimuli)
  998. wasSuccess = true;
  999. nPassLimit = 30;
  1000. if nback==1
  1001. nLures = round(percLure * (nTrials - nMatches));
  1002. else
  1003. nLures = round(percLure * (nTrials - nMatches - nback + 1));
  1004. end
  1005. %% 1. Add matches:
  1006. idx = zeros(1,nTrials); nMatchesReal = 0; nLuresReal = 0; nPasses = 0;
  1007. while (nMatchesReal ~= nMatches || nLuresReal > nLures) && nPasses<nPassLimit
  1008. nPasses = nPasses + 1;
  1009. matchInd = find(idx==0); matchInd(matchInd<=nback) = [];
  1010. matchInd = matchInd(randperm(length(matchInd),length(matchInd)));
  1011. if nMatchesReal < nMatches
  1012. if idx(matchInd(1)-nback)>0 % match already present
  1013. idx(matchInd(1)) = idx(matchInd(1)-nback);
  1014. else % match not present
  1015. idx(matchInd(1)) = randi(nStimuli);
  1016. idx(matchInd(1)-nback) = idx(matchInd(1));
  1017. end
  1018. elseif ((nMatchesReal > nMatches) && (nLuresReal <= nLures))
  1019. opts = find(idx>0);
  1020. if ~isempty(opts)
  1021. idx(opts(randi(length(opts),1))) = 0;
  1022. idx = clearNonMatches(idx, nback);
  1023. end
  1024. elseif (nLuresReal > nLures)
  1025. [~, lureInd] = findLures(idx,nback);
  1026. idx(lureInd) = 0;
  1027. end
  1028. nMatchesReal = findMatches(idx, nback); nLuresReal = findLures(idx, nback);
  1029. end
  1030. idx = clearNonMatches(idx, nback);
  1031. %% 2. Add Lures:
  1032. lurePoss = find(idx==0);
  1033. if nback>1;
  1034. lurePoss(lurePoss<nback) = [];
  1035. else lurePoss(lurePoss<nback+1) = [];
  1036. end
  1037. lurePossShuffled = lurePoss(randperm(length(lurePoss),length(lurePoss)));
  1038. [~, whereMatches] = findMatches(idx, nback);
  1039. whereMatches = find(whereMatches);
  1040. whereMatches = [whereMatches, whereMatches-nback];
  1041. nPasses = 0; ixx = 0; nLuresReal = findLures(idx, nback);
  1042. while nLuresReal~=nLures && ixx<length(lurePossShuffled) && nPasses < nPassLimit
  1043. nPasses = nPasses + 1;
  1044. if nLuresReal < nLures
  1045. % Iterate:
  1046. ixx = ixx + 1;
  1047. lureInd = lurePossShuffled(ixx);
  1048. % Check if viable index:
  1049. if idx(lureInd)~=0; continue; end
  1050. % Choose Lure Type (1 or 2):
  1051. onlyOneOption = false;
  1052. if lureInd-nback-1 <= 0 || any(whereMatches == lureInd-nback-1)
  1053. lureType = 2; onlyOneOption = true;
  1054. if lureInd-nback+1<=0
  1055. continue; % can't add this lure at all
  1056. end
  1057. elseif lureInd-nback+1 <= 0 || any(whereMatches == lureInd-nback+1)
  1058. continue;
  1059. else
  1060. lureType = randi(2);
  1061. end
  1062. % Find stimuli to avoid at this index:
  1063. toAvoid = whichToAvoid(lureInd, lureType, idx, nback);
  1064. indOpts = setdiff(1:nStimuli, toAvoid);
  1065. useStimulus = indOpts(randi(length(indOpts),1));
  1066. if (lureType==1)
  1067. if (idx(lureInd-nback-1)==0)
  1068. idx(lureInd) = useStimulus;
  1069. idx(lureInd-nback-1) = useStimulus;
  1070. elseif (idx(lureInd-nback-1)~=idx(lureInd-nback)) && (lureInd+nback>nTrials || idx(lureInd-nback-1)~=idx(lureInd+nback)) % if -nBack-1 already has a lure, just use that stimulus for new lure:
  1071. idx(lureInd) = idx(lureInd-nback-1);
  1072. elseif onlyOneOption
  1073. continue;
  1074. else % try adding -nBack+1 instead
  1075. toAvoid = whichToAvoid(lureInd, 2, idx, nback);
  1076. indOpts = setdiff(1:nStimuli, toAvoid);
  1077. useStimulus = indOpts(randi(length(indOpts),1));
  1078. if (idx(lureInd-nback+1)==0)
  1079. idx(lureInd) = useStimulus;
  1080. idx(lureInd - nback + 1) = useStimulus;
  1081. elseif ((lureInd-nback<=0 || idx(lureInd - nback + 1) ~= idx(lureInd - nback)) && (lureInd+nback>nTrials || idx(lureInd - nback + 1) ~= idx(lureInd + nback)))
  1082. idx(lureInd) = idx(lureInd - nback + 1);
  1083. else
  1084. continue;
  1085. end
  1086. end
  1087. elseif (lureType==2)
  1088. if (idx(lureInd-nback+1)==0)
  1089. idx(lureInd) = useStimulus;
  1090. idx(lureInd - nback + 1) = useStimulus;
  1091. elseif ((lureInd-nback<=0 || idx(lureInd - nback + 1) ~= idx(lureInd - nback)) && (lureInd+nback>nTrials || idx(lureInd - nback + 1) ~= idx(lureInd + nback)))
  1092. idx(lureInd) = idx(lureInd - nback + 1);
  1093. elseif (onlyOneOption)
  1094. continue;
  1095. else % Try adding -nBack-1 instead:
  1096. toAvoid = whichToAvoid(lureInd, 1, idx, nback);
  1097. indOpts = setdiff(1:nStimuli, toAvoid);
  1098. useStimulus = indOpts(randi(length(indOpts),1));
  1099. if (idx(lureInd-nback-1)==0)
  1100. idx(lureInd) = useStimulus;
  1101. idx(lureInd - nback - 1) = useStimulus;
  1102. elseif ((lureInd-nback-1<=0 || idx(lureInd - nback - 1) ~= idx(lureInd-nback)) && (lureInd+nback>nTrials || idx(lureInd - nback - 1) ~= idx(lureInd + nback)))
  1103. idx(lureInd) = idx(lureInd - nback - 1);
  1104. else
  1105. continue;
  1106. end
  1107. end
  1108. end
  1109. % Count up the # of lures (new lures can be created unexpectedly):
  1110. nLuresReal = findLures(idx, nback);
  1111. elseif nLuresReal > nLures
  1112. opts = lurePossShuffled(1:ixx);
  1113. if ~isempty(opts)
  1114. idx(opts(randi(length(opts),1))) = 0;
  1115. end
  1116. % Count up the # of lures (new lures can be created unexpectedly):
  1117. nLuresReal = findLures(idx, nback);
  1118. end
  1119. end
  1120. % If still too few lures, add more wherever gaps remain:
  1121. remainingPossible = find(idx==0); nPasses = 0;
  1122. while nLuresReal~=nLures && ~isempty(remainingPossible) && nPasses<nPassLimit
  1123. nPasses = nPasses + 1;
  1124. if nLuresReal < nLures
  1125. for ixx = remainingPossible
  1126. possible = [];
  1127. if (ixx+nback-1 <= nTrials)
  1128. if ((ixx+nback>nTrials || idx(ixx+nback-1)~=idx(ixx+nback)) && (ixx-nback<=0 || idx(ixx+nback-1)~=idx(ixx-nback)))
  1129. possible = [possible, idx(ixx+nback-1)]; %#ok
  1130. end
  1131. if (ixx+nback+1 <= nTrials)
  1132. if (idx(ixx+nback+1)~=idx(ixx+nback) && (ixx-nback<=0 || idx(ixx+nback+1)~=idx(ixx-nback)))
  1133. possible = [possible, idx(ixx+nback+1)]; %#ok
  1134. end
  1135. end
  1136. end
  1137. if (ixx-nback+1 > 0)
  1138. if ((ixx-nback<=0 || idx(ixx-nback+1)~=idx(ixx-nback)) && (ixx+nback>nTrials || idx(ixx-nback+1)~=idx(ixx+nback)))
  1139. possible = [possible, idx(ixx-nback+1)]; %#ok
  1140. end
  1141. if (ixx-nback-1 > 0)
  1142. if (idx(ixx-nback-1)~=idx(ixx-nback) && (ixx+nback>nTrials || idx(ixx-nback-1)~=idx(ixx+nback)))
  1143. possible = [possible, idx(ixx-nback-1)]; %#ok
  1144. end
  1145. end
  1146. end
  1147. possible(possible==0) = []; %#ok
  1148. if ~isempty(possible)
  1149. idx(ixx) = possible(randi(length(possible),1));
  1150. nLuresReal = findLures(idx, nback);
  1151. remainingPossible = find(idx==0);
  1152. if (nLuresReal >= nLures)
  1153. break;
  1154. end
  1155. else % if is empty, cannot add lure
  1156. remainingPossible(remainingPossible==ixx) = [];
  1157. end
  1158. end
  1159. elseif nLuresReal > nLures
  1160. opts = lurePossShuffled(1:min(ixx,length(lurePossShuffled)));
  1161. if ~isempty(opts)
  1162. idx(opts(randi(length(opts),1))) = 0;
  1163. end
  1164. % Count up the # of lures (new lures can be created unexpectedly):
  1165. nLuresReal = findLures(idx, nback);
  1166. remainingPossible = find(idx==0);
  1167. end
  1168. end
  1169. %% 3. Add non-lures & non-matches:
  1170. for ixx = find(idx==0)
  1171. toAvoid = [];
  1172. if (ixx-nback+1 > 0)
  1173. if (idx(ixx-nback+1) ~= 0)
  1174. toAvoid = [toAvoid, idx(ixx-nback+1)]; %#ok
  1175. end
  1176. if (ixx-nback > 0)
  1177. if (idx(ixx-nback) ~= 0)
  1178. toAvoid = [toAvoid, idx(ixx-nback)]; %#ok
  1179. end
  1180. if (ixx-nback-1 > 0)
  1181. if (idx(ixx-nback-1) ~= 0)
  1182. toAvoid = [toAvoid, idx(ixx-nback-1)]; %#ok
  1183. end
  1184. end
  1185. end
  1186. end
  1187. if (ixx+nback-1 <= nTrials)
  1188. if (idx(ixx+nback-1) ~= 0)
  1189. toAvoid = [toAvoid, idx(ixx+nback-1)]; %#ok
  1190. end
  1191. if (ixx+nback <= nTrials)
  1192. if (idx(ixx+nback) ~= 0)
  1193. toAvoid = [toAvoid, idx(ixx+nback)]; %#ok
  1194. end
  1195. if (ixx+nback+1 <= nTrials)
  1196. if (idx(ixx+nback+1) ~= 0)
  1197. toAvoid = [toAvoid, idx(ixx+nback+1)]; %#ok
  1198. end
  1199. end
  1200. end
  1201. end
  1202. indOpts = setdiff(1:nStimuli, toAvoid);
  1203. idx(ixx) = indOpts(randi(length(indOpts),1));
  1204. end
  1205. %% Check final counts of matches and lures:
  1206. [countMatches, matches] = findMatches(idx, nback);
  1207. if countMatches~=nMatches;
  1208. warning(['# matches, target : ',num2str(countMatches),', ',num2str(nMatches)]);
  1209. wasSuccess = false;
  1210. end
  1211. [countLures, lures] = findLures(idx, nback);
  1212. if countLures~=nLures && ~isempty(remainingPossible) %&& nPasses < nPassLimit
  1213. warning(['# lures, target : ',num2str(countLures),', ',num2str(nLures)]);
  1214. wasSuccess = false;
  1215. return;
  1216. end
  1217. %% Helper Functions:
  1218. function [nMatches, matches] = findMatches(vec, nback)
  1219. matches = false(size(vec)); nMatches = 0;
  1220. for ixxx = 1:length(vec)
  1221. if vec(ixxx)>0 && ixxx>nback && vec(ixxx)==vec(ixxx-nback)
  1222. matches(ixxx) = true;
  1223. nMatches = nMatches + 1;
  1224. end
  1225. end
  1226. end
  1227. function [nLures, lures] = findLures(vec,nback)
  1228. nLures = 0; lures = false(size(vec));
  1229. for ixxx = 1:length(vec)
  1230. if vec(ixxx)~=0
  1231. if (ixxx-nback<=0 || vec(ixxx)~=vec(ixxx-nback)) % assure not a match
  1232. if (ixxx-nback+1>0 && vec(ixxx)==vec(ixxx-nback+1))
  1233. nLures = nLures+1;
  1234. lures(ixxx) = true;
  1235. continue;
  1236. end
  1237. if ixxx-nback-1>0 && vec(ixxx)==vec(ixxx-nback-1)
  1238. nLures = nLures+1;
  1239. lures(ixxx) = true;
  1240. continue;
  1241. end
  1242. end
  1243. end
  1244. end
  1245. end
  1246. function avoid = whichToAvoid(lureInd, whichLureType, idx, nBackNum)
  1247. avoid = [];
  1248. if (lureInd+nBackNum <= length(idx)) && idx(lureInd+nBackNum)~=0 % Avoid new match forward
  1249. avoid = [avoid, idx(lureInd+nBackNum)];
  1250. end
  1251. if (lureInd-nBackNum>0) && idx(lureInd-nBackNum)~=0 % Avoid new match backward
  1252. avoid = [avoid, idx(lureInd-nBackNum)];
  1253. end
  1254. if (whichLureType==1)
  1255. if (lureInd-nBackNum-1-nBackNum>0) && idx(lureInd-nBackNum-1-nBackNum)~=0 % Avoid creating new match
  1256. avoid = [avoid, idx(lureInd-nBackNum-1-nBackNum)];
  1257. end
  1258. if (idx(lureInd-nBackNum-1+nBackNum) ~= 0) % Avoid creating new match
  1259. avoid = [avoid, idx(lureInd-nBackNum-1+nBackNum)];
  1260. end
  1261. elseif (whichLureType==2)
  1262. if (lureInd-nBackNum-nBackNum+1 > 0) && idx(lureInd-nBackNum-nBackNum+1)~=0 % Avoid creating new match
  1263. avoid = [avoid, idx(lureInd-nBackNum-nBackNum+1)];
  1264. end
  1265. if (lureInd+1<=length(idx)) && idx(lureInd+1)~=0 % Avoid creating new match
  1266. avoid = [avoid, idx(lureInd+1)];
  1267. end
  1268. end
  1269. end
  1270. function vec = clearNonMatches(vec, nback)
  1271. for ixxx = 1:length(vec)
  1272. if vec(ixxx)>0 && ~((ixxx>nback && vec(ixxx)==vec(ixxx-nback)) || (ixxx+nback<=length(vec) && vec(ixxx)==(vec(ixxx+nback))))
  1273. vec(ixxx) = 0;
  1274. end
  1275. end
  1276. end
  1277. end
  1278. end