nback_matlab.m 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  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.
  8. % A 'UserData' subfolder will be written to the same location as
  9. % 'nback_matlab.m the first time the program is run. User performance
  10. % data will be stored in a text file within this folder
  11. % ('nback_matlab_progress.txt'). Also, whenever the program closes, the
  12. % 'nback_matlab_settings.txt' file will be updated with the user's most
  13. % recent preferences (this will be loaded upon program reboot).
  14. %
  15. % *Scoring: A' (Discrimination Index), similar to d'
  16. % H = Hit Rate (hits / # signal trials),
  17. % F = False-positive Rate (false pos / # noise trials)
  18. % aPrime = .5 + sign(H - F) * ((H - F)^2 + abs(H - F)) / (4 * max(H, F) - 4 * H * F);
  19. %
  20. % A' references:
  21. % Snodgrass, J. G., & Corwin, J. (1988). Pragmatics of measuring recognition
  22. % memory: applications to dementia and amnesia. Journal of Experimental
  23. % Psychology: General, 117(1), 34.
  24. %
  25. % Stanislaw, H., & Todorov, N. (1999). Calculation of signal detection theory
  26. % measures. Behavior research methods, instruments, & computers, 31(1),
  27. % 137-149.
  28. %
  29. % *For mathematical formula, see Stanislaw & Todorov (1999, p. 142)
  30. %
  31. % *Default Settings:
  32. % nback = 2; % nBack level
  33. % sound_on = 1; % sound-type nBack: 1 (on), 0 (off)
  34. % position_on = 1; % position-type nBack: 1 (on), 0 (off)
  35. % color_on = 0; % color-type nBack: 1 (on), 0 (off)
  36. % completely_random = 0; % off (0), on (1) (if on, next 3 settings are irrelevant)
  37. % n_positionHits = 4; % control # of matches for position
  38. % n_soundHits = 4; % control # of matches for sound
  39. % n_colorHits = 4; % control # of matches for color
  40. % advance_thresh = .9; % minimum score (A') required to advance automatically to next nBack level;
  41. % fallback_thresh = .75; % score (A') below which one automatically regresses to previous nBack level;
  42. % nTrials = nback + 20; % # trials
  43. % trial_time = 2.4; % seconds
  44. % volume_factor = 1; % Default: 1 (no change); >1 = increase volume; <1 = decrease volume
  45. % sound_type = 1; % use Numbers-Female (1), Numbers-Male (2), Letters-Female1 (3), or Letters-Female2 (4)
  46. % enable_applause = 1; % 1 (on) or 0 (off); applause on advance to next n-back level
  47. % enable_boos = 0; % 1 (on) or 0 (off); comical boos if fallback to previous n-back level
  48. % realtime_feedback = 1; % 1 (on) or 0 (off); highlight labels red/green based on accuracy
  49. % figure_window_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in figure window upon completion
  50. % command_line_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in command window upon completion
  51. % show_remaining = 1; % 1 (on) or 0 (off); show remaining trials
  52. % background_color_scheme = 1; % black (1), white (2)
  53. %
  54. % *Author: Elliot A. Layden (2017-18),
  55. % at The Environmental Neuroscience Laboratory, The University of Chicago
  56. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  57. %% Hotkey Designations:
  58. startSessionKey = 'space'; % 's'
  59. positionMatchKey = 'a'; % '4'
  60. soundMatchKey = 'l'; % '6'
  61. colorMatchKey = 'j';
  62. stopRunningKey = 'escape';
  63. %% ------------------------ Begin ------------------------
  64. customizable = {'nback','sound_on','position_on','color_on','completely_random',...
  65. 'n_soundHits','n_positionHits','n_colorHits','advance_thresh',...
  66. 'fallback_thresh','nTrials','trial_time','volume_factor',...
  67. 'sound_type','enable_applause','enable_boos','realtime_feedback',...
  68. 'command_line_feedback','figure_window_feedback','show_remaining',...
  69. 'background_color_scheme'};
  70. % Initialize Global Variables:
  71. curr_running = 0; stop_running = 0; ix = 1; canResize = false;
  72. position_mem = []; color_mem = []; sound_mem = [];
  73. position_match_vec = []; sound_match_vec = []; color_match_vec = [];
  74. user_position_match = []; user_sound_match = []; user_color_match = [];
  75. advance_thresh = []; fallback_thresh = [];
  76. figure_window_feedback = []; command_line_feedback = []; volume_factor = [];
  77. h_pos_feedback1_pos = []; h_pos_feedback2_pos = [];
  78. h_color_feedback1_pos = []; h_color_feedback2_pos = [];
  79. h_sound_feedback1_pos = []; h_sound_feedback2_pos = [];
  80. % Initialize object handles:
  81. h_title = 10.384372; h_volume = 28.1717839;
  82. h_pos_feedback1 = 10.384372; h_pos_feedback2 = 10.384372;
  83. h_sound_feedback1 = 10.384372; h_sound_feedback2 = 10.384372;
  84. h_color_feedback1 = 10.384372; h_color_feedback2 = 10.384372;
  85. square_width = .2;
  86. positions = repmat([.05,.05,square_width,square_width],9,1);
  87. % Get script path and subfolders:
  88. script_fullpath = mfilename('fullpath');
  89. [script_path,~,~] = fileparts(script_fullpath);
  90. addpath(genpath(script_path))
  91. audio_path = fullfile(script_path,'Audio');
  92. audio_subfolders = {fullfile(audio_path,'numbers-female'),fullfile(audio_path,'numbers-male'),...
  93. fullfile(audio_path,'letters-female1'),fullfile(audio_path,'letters-female2')};
  94. % Get Settings:
  95. get_settings;
  96. % Calculate Parameters
  97. [~,nBack_spec] = min(abs((1:20)-nback));
  98. trial_num_opts = 15:5:100; % nTrials
  99. n_trial_num_opts = length(trial_num_opts);
  100. if nTrials==nback+20
  101. trial_num_spec = 1;
  102. else trial_num_spec = 1+min(abs(trial_num_opts-nTrials));
  103. end
  104. trial_time_opts = 1:.2:4; % seconds;
  105. n_trial_time_opts = length(trial_time_opts);
  106. [~,trial_time_spec] = min(abs(trial_time_opts-trial_time));
  107. % Appearance Customization:
  108. if background_color_scheme==1
  109. txt_color = ones(1,3);
  110. background_color = zeros(1,3);
  111. colors = {'y','m','c','r','g','b','w'};
  112. elseif background_color_scheme==2
  113. txt_color = zeros(1,3);
  114. background_color = ones(1,3);
  115. colors = {'y','m','c','r','g','b','k'};
  116. end
  117. % Load Main Audio:
  118. audio_dat = struct('sound',cell(1,4),'Fs',cell(1,4),'nSound',cell(1,4));
  119. for i = 1:4
  120. listing = dir(audio_subfolders{i});
  121. nSound = length(listing)-2;
  122. audio_dat(i).sound = cell(1,nSound);
  123. audio_dat(i).Fs = cell(1,nSound);
  124. audio_dat(i).nSound = nSound;
  125. for j = 1:nSound
  126. [audio_dat(i).sound{j},audio_dat(i).Fs{j}] = audioread(fullfile(audio_subfolders{i},listing(j+2).name));
  127. end
  128. end
  129. % Load Reaction Sounds (Applause/Boos):
  130. [applause_dat, Fs_applause] = audioread(fullfile(audio_path,'applause.mp3'));
  131. [boo_dat, Fs_boo] = audioread(fullfile(audio_path,'boo.mp3'));
  132. % Initialize Figure:
  133. handles.figure = figure('menubar','none','color',background_color,'numbertitle',...
  134. 'off','name',['nback_matlab: (',num2str(nback),'-Back)'],'units','norm',...
  135. 'Position',[.271,.109,.488,.802],'ResizeFcn',@resizeScreen);
  136. handles.axes1 = gca; set(handles.axes1,'Position',[0,0,1,1],'XLim',[0,1],...
  137. 'YLim',[0,1],'Visible','off');
  138. h_title = annotation('textbox','String','','FontName','Helvetica','FontSize',14,...
  139. 'Position',[.4,.955,.2,.05],'Color',txt_color,'HorizontalAlignment','center',...
  140. 'FontWeight','bold','EdgeColor','none');
  141. %% Menus:
  142. % File Menu:
  143. file_menu = uimenu(handles.figure,'Label','File');
  144. % uimenu(file_menu,'Label','Progress Report','Callback',@show_progress);
  145. uimenu(file_menu,'Label','Exit','Callback',@close_nback_matlab);
  146. % Session Menu:
  147. session_menu = uimenu(handles.figure,'Label','Session');
  148. % N-Back Level:
  149. nBack_num_menu = uimenu(session_menu,'Label','N-Back Level');
  150. h_nBack = zeros(1,20);
  151. for i = 1:20
  152. h_nBack(i) = uimenu(nBack_num_menu,'Label',sprintf('%02g',i),'Callback',{@change_nBack,i});
  153. end; set(h_nBack(nBack_spec),'Checked','on')
  154. % Stimuli Type(s) Menu:
  155. nBack_type_menu = uimenu(session_menu,'Label','Stimuli Type(s)');
  156. if position_on
  157. types_menu(1) = uimenu(nBack_type_menu,'Label','Position','Checked','on','Callback',{@change_types,1});
  158. else types_menu(1) = uimenu(nBack_type_menu,'Label','Position','Checked','off','Callback',{@change_types,1});
  159. end
  160. if sound_on
  161. types_menu(2) = uimenu(nBack_type_menu,'Label','Sound','Checked','on','Callback',{@change_types,2});
  162. else types_menu(2) = uimenu(nBack_type_menu,'Label','Sound','Checked','off','Callback',{@change_types,2});
  163. end
  164. if color_on
  165. types_menu(3) = uimenu(nBack_type_menu,'Label','Color','Checked','on','Callback',{@change_types,3});
  166. else types_menu(3) = uimenu(nBack_type_menu,'Label','Color','Checked','off','Callback',{@change_types,3});
  167. end
  168. trial_num_menu = uimenu(session_menu,'Label','# Trials');
  169. h_trial_num = zeros(1,n_trial_num_opts+1);
  170. h_trial_num(1) = uimenu(trial_num_menu,'Label','20 + N (default)','Callback',{@change_trial_num,1});
  171. for i = 2:n_trial_num_opts+1
  172. h_trial_num(i) = uimenu(trial_num_menu,'Label',sprintf('%1g',trial_num_opts(i-1)),'Callback',{@change_trial_num,i});
  173. end; set(h_trial_num(trial_num_spec),'Checked','on')
  174. trial_length_menu = uimenu(session_menu,'Label','Trial Length');
  175. h_trial_times = zeros(1,n_trial_time_opts);
  176. for i = 1:n_trial_time_opts
  177. h_trial_times(i) = uimenu(trial_length_menu,'Label',sprintf('%1.1f seconds',trial_time_opts(i)),'Callback',{@change_trial_time,i});
  178. end; set(h_trial_times(trial_time_spec),'Checked','on')
  179. advanced_menu = uimenu(session_menu,'Label','Advanced');
  180. num_hits_menu = uimenu(advanced_menu,'Label','# Matches');
  181. sound_matches_menu = uimenu(num_hits_menu,'Label','# Sound Matches');
  182. n_poss = round(.6*nTrials);
  183. h_n_sound_matches = zeros(1,n_poss);
  184. for i = 1:n_poss
  185. h_n_sound_matches(i) = uimenu(sound_matches_menu,'Label',sprintf('%02g',i),'Callback',{@change_sound_matches,i});
  186. end
  187. set(h_n_sound_matches(n_soundHits),'Checked','on')
  188. position_matches_menu = uimenu(num_hits_menu,'Label','# Position Matches');
  189. h_n_position_matches = zeros(1,n_poss);
  190. for i = 1:n_poss
  191. h_n_position_matches(i) = uimenu(position_matches_menu,'Label',sprintf('%02g',i),'Callback',{@change_position_matches,i});
  192. end
  193. set(h_n_position_matches(n_positionHits),'Checked','on')
  194. color_matches_menu = uimenu(num_hits_menu,'Label','# Color Matches');
  195. h_n_color_matches = zeros(1,n_poss);
  196. for i = 1:n_poss
  197. h_n_color_matches(i) = uimenu(color_matches_menu,'Label',sprintf('%02g',i),'Callback',{@change_color_matches,i});
  198. end
  199. set(h_n_color_matches(n_colorHits),'Checked','on')
  200. if completely_random
  201. uimenu(num_hits_menu,'Label',...
  202. 'Completely Random','Checked','on','Callback',@change_completely_random);
  203. else
  204. uimenu(num_hits_menu,'Label',...
  205. 'Completely Random','Checked','off','Callback',@change_completely_random);
  206. end
  207. advance_fallback_menu = uimenu(advanced_menu,'Label','Advance/Fallback');
  208. advance_menu = uimenu(advance_fallback_menu,'Label','Advance Threshold');
  209. advance_thresholds = 1:-0.02:0.80;
  210. h_advances = zeros(1,length(advance_thresholds));
  211. for i = 1:length(advance_thresholds)
  212. h_advances(i) = uimenu(advance_menu,'Label',sprintf('%g%',advance_thresholds(i)),'Callback',{@change_advance_thresh,i});
  213. end
  214. [~,which_advance] = min(abs(advance_thresholds-advance_thresh));
  215. set(h_advances(which_advance),'Checked','on')
  216. set(h_advances(6),'Label',[sprintf('%g%',advance_thresholds(6)),' (default)']);
  217. fallback_menu = uimenu(advance_fallback_menu,'Label','Fallback Threshold');
  218. fallback_thresholds = .80:-.05:.50;
  219. h_fallbacks = zeros(1,length(fallback_thresholds));
  220. for i = 1:length(fallback_thresholds)
  221. h_fallbacks(i) = uimenu(fallback_menu,'Label',sprintf('%g%',fallback_thresholds(i)),'Callback',{@change_fallback_thresh,i});
  222. end
  223. [~,which_fallback] = min(abs(fallback_thresholds-fallback_thresh));
  224. set(h_fallbacks(which_fallback),'Checked','on')
  225. set(h_fallbacks(2),'Label',[sprintf('%g%',fallback_thresholds(2)),' (default)']);
  226. feedback_menu = uimenu(handles.figure,'Label','Feedback');
  227. if show_remaining
  228. uimenu(feedback_menu,'Label','Show Remaining Trials','Checked','on','Callback',@change_show_remaining);
  229. else uimenu(feedback_menu,'Label','Show Remaining Trials','Checked','off','Callback',@change_show_remaining);
  230. end
  231. if realtime_feedback
  232. uimenu(feedback_menu,'Label','Realtime Feedback','Checked','on','Callback',@change_realtime_feedback);
  233. else uimenu(feedback_menu,'Label','Realtime Feedback','Checked','off','Callback',@change_realtime_feedback);
  234. end
  235. if figure_window_feedback
  236. uimenu(feedback_menu,'Label','Results in GUI','Checked','on','Callback',@change_figure_feedback);
  237. else uimenu(feedback_menu,'Label','Results in GUI','Checked','off','Callback',@change_figure_feedback);
  238. end
  239. if command_line_feedback
  240. uimenu(feedback_menu,'Label','Command Line Results','Checked','on','Callback',@change_cmd_feedback);
  241. else uimenu(feedback_menu,'Label','Command Line Results','Checked','off','Callback',@change_cmd_feedback);
  242. end
  243. sound_settings_menu = uimenu(handles.figure,'Label','Sounds');
  244. uimenu(sound_settings_menu,'Label','Volume','Callback',@change_volume);
  245. sound_type_menu = uimenu(sound_settings_menu,'Label','Type');
  246. h_letters = uimenu(sound_type_menu,'Label','Letters');
  247. h_sound_types(3) = uimenu(h_letters,'Label','Female Voice 1','Callback',{@change_sound_types,3});
  248. h_sound_types(4) = uimenu(h_letters,'Label','Female Voice 2','Callback',{@change_sound_types,4});
  249. h_numbers = uimenu(sound_type_menu,'Label','Numbers');
  250. h_sound_types(1) = uimenu(h_numbers,'Label','Female Voice','Callback',{@change_sound_types,1});
  251. h_sound_types(2) = uimenu(h_numbers,'Label','Male Voice','Callback',{@change_sound_types,2});
  252. set(h_sound_types(sound_type),'Checked','on')
  253. reactions_menu = uimenu(sound_settings_menu,'Label','Reactions');
  254. if enable_applause
  255. uimenu(reactions_menu,'Label','Applause','Checked','on','Callback',@change_applause_setting);
  256. else uimenu(reactions_menu,'Label','Applause','Checked','off','Callback',@change_applause_setting);
  257. end
  258. if enable_boos
  259. uimenu(reactions_menu,'Label','Boos','Checked','on','Callback',@change_boos_setting);
  260. else uimenu(reactions_menu,'Label','Boos','Checked','off','Callback',@change_boos_setting);
  261. end
  262. appearance_menu = uimenu(handles.figure,'Label','Appearance');
  263. background_menu = uimenu(appearance_menu,'Label','Background');
  264. if background_color_scheme==1
  265. h_background_menu1 = uimenu(background_menu,'Label','Black','Checked','on','Callback',{@change_background,1});
  266. h_background_menu2 = uimenu(background_menu,'Label','White','Callback',{@change_background,2});
  267. txt_color = ones(1,3);
  268. elseif background_color_scheme==2
  269. h_background_menu1 = uimenu(background_menu,'Label','Black','Callback',{@change_background,1});
  270. h_background_menu2 = uimenu(background_menu,'Label','White','Checked','on','Callback',{@change_background,2});
  271. txt_color = zeros(1,3);
  272. else warning('''background_color'' should be either integer 1 (black) or 2 (white)')
  273. h_background_menu1 = uimenu(background_menu,'Label','Black','Checked','on','Callback',{@change_background,1});
  274. h_background_menu2 = uimenu(background_menu,'Label','White','Callback',{@change_background,2});
  275. txt_color = ones(1,3);
  276. end
  277. %% Add other display elements (gridlines, rectangle, etc.):
  278. % Gridlines:
  279. minMeasure = .05; maxMeasure = .95; one_third = .3497; two_thirds = .6503;
  280. h_grid_lines = zeros(1,4);
  281. h_grid_lines(1) = annotation('line','X',[one_third,one_third],'Y',[minMeasure,maxMeasure],'Color',txt_color,'LineWidth',1);
  282. h_grid_lines(2) = annotation('line','X',[two_thirds,two_thirds],'Y',[minMeasure,maxMeasure],'Color',txt_color,'LineWidth',1);
  283. h_grid_lines(3) = annotation('line','X',[minMeasure,maxMeasure],'Y',[two_thirds,two_thirds],'Color',txt_color,'LineWidth',1);
  284. h_grid_lines(4) = annotation('line','X',[minMeasure,maxMeasure],'Y',[one_third,one_third],'Color',txt_color,'LineWidth',1);
  285. % Initialize Match Button Text and Rectangle:
  286. handles.h_rect = rectangle('Parent',handles.axes1,'Position',...
  287. [.05,.05,square_width,square_width],'Curvature',[.2,.2],'FaceColor',...
  288. 'blue','Visible','off');
  289. h_txt_begin = annotation('textbox',[.37,.48,.26,.04],'String',...
  290. ['Press ',startSessionKey,sprintf(' \nto begin')],...
  291. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment',...
  292. 'middle','FontSize',14,'FontWeight','bold','EdgeColor','none');
  293. h_txt_pos = text('position',[.04,.01],'String',['Position Match: ''',positionMatchKey,''''],...
  294. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment','top',...
  295. 'FontSize',13,'FontWeight','normal','Visible','off','EdgeColor','none');
  296. h_txt_color = text('position',[.35,.01],'String',['Color Match: ''',colorMatchKey,''''],...
  297. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment','top',...
  298. 'FontSize',13,'FontWeight','normal','Visible','off','EdgeColor','none');
  299. h_txt_sound = text('position',[.658,.01],'String',['Sound Match: ''',soundMatchKey,''''],...
  300. 'Color',txt_color,'HorizontalAlignment','center','VerticalAlignment','top',...
  301. 'FontSize',13,'FontWeight','normal','Visible','off','EdgeColor','none');
  302. if position_on; set(h_txt_pos,'Visible','on'); end
  303. if color_on; set(h_txt_color,'Visible','on'); end
  304. if sound_on; set(h_txt_sound,'Visible','on'); end
  305. % Declare Figure Callbacks:
  306. set(handles.figure,'WindowKeyPressFcn',@keypress_callback);
  307. set(handles.figure,'CloseRequestFcn',@close_nback_matlab)
  308. % Resize Figure function:
  309. canResize = true;
  310. resizeScreen;
  311. %% Run Function:
  312. function run_session
  313. disableMenus; % disallow users from toggling settings during play
  314. if ishandle(h_pos_feedback1)
  315. delete(h_pos_feedback1); delete(h_pos_feedback2)
  316. end
  317. if ishandle(h_sound_feedback1)
  318. delete(h_sound_feedback1); delete(h_sound_feedback2)
  319. end
  320. if ishandle(h_color_feedback1)
  321. delete(h_color_feedback1); delete(h_color_feedback2)
  322. end
  323. % START TRIALS:
  324. ix = 0;
  325. while ix<nTrials && ~stop_running
  326. ix = ix + 1;
  327. if realtime_feedback
  328. set(h_txt_pos,'Color',txt_color)
  329. set(h_txt_sound,'Color',txt_color)
  330. set(h_txt_color,'Color',txt_color)
  331. end
  332. if show_remaining
  333. set(h_title,'String',num2str(nTrials-ix))
  334. end
  335. tic
  336. if position_on
  337. set(handles.h_rect,'Position',positions(position_mem(ix),:),'Visible','on')
  338. end
  339. if color_on
  340. set(handles.h_rect,'FaceColor',colors{color_mem(ix)})
  341. end
  342. if sound_on
  343. sound(audio_dat(sound_type).sound{sound_mem(ix)}*volume_factor,audio_dat(sound_type).Fs{sound_mem(ix)})
  344. end
  345. t = toc;
  346. pause(trial_time-t)
  347. end
  348. curr_running = 0; set(handles.h_rect,'Visible','off')
  349. set(h_txt_begin,'Visible','on'); set(h_title,'String','')
  350. set(h_txt_pos,'Color',txt_color); set(h_txt_sound,'Color',txt_color)
  351. set(h_txt_color,'Color',txt_color)
  352. % Calculate Score:
  353. if ~stop_running
  354. % Position Stats:
  355. position_hits = sum((position_match_vec+user_position_match)==2);
  356. position_false = sum((position_match_vec-user_position_match)==-1);
  357. H_pos = position_hits / n_positionHits; F_pos = position_false / (nTrials - n_positionHits);
  358. 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);
  359. position_stats = cell(4,2);
  360. position_stats(1:4,1) = {'Position','A'':','Hits:','False-Alarms:'};
  361. position_stats{2,2} = num2str(round(pos_score,2));
  362. position_stats{3,2} = [num2str(round(100*H_pos)),'%'];
  363. position_stats{4,2} = [num2str(round(100*F_pos)),'%'];
  364. % Sound Stats:
  365. sound_hits = sum((sound_match_vec+user_sound_match)==2);
  366. sound_false = sum((sound_match_vec-user_sound_match)==-1);
  367. H_sound = sound_hits / n_soundHits; F_sound = sound_false / (nTrials - n_soundHits);
  368. 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);
  369. sound_stats = cell(4,2);
  370. sound_stats(1:4,1) = {'Sound','A'':','Hits:','False-Alarms:'};
  371. sound_stats{2,2} = num2str(round(sound_score,2));
  372. sound_stats{3,2} = [num2str(round(100*H_sound)),'%'];
  373. sound_stats{4,2} = [num2str(round(100*F_sound)),'%'];
  374. % Color Stats:
  375. color_hits = sum((color_match_vec+user_color_match)==2);
  376. color_false = sum((color_match_vec-user_color_match)==-1);
  377. H_color = color_hits / n_colorHits; F_color = color_false / (nTrials - n_colorHits);
  378. 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);
  379. color_stats = cell(4,2);
  380. color_stats(1:4,1) = {'Color','A'':','Hits:','False-Alarms:'};
  381. color_stats{2,2} = num2str(round(color_score,2));
  382. color_stats{3,2} = [num2str(round(100*H_color,1)),'%'];
  383. color_stats{4,2} = [num2str(round(100*F_color,1)),'%'];
  384. % Command-line Feedback:
  385. if command_line_feedback
  386. if position_on && sound_on && color_on
  387. session_stats = [position_stats,sound_stats,color_stats];
  388. elseif position_on && sound_on && ~color_on
  389. session_stats = [position_stats,sound_stats];
  390. elseif position_on && ~sound_on && ~color_on
  391. session_stats = position_stats;
  392. elseif ~position_on && sound_on && ~color_on
  393. session_stats = sound_stats;
  394. elseif ~position_on && sound_on && color_on
  395. session_stats = [sound_stats,color_stat];
  396. elseif position_on && ~sound_on && color_on
  397. session_stats = [position_stats,color_stats];
  398. elseif ~position_on && ~sound_on && color_on
  399. session_stats = color_stats;
  400. else return;
  401. end
  402. disp(session_stats)
  403. end
  404. % Write data to 'nback_matlab_progress.txt':
  405. write_progress(pos_score,sound_score,color_score)
  406. % Check if Advance:
  407. advance_vec = [(pos_score >= advance_thresh),(sound_score >= advance_thresh),(color_score >= advance_thresh)];
  408. fallback_vec = [(pos_score <= fallback_thresh),(sound_score <= fallback_thresh),(color_score <= fallback_thresh)];
  409. if all(advance_vec(logical([position_on,sound_on,color_on])))
  410. if command_line_feedback
  411. disp('Congratulations, you have been advanced to the next nBack level!')
  412. end
  413. nback = nback+1;
  414. if get(h_trial_num(1),'checked')
  415. nTrials = nTrials + 1;
  416. end
  417. set(handles.figure,'name',['nback_matlab: (',num2str(nback),'-Back)'])
  418. set(h_nBack(nback-1),'Checked','off'); set(h_nBack(nback),'Checked','on');
  419. if enable_applause; sound(applause_dat,Fs_applause); end
  420. if figure_window_feedback
  421. figure_feedback_color = 'g';
  422. end
  423. elseif any(fallback_vec(logical([position_on,sound_on,color_on])))
  424. if nback>1;
  425. nback = nback-1;
  426. if get(h_trial_num(1),'checked')
  427. nTrials = nTrials - 1;
  428. end
  429. if command_line_feedback
  430. disp('Good effort - nBack level lowered.')
  431. end
  432. set(handles.figure,'name',['nback_matlab: (',num2str(nback),'-Back)'])
  433. set(h_nBack(nback+1),'Checked','off'); set(h_nBack(nback),'Checked','on');
  434. else if command_line_feedback; disp('Good effort.'); end
  435. end
  436. if enable_boos; sound(boo_dat,Fs_boo); end
  437. if figure_window_feedback
  438. figure_feedback_color = 'r';
  439. end
  440. else
  441. if command_line_feedback; disp('Good score - keep training!'); end
  442. if figure_window_feedback
  443. if background_color_scheme==1
  444. figure_feedback_color = 'w';
  445. elseif background_color_scheme==2
  446. figure_feedback_color = 'k';
  447. end
  448. end
  449. end
  450. if figure_window_feedback
  451. if position_on
  452. main_char = char('Position','A'':','Hits:','False-Alarms:');
  453. char2 = char('',position_stats{2,2},position_stats{3,2},position_stats{4,2});
  454. h_pos_feedback1 = text('String',main_char,'FontName','Helvetica','FontSize',13,...
  455. 'Position',h_pos_feedback1_pos,'Color',figure_feedback_color,...
  456. 'FontWeight','bold','EdgeColor','none','HorizontalAlignment','left');
  457. h_pos_feedback2 = text('String',char2,'FontName','Helvetica','FontSize',13,...
  458. 'Position',h_pos_feedback2_pos,'Color',figure_feedback_color,'HorizontalAlignment','right',...
  459. 'FontWeight','normal','EdgeColor','none');
  460. end
  461. if sound_on
  462. main_char = char('Sound','A'':','Hits:','False-Alarms:');
  463. char2 = char('',sound_stats{2,2},sound_stats{3,2},sound_stats{4,2});
  464. h_sound_feedback1 = text('String',main_char,'FontName','Helvetica','FontSize',13,...
  465. 'Position',h_sound_feedback1_pos,'Color',figure_feedback_color,...
  466. 'FontWeight','bold','EdgeColor','none');
  467. h_sound_feedback2 = text('String',char2,'FontName','Helvetica','FontSize',13,...
  468. 'Position',h_sound_feedback2_pos,'Color',figure_feedback_color,'HorizontalAlignment','right',...
  469. 'FontWeight','normal','EdgeColor','none');
  470. end
  471. if color_on
  472. main_char = char('Color','A'':','Hits:','False-Alarms:');
  473. char2 = char('',color_stats{2,2},color_stats{3,2},color_stats{4,2});
  474. h_color_feedback1 = text('String',main_char,'FontName','Helvetica','FontSize',13,...
  475. 'Position',h_color_feedback1_pos,'Color',figure_feedback_color,...
  476. 'FontWeight','bold','EdgeColor','none');
  477. h_color_feedback2 = text('String',char2,'FontName','Helvetica','FontSize',13,...
  478. 'Position',h_color_feedback2_pos,'Color',figure_feedback_color,'HorizontalAlignment','right',...
  479. 'FontWeight','normal','EdgeColor','none');
  480. end
  481. end
  482. else stop_running = 0;
  483. end
  484. enableMenus;
  485. end
  486. % Key Press Callback:
  487. function keypress_callback(~,which_key,~)
  488. switch which_key.Key
  489. case startSessionKey
  490. if ~curr_running
  491. curr_running = 1; set(h_txt_begin,'Visible','off')
  492. if completely_random
  493. position_mem = randi(9,[1,nTrials]);
  494. color_mem = randi(7,[1,nTrials]);
  495. sound_mem = randi(audio_dat(sound_type).nSound,[1,nTrials]);
  496. else
  497. position_mem_match = zeros(1,nTrials-nback);
  498. position_mem_match(1:n_positionHits) = 1;
  499. color_mem_match = zeros(1,nTrials-nback);
  500. color_mem_match(1:n_colorHits) = 1;
  501. sound_mem_match = zeros(1,nTrials-nback);
  502. sound_mem_match(1:n_soundHits) = 1;
  503. % Shuffle Match Vectors:
  504. position_mem_match = position_mem_match(randperm(length(position_mem_match)));
  505. position_mem_match = [zeros(1,nback),position_mem_match]; % pad
  506. color_mem_match = color_mem_match(randperm(length(color_mem_match)));
  507. color_mem_match = [zeros(1,nback),color_mem_match]; % pad
  508. sound_mem_match = sound_mem_match(randperm(length(sound_mem_match)));
  509. sound_mem_match = [zeros(1,nback),sound_mem_match]; % pad
  510. % Generate new mem's:
  511. position_mem = zeros(1,nTrials);
  512. sound_mem = zeros(1,nTrials);
  513. color_mem = zeros(1,nTrials);
  514. for ixx = 1:nTrials
  515. % Position:
  516. if position_mem_match(ixx) && ixx>nback
  517. position_mem(ixx) = position_mem(ixx-nback);
  518. elseif ixx<=nback
  519. position_mem(ixx) = randi(9,1);
  520. else
  521. position_bank = setdiff(1:9,position_mem(ixx-nback));
  522. position_mem(ixx) = position_bank(randperm(8,1));
  523. end
  524. % Color:
  525. if color_mem_match(ixx) && ixx>nback
  526. color_mem(ixx) = color_mem(ixx-nback);
  527. elseif ixx<=nback
  528. color_mem(ixx) = randi(7,1);
  529. else
  530. color_bank = setdiff(1:7,color_mem(ixx-nback));
  531. color_mem(ixx) = color_bank(randperm(6,1));
  532. end
  533. % Sound:
  534. if sound_mem_match(ixx) && ixx>nback
  535. sound_mem(ixx) = sound_mem(ixx-nback);
  536. elseif ixx<=nback
  537. sound_mem = randi(audio_dat(sound_type).nSound,[1,nTrials]);
  538. sound_mem(ixx) = randi(audio_dat(sound_type).nSound,1);
  539. else
  540. sound_bank = setdiff(1:audio_dat(sound_type).nSound,sound_mem(ixx-nback));
  541. sound_mem(ixx) = sound_bank(randperm(audio_dat(sound_type).nSound-1,1));
  542. end
  543. end
  544. end
  545. position_match_vec = [position_mem,nan(1,nback)]==[zeros(1,nback),position_mem];
  546. color_match_vec = [color_mem,nan(1,nback)]==[zeros(1,nback),color_mem];
  547. sound_match_vec = [sound_mem,nan(1,nback)]==[zeros(1,nback),sound_mem];
  548. user_position_match = nan(1,nTrials+nback);
  549. user_color_match = nan(1,nTrials+nback);
  550. user_sound_match = nan(1,nTrials+nback);
  551. pause(.5)
  552. run_session
  553. end
  554. case positionMatchKey
  555. if curr_running
  556. user_position_match(ix) = 1;
  557. if realtime_feedback
  558. if position_match_vec(ix)
  559. set(h_txt_pos,'Color',[0,1,0])
  560. else
  561. set(h_txt_pos,'Color',[1,0,0])
  562. end
  563. end
  564. end
  565. case colorMatchKey
  566. if curr_running
  567. user_color_match(ix) = 1;
  568. if realtime_feedback
  569. if color_match_vec(ix)
  570. set(h_txt_color,'Color',[0,1,0])
  571. else
  572. set(h_txt_color,'Color',[1,0,0])
  573. end
  574. end
  575. end
  576. case soundMatchKey
  577. if curr_running
  578. user_sound_match(ix) = 1;
  579. if realtime_feedback
  580. if sound_match_vec(ix)
  581. set(h_txt_sound,'Color',[0,1,0])
  582. else
  583. set(h_txt_sound,'Color',[1,0,0])
  584. end
  585. end
  586. end
  587. case stopRunningKey
  588. stop_running = 1;
  589. end
  590. end
  591. % Change nBack Types:
  592. function change_types(~,~,which_type)
  593. switch which_type
  594. case 1,
  595. switch position_on
  596. case 1, set(types_menu(1),'Checked','off'); position_on = false;
  597. set(h_txt_pos,'Visible','off')
  598. case 0, set(types_menu(1),'Checked','on'); position_on = true;
  599. set(h_txt_pos,'Visible','on')
  600. end
  601. case 2,
  602. switch sound_on
  603. case 1, set(types_menu(2),'Checked','off'); sound_on = false;
  604. set(h_txt_sound,'Visible','off')
  605. case 0, set(types_menu(2),'Checked','on'); sound_on = true;
  606. set(h_txt_sound,'Visible','on')
  607. end
  608. case 3,
  609. switch color_on
  610. case 1, set(types_menu(3),'Checked','off'); color_on = false;
  611. set(h_txt_color,'Visible','off')
  612. set(handles.h_rect,'FaceColor','b')
  613. case 0, set(types_menu(3),'Checked','on'); color_on = true;
  614. set(h_txt_color,'Visible','on')
  615. end
  616. end
  617. end
  618. % Change nBack Level:
  619. function change_nBack(~,~,nBack_spec)
  620. nback = nBack_spec;
  621. for ix1 = 1:20
  622. set(h_nBack(ix1),'Checked','off');
  623. end
  624. set(h_nBack(nback),'Checked','on')
  625. set(handles.figure,'name',['nback_matlab: (',num2str(nback),'-Back)'])
  626. end
  627. % Change N Position Matches:
  628. function change_position_matches(~,~,change_n)
  629. n_positionHits = change_n;
  630. for ixx = 1:n_poss
  631. set(h_n_position_matches(ixx),'Checked','off');
  632. end
  633. set(h_n_position_matches(n_positionHits),'Checked','on')
  634. end
  635. % Change N Sound Matches:
  636. function change_sound_matches(~,~,change_n)
  637. n_soundHits = change_n;
  638. for ixx = 1:n_poss
  639. set(h_n_sound_matches(ixx),'Checked','off');
  640. end
  641. set(h_n_sound_matches(n_soundHits),'Checked','on')
  642. end
  643. % Change N Color Matches:
  644. function change_color_matches(~,~,change_n)
  645. n_colorHits = change_n;
  646. for ixx = 1:n_poss
  647. set(h_n_color_matches(ixx),'Checked','off');
  648. end
  649. set(h_n_color_matches(n_colorHits),'Checked','on')
  650. end
  651. % Change Completely Random:
  652. function change_completely_random(hObject,~,~)
  653. switch completely_random
  654. case 1, set(hObject,'Checked','off'); completely_random = 0;
  655. case 0, set(hObject,'Checked','on'); completely_random = 1;
  656. end
  657. end
  658. % Change Advance Threshold:
  659. function change_advance_thresh(~,~,advance_spec)
  660. advance_thresh = advance_thresholds(advance_spec);
  661. for ixx = 1:length(advance_thresholds)
  662. set(h_advances(ixx),'Checked','off')
  663. end
  664. set(h_advances(advance_spec),'Checked','on')
  665. end
  666. % Change Advance Threshold:
  667. function change_fallback_thresh(~,~,fallback_spec)
  668. fallback_thresh = fallback_thresholds(fallback_spec);
  669. for ixx = 1:length(fallback_thresholds)
  670. set(h_fallbacks(ixx),'Checked','off')
  671. end
  672. set(h_fallbacks(fallback_spec),'Checked','on')
  673. end
  674. % Change # Trials:
  675. function change_trial_num(~,~,trial_num_spec)
  676. if (trial_num_spec > 1)
  677. nTrials = trial_num_opts(trial_num_spec-1);
  678. else
  679. nTrials = nback + 20;
  680. end
  681. for ixx = 1:n_trial_num_opts+1
  682. set(h_trial_num(ixx),'Checked','off')
  683. end
  684. set(h_trial_num(trial_num_spec),'Checked','on')
  685. end
  686. % Change Trial Time:
  687. function change_trial_time(~,~,trial_time_spec)
  688. trial_time = trial_time_opts(trial_time_spec);
  689. for ixx = 1:n_trial_time_opts
  690. set(h_trial_times(ixx),'Checked','off')
  691. end
  692. set(h_trial_times(trial_time_spec),'Checked','on')
  693. end
  694. % Change Show Remaining Trials:
  695. function change_show_remaining(hObject,~,~)
  696. switch show_remaining
  697. case 1, set(hObject,'Checked','off'); show_remaining = 0;
  698. case 0, set(hObject,'Checked','on'); show_remaining = 1;
  699. end
  700. end
  701. function change_realtime_feedback(hObject,~,~)
  702. switch realtime_feedback
  703. case 1, set(hObject,'Checked','off'); realtime_feedback = 0;
  704. case 0, set(hObject,'Checked','on'); realtime_feedback = 1;
  705. end
  706. end
  707. function change_figure_feedback(hObject,~,~)
  708. switch figure_window_feedback
  709. case 1, set(hObject,'Checked','off'); figure_window_feedback = 0;
  710. case 0, set(hObject,'Checked','on'); figure_window_feedback = 1;
  711. end
  712. end
  713. function change_cmd_feedback(hObject,~,~)
  714. switch command_line_feedback
  715. case 1, set(hObject,'Checked','off'); command_line_feedback = 0;
  716. case 0, set(hObject,'Checked','on'); command_line_feedback = 1;
  717. end
  718. end
  719. % Change Sound Settings:
  720. function change_volume(~,~,~)
  721. if ~ishandle(h_volume)
  722. h_volume = figure('menubar','none','color',background_color,'numbertitle',...
  723. 'off','name','Adjust Volume','units','norm','Position',[.4729,.8646,.2174,.0352]);
  724. uicontrol(h_volume,'Style','slider','units','normalized','Position',...
  725. [0,.2,1,.6],'Min',0,'Max',1.2,'Value',volume_factor,'Callback',@change_volume_slider);
  726. else figure(h_volume)
  727. end
  728. end
  729. function change_volume_slider(hObject,~,~)
  730. volume_factor = hObject.Value;
  731. end
  732. function change_sound_types(~,~,which_type)
  733. for ixx = 1:4; set(h_sound_types(ixx),'Checked','off'); end
  734. set(h_sound_types(which_type),'Checked','on')
  735. sound_type = which_type;
  736. end
  737. function change_applause_setting(hObject,~,~)
  738. switch enable_applause
  739. case 1, set(hObject,'Checked','off'); enable_applause = 0;
  740. case 0, set(hObject,'Checked','on'); enable_applause = 1;
  741. end
  742. end
  743. function change_boos_setting(hObject,~,~)
  744. switch enable_boos
  745. case 1, set(hObject,'Checked','off'); enable_boos = 0;
  746. case 0, set(hObject,'Checked','on'); enable_boos = 1;
  747. end
  748. end
  749. % Appearance:
  750. function change_background(~,~,which_background)
  751. switch which_background
  752. case 1,
  753. if strcmp(h_background_menu1.Checked,'on')
  754. background_color_scheme = 2;
  755. colors = {'y','m','c','r','g','b','k'};
  756. set(h_background_menu1,'Checked','off')
  757. set(h_background_menu2,'Checked','on')
  758. elseif strcmp(h_background_menu1.Checked,'off')
  759. background_color_scheme = 1;
  760. colors = {'y','m','c','r','g','b','w'};
  761. set(h_background_menu1,'Checked','on')
  762. set(h_background_menu2,'Checked','off')
  763. end
  764. case 2,
  765. if strcmp(h_background_menu2.Checked,'on')
  766. background_color_scheme = 1;
  767. colors = {'y','m','c','r','g','b','w'};
  768. set(h_background_menu1,'Checked','on')
  769. set(h_background_menu2,'Checked','off')
  770. elseif strcmp(h_background_menu2.Checked,'off')
  771. background_color_scheme = 2;
  772. colors = {'y','m','c','r','g','b','k'};
  773. set(h_background_menu1,'Checked','off')
  774. set(h_background_menu2,'Checked','on')
  775. end
  776. end
  777. if background_color_scheme==1
  778. txt_color = ones(1,3);
  779. set(handles.figure,'Color',zeros(1,3))
  780. elseif background_color_scheme==2
  781. txt_color = zeros(1,3);
  782. set(handles.figure,'Color',ones(1,3))
  783. end
  784. for ixxx = 1:4
  785. set(h_grid_lines(ixxx),'Color',txt_color)
  786. end
  787. set(h_title,'Color',txt_color); set(h_txt_begin,'Color',txt_color)
  788. set(h_txt_pos,'Color',txt_color); set(h_txt_color,'Color',txt_color)
  789. set(h_txt_sound,'Color',txt_color)
  790. end
  791. % Write Current Trial Data:
  792. function write_progress(pos_score,sound_score,color_score)
  793. if ~isdir(fullfile(script_path,'UserData')); mkdir(fullfile(script_path,'UserData')); end
  794. listing = dir(fullfile(script_path,'UserData','nback_matlab_progress.txt'));
  795. if isempty(listing);
  796. tbl_cell = cell(2,7);
  797. tbl_cell(1,:) = {'Date-Time','Position_score','Sound_score','Color_score','nBack','nTrials','Trial_Length'};
  798. tbl_cell(2,:) = {char(datetime),num2str(pos_score),num2str(sound_score),num2str(color_score),num2str(nback),num2str(nTrials),num2str(trial_time)};
  799. % Write .txt File:
  800. fileID = fopen(fullfile(script_path,'UserData','nback_matlab_progress.txt'),'w');
  801. for ix1 = 1:2
  802. for ix2 = 1:7
  803. fprintf(fileID,'%s,',char(tbl_cell{ix1,ix2}));
  804. end
  805. fprintf(fileID,'\r\n');
  806. end
  807. fclose(fileID);
  808. else
  809. tbl_cell = cell(1,7);
  810. tbl_cell(1,:) = {char(datetime),num2str(pos_score),num2str(sound_score),num2str(color_score),num2str(nback),num2str(nTrials),num2str(trial_time)};
  811. fileID = fopen(fullfile(script_path,'UserData','nback_matlab_progress.txt'),'a');
  812. fprintf(fileID,'\r\n');
  813. for ix2 = 1:7
  814. fprintf(fileID,'%s,',char(tbl_cell{1,ix2}));
  815. end
  816. fclose(fileID);
  817. end
  818. end
  819. % Get Default Settings:
  820. function get_defaults
  821. nback = 2; % nBack level
  822. nTrials = nback + 20;
  823. trial_time = 2.4; % seconds
  824. sound_on = true; % sound-type nBack: 1 (on), 0 (off)
  825. position_on = true; % position-type nBack: 1 (on), 0 (off)
  826. color_on = false; % color-type nBack: 1 (on), 0 (off)
  827. completely_random = 0; % off (0), on (1) (if on, next 3 settings are irrelevant)
  828. n_positionHits = 4; % control # of matches for position
  829. n_soundHits = 4; % control # of matches for sound
  830. n_colorHits = 4; % control # of matches for color
  831. advance_thresh = .9; % A' default
  832. fallback_thresh = .75; % A' default
  833. volume_factor = 1; % Default: 1 (no change); >1 = increase volume; <1 = decrease volume
  834. sound_type = 3; % use Numbers-Female (1), Numbers-Male (2), Letters-Female1 (3), or Letters-Female2 (4)
  835. enable_applause = 1; % 1 (on) or 0 (off)
  836. enable_boos = 0; % 1 (on) or 0 (off); comical boos if fail to advance to next nBack
  837. realtime_feedback = 1; % 1 (on) or 0 (off); highlight labels red/green based on accuracy
  838. figure_window_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in figure window upon completion
  839. command_line_feedback = 1; % 1 (on) or 0 (off); display hits, misses, false alarms in command window upon completion
  840. show_remaining = 1; % 1 (on) or 0 (off); show remaining trials as title
  841. background_color_scheme = 1; % black (1), white (2)
  842. end
  843. % Get Custom Settings:
  844. function success = get_settings
  845. if ~isdir(fullfile(script_path,'UserData')); mkdir(fullfile(script_path,'UserData')); end
  846. listing = dir(fullfile(script_path,'UserData','nback_matlab_settings.txt'));
  847. if isempty(listing);
  848. success = 0;
  849. get_defaults
  850. return;
  851. end
  852. nback_matlab_settings = importdata(fullfile(script_path,'UserData',listing(1).name));
  853. for ix1 = 1:length(customizable); eval(nback_matlab_settings{ix1}); end
  854. success = 1;
  855. end
  856. % Write Custom Settings:
  857. function write_settings
  858. % Change Settings Data:
  859. nback_matlab_settings = cell(length(customizable),1);
  860. for ix1 = 1:length(customizable)
  861. setting1 = eval(customizable{ix1});
  862. nback_matlab_settings{ix1} = [customizable{ix1},'=',num2str(setting1),';'];
  863. end
  864. nback_matlab_settings = char(nback_matlab_settings);
  865. % Write Text File:
  866. if ~isdir(fullfile(script_path,'UserData')); mkdir(fullfile(script_path,'UserData')); end
  867. fileID = fopen(fullfile(script_path,'UserData','nback_matlab_settings.txt'),'w');
  868. for ix1 = 1:length(customizable)
  869. fprintf(fileID,'%s\r\n',nback_matlab_settings(ix1,:));
  870. end
  871. fclose(fileID);
  872. end
  873. % Close Requests:
  874. function close_nback_matlab(varargin)
  875. % Save Custom Settings:
  876. write_settings
  877. % Close:
  878. delete(handles.figure)
  879. end
  880. function enableMenus
  881. set(handles.figure,'resize','on')
  882. set(file_menu,'Enable','on'); set(session_menu,'Enable','on')
  883. set(nBack_type_menu,'Enable','on'); set(feedback_menu,'Enable','on')
  884. set(sound_settings_menu,'Enable','on'); set(appearance_menu,'Enable','on')
  885. end
  886. function disableMenus
  887. clear sound % stop any sound if playing
  888. set(handles.figure,'resize','off')
  889. set(file_menu,'Enable','off')
  890. set(session_menu,'Enable','off')
  891. set(nBack_type_menu,'Enable','off')
  892. set(feedback_menu,'Enable','off')
  893. set(sound_settings_menu,'Enable','off')
  894. set(appearance_menu,'Enable','off')
  895. end
  896. function resizeScreen(varargin)
  897. if canResize
  898. % Determine which dimension needs shrinking to become square:
  899. for ixx = 1:4; set(h_grid_lines(ixx),'units','norm'); end
  900. set(h_grid_lines(1),'Y',[minMeasure,maxMeasure])
  901. set(h_grid_lines(2),'Y',[minMeasure,maxMeasure])
  902. set(h_grid_lines(3),'X',[minMeasure,maxMeasure])
  903. set(h_grid_lines(4),'X',[minMeasure,maxMeasure])
  904. % Change units to points and shrink width or height to match:
  905. for ixx = 1:4; set(h_grid_lines(ixx),'units','points'); end
  906. ySpec = get(h_grid_lines(1),'Y'); xSpec = get(h_grid_lines(3),'X');
  907. height = diff(ySpec); width = diff(xSpec);
  908. axesRatio = height/width;
  909. diffPixels = abs(height - width);
  910. if height > width % shrink height to match
  911. set(h_grid_lines(1),'Y',[ySpec(1) + diffPixels/2, ySpec(2) - diffPixels/2])
  912. set(h_grid_lines(2),'Y',[ySpec(1) + diffPixels/2, ySpec(2) - diffPixels/2])
  913. elseif height < width % shrink width to match
  914. set(h_grid_lines(3),'X',[xSpec(1) + diffPixels/2, xSpec(2) - diffPixels/2])
  915. set(h_grid_lines(4),'X',[xSpec(1) + diffPixels/2, xSpec(2) - diffPixels/2])
  916. end
  917. % Change units back to normalized, and adjust grid spacing:
  918. for ixx = 1:4; set(h_grid_lines(ixx),'units','norm'); end
  919. xSpec = get(h_grid_lines(3),'X'); ySpec = get(h_grid_lines(1),'Y');
  920. oneThirdX = (diff(xSpec)/3) + xSpec(1);
  921. twoThirdsX = xSpec(2) - (diff(xSpec)/3);
  922. oneThirdY = (diff(ySpec)/3) + ySpec(1);
  923. twoThirdsY = ySpec(2) - (diff(ySpec)/3);
  924. set(h_grid_lines(1),'X',[oneThirdX, oneThirdX])
  925. set(h_grid_lines(2),'X',[twoThirdsX, twoThirdsX])
  926. set(h_grid_lines(3),'Y',[oneThirdY, oneThirdY])
  927. set(h_grid_lines(4),'Y',[twoThirdsY, twoThirdsY])
  928. % Square Positions:
  929. ylim(handles.axes1,[0,axesRatio]); % adjust Y-axes limits based on its ratio w/ X
  930. square_width = .95*(twoThirdsX-oneThirdX);
  931. useGapX = ((twoThirdsX-oneThirdX)-square_width)/2;
  932. oneThirdY = oneThirdY*axesRatio; twoThirdsY = twoThirdsY*axesRatio;
  933. useGapY = ((twoThirdsY-oneThirdY)-square_width)/2;
  934. positions = repmat([.05,.05,square_width,square_width],9,1);
  935. positions(1,1:2) = [xSpec(1)+useGapX,twoThirdsY+useGapY];
  936. positions(2,1:2) = [oneThirdX+useGapX,twoThirdsY+useGapY];
  937. positions(3,1:2) = [twoThirdsX+useGapX,twoThirdsY+useGapY];
  938. positions(4,1:2) = [xSpec(1)+useGapX,oneThirdY+useGapY];
  939. positions(5,1:2) = [oneThirdX+useGapX,oneThirdY+useGapY];
  940. positions(6,1:2) = [twoThirdsX+useGapX,oneThirdY+useGapY];
  941. positions(7,1:2) = [xSpec(1)+useGapX,ySpec(1)*axesRatio+useGapY];
  942. positions(8,1:2) = [oneThirdX+useGapX,ySpec(1)*axesRatio+useGapY];
  943. positions(9,1:2) = [twoThirdsX+useGapX,ySpec(1)*axesRatio+useGapY];
  944. % Adjust match button text positions:
  945. set(h_txt_pos,'position',[median([xSpec(1),oneThirdX]),ySpec(1)*axesRatio])
  946. set(h_txt_color,'position',[median([oneThirdX, twoThirdsX]),ySpec(1)*axesRatio])
  947. set(h_txt_sound,'position',[median([twoThirdsX,xSpec(2)]),ySpec(1)*axesRatio])
  948. % Adjust Feedback Positions:
  949. h_pos_feedback1_pos = [xSpec(1)+.005*xSpec(1), median([ySpec(1)*axesRatio, oneThirdY])];
  950. h_pos_feedback2_pos = [oneThirdX-.005*oneThirdX, median([ySpec(1)*axesRatio, oneThirdY])];
  951. h_color_feedback1_pos = [oneThirdX+.005*oneThirdX, median([ySpec(1)*axesRatio, oneThirdY])];
  952. h_color_feedback2_pos = [twoThirdsX-.005*twoThirdsX, median([ySpec(1)*axesRatio, oneThirdY])];
  953. h_sound_feedback1_pos = [twoThirdsX+.005*twoThirdsX, median([ySpec(1)*axesRatio, oneThirdY])];
  954. h_sound_feedback2_pos = [xSpec(2)-.005*xSpec(2), median([ySpec(1)*axesRatio, oneThirdY])];
  955. if ishandle(h_pos_feedback1); set(h_pos_feedback1,'position',h_pos_feedback1_pos); end
  956. if ishandle(h_pos_feedback2); set(h_pos_feedback2,'position',h_pos_feedback2_pos); end
  957. if ishandle(h_color_feedback1); set(h_color_feedback1,'position',h_color_feedback1_pos); end
  958. if ishandle(h_color_feedback2); set(h_color_feedback2,'position',h_color_feedback2_pos); end
  959. if ishandle(h_sound_feedback1); set(h_sound_feedback1,'position',h_sound_feedback1_pos); end
  960. if ishandle(h_sound_feedback2); set(h_sound_feedback2,'position',h_sound_feedback2_pos); end
  961. end
  962. end
  963. end