'Path to the file with default settings: var defaultSettings$ := ""; '"C:/Users/User/Desktop/PPI/Saved_settings/Default_settings.txt"; 'Settings file can be generated in the "Settings" dialog, "Save settings" button var currentSettingsFile$ := ""; 'Set variables for Settings dialog 'These values are just an example untill default settings are set. 'They will be overridden by the values from the file with default settings. var auto% := 0; 'Automatic experiment procedure var noiseDb := 70; 'Sound volume of background noise, db var noiseDurSet% := 10; 'Duration of white noise before experiment starts, min var prePulseDb := 80; 'Sound volume of prepulse, db var prePulseDur% := 50; 'Duration of prepulse, ms var ISI% := 100; 'Inter-stimulus interval between prepulse and pulse, ms var pulseDb := 110; 'Sound volume of pulse, db var pulseDur% := 50; 'Duration of pulse, ms var ITIset := 10; 'Inter-trial interval, s var ITIvar% := 5; 'Random variation of ITI in % of ITI duration var numPrep% := 10; 'Number of prepulses var numPulse% := 10; 'Number of pulses var numPair% := 10; 'Number of prepulse+pulse pairs var blockSeq$ := "RPD"; 'Sequence of block of stimuli of the same type 'Update settings parameters values with default settings, if specified if defaultSettings$ <> "" then LoadDefaultSettings%(); endif; 'If info% = 1 Settings dialog can be opened again only for information var info% := 0; 'Open Settings dialog info% := Settings%(); 'Set technical variables var ITI, ITIvarVal; var noiseDur%, noisePer%; var noiseLoop%, ITIloop%, varLoop%; SetVariables(); 'Delete existing waves in memory PlayWaveDelete(); 'Generate wave channels for noise, prepulse and pulse MakeWaves(); 'Write output sequencer file WriteSequence%(); 'Create new file for data recording var recDataHandle% := FileNew(0); FrontView(recDataHandle%); 'Set experiment control toolbar DoToolbar(); 'Calculate and initialize variables proc SetVariables() ITI := ITIset * 1000; 'Convert seconds of ITI into ms ITIvarVal := ITI * ITIvar% / 100.0; 'Amplitude of ITI variation, ms ITI -= ITIvarVal; 'Give room for ITIvar ITIvarVal *= 2; 'Double amplitude for neg and pos variation nnnnn noiseDur% := noiseDurSet% * 60; 'Convert mins to secs noisePer% := 60; 'Period of noise pattern repetition, s noiseLoop% := noiseDur% / noisePer%; 'Num of noise periods in noiseDur ITIloop% := ITI / (noisePer% * 1000); 'Num of noise periods in ITI varLoop% := ITIvarVal / (noisePer% * 1000); 'Num of noise periods in ITI variation return; end; func DoToolbar() ToolbarClear(); ToolbarSet(1, "Stop", Stop%); ToolbarSet(2, "Start", Start%); ToolBarSet(3, "Settings", Settings%); ToolbarEnable(1, 1); ToolbarEnable(2, 1); ToolbarEnable(3, 1); return Toolbar("Experiment control", 2 + 128); end; 'Create settings dialog box func Settings%() DlgCreate("Settings"); 'Start new dialog DlgCheck(1, "Enable automatic program", 0, 2); 'Limitations on sound volume (50-115 dB) are specific for hardware 'They can be changed according to sound level calibration DlgInteger(2, "Background volume (50-115 dB)", 50, 115, 0, 3); DlgInteger(3, "Noise duration (min)", 0, 600, 0, 4); DlgInteger(4, "Prepulse volume (50-115 dB)", 50, 115, 0, 6); DlgInteger(5, "Prepulse duration (0-100 ms)", 0, 100, 0, 7); DlgInteger(6, "Number of prepulses", 0, 100, 0, 8); DlgInteger(7, "Pulse volume (50-115 dB)", 50, 115, 0, 10); DlgInteger(8, "Pulse duration (0-100 ms)", 0, 100, 0, 11); DlgInteger(9, "Number of pulses", 0, 100, 0, 12); DlgInteger(10, "Prepulse-pulse interval (ISI) (ms)", 0, 1000, 0, 14); DlgInteger(11, "Number of prepulse-pulse pairs", 0, 100, 0, 15); DlgInteger(12, "Inter-trial interval (ITI) (s)", 1, 3600, 0, 17); DlgInteger(13, "ITI variation (0-100%)", 0, 100, 0, 18); DlgString(14, "Sequence of blocks", 20, "RPD", 0, 20); DlgText("R - pRepulse, P - Pulse, D - Double", 0, 21); DlgButton(1, "OK"); DlgButton(2, "Save settings", SaveSettings%); DlgLabel(15, currentSettingsFile$, -2, 1); if info% <> 1 then 'Settings opened for the first time before experiment DlgButton(3, "Load settings", LoadSettings%, 2, 1); DlgButton(0, "Cancel"); else 'Seeings opened after experiment starts DlgButton(0, ""); endif; 'Enable/disable dialogue items for automatic program DlgAllow(0x3ff, 0, Change%); var ok% := DlgShow(auto%, noiseDb, noiseDurSet%, prePulseDb, prePulseDur%, numPrep%, pulseDb, pulseDur%, numPulse%, ISI%, numPair%, ITISet, ITIvar%, blockSeq$, currentSettingsFile$); 'ok% is 0 if user cancels, variables updated if not if ok% = 0 then 'If Cancel - don't enter sampling halt; endif; return 1; end; 'Automatic/manual settings dialogue box func Change%(item%) var v%; docase case ((item% = 1) or (item% = 0)) then '0 is initial setup v% := DlgValue(1); 'Get check box state DlgEnable(v%, 3, 6, 9, 11, 12, 13, 14); 'Enable automatic variables endcase; return 1; end; func LoadDefaultSettings%() FileOpen(defaultSettings$, 8, 0); 'Read settings from the file var n% := Read(auto%, noiseDb, noiseDurSet%, prePulseDb, prePulseDur%, numPrep%, pulseDb, pulseDur%, numPulse%, ISI%, numPair%, ITISet, ITIvar%, blockSeq$); currentSettingsFile$ := FileName$(3) + FileName$(4); FileClose(); return 1; end; func LoadSettings%() FileOpen("", 8, 0); currentSettingsFile$ := FileName$(3) + FileName$(4); 'Read settings from chosen file var n% := Read(auto%, noiseDb, noiseDurSet%, prePulseDb, prePulseDur%, numPrep%, pulseDb, pulseDur%, numPulse%, ISI%, numPair%, ITISet, ITIvar%, blockSeq$); 'Update all dialogue values with settings from file DlgValue(1,auto%); DlgValue(2,noiseDb); DlgValue(3,noiseDurSet%); DlgValue(4,prePulseDb); DlgValue(5,prePulseDur%); DlgValue(6,numPrep%); DlgValue(7,pulseDb); DlgValue(8,pulseDur%); DlgValue(9,numPulse%); DlgValue(10,ISI%); DlgValue(11,numPair%); DlgValue(12,ITIset); DlgValue(13,ITIvar%); DlgValue$(14,blockSeq$); DlgValue$(15,currentSettingsFile$); 'Toggle "Enable automatic program" checkbox Change%(1); FileClose(); return 1; end; func SaveSettings%() FileNew(1); 'Print current dialogue values into new file var i%; for i% := 1 to 13 do Print("%f,", DlgValue(i%)); next; Print("%s", DlgValue$(14)); 'Prompt user to save the file with settings FileSaveAs(""); currentSettingsFile$ := FileName$(3) + FileName$(4); DlgValue$(15, currentSettingsFile$); FileClose(); return 1; end; 'Start experiment func Start%() SampleStart(); 'Start sampling Resume%(); 'Start background noise and update doolbar buttons ToolbarEnable(1, 1); 'Enable Stop button return 1; end; 'Pause sound output func Pause%() ToolbarSet(2, "Resume", Resume%); 'Replace "Pause" button with "Resume" SampleKey("h"); return 1; end; 'Resume output after pause func Resume%() ToolbarSet(2, "Pause", Pause%); 'Replace "Resume" button with "Pause" SampleKey("s"); 'Start background noise return 1; end; 'Stop sampling func Stop%() SampleStop(); 'Stop sampling SampleKey("h"); 'Stop sequencer output return 0; end; 'Generate waves for sound output proc makeWaves() var ExpN$; 'Virtual channel expression for background noize var ExpR$; 'Virtual channel expression for prepulse var ExpP$; 'Virtual channel expression for pulse var DAC% := 0; 'Output DAC var memN%; 'Memory channel for noise var memR%; 'Memory channel for prepulse var memP%; 'Memory channel for pulse var Noise[0]; 'Array for generated white noise 'Create new file for virtual data var virtDataHandle% := FileNew(7, 2, 5, 1, 10); 'upt = 5 us MemDeleteItem(memN%); MemDeleteItem(memR%); MemDeleteItem(memP%); 'Create new memory channels and attach them to the current file memN% := Memchan(1, 0, 0.00002); '4 times larger than 'upt' argument of FileNew() memR% := Memchan(1, 0, 0.00002); memP% := Memchan(1, 0, 0.00002); 'Convert sound volume in decibels to output voltage in mV var noiseAmp := Db2Amp(noiseDb); var prePulseAmp := Db2Amp(prePulseDb); var pulseAmp := Db2Amp(pulseDb); 'Generate noise sequence for background noise Resize Noise[noisePer% / Binsize(memN%)]; Rand(Noise[], 2*noiseAmp, -noiseAmp); '2*noiseAmp - scale of the random number Memsetitem(memN%, 0, 0, Noise[]); '-noiseAmp - offset of the random number const nMax% := 1000000; var wave[nMax%]; 'Real and integer wave, first time var n% := ChanData(memN%, wave, 0, MaxTime(memN%)); 'Generate noise sequence for prepulse Resize Noise[prePulseDur% / Binsize(memR%)]; Rand(Noise[], 2*prePulseAmp, -prePulseAmp); Memsetitem(memR%, 0, 0, Noise[]); 'Generate noise sequence for pulse Resize Noise[pulseDur% / Binsize(memP%)]; Rand(Noise[], 2*pulseAmp, -pulseAmp); Memsetitem(memP%, 0, 0, Noise[]); var virtChanN := VirtualChan(0, "", memN%); var virtChanR := VirtualChan(0, "", memR%); var virtChanP := VirtualChan(0, "", memP%); 'Form envelope ExpN$ := Print$("Ch(%d)", memN%); 'Set the sample rate to match memory channel, or else DAC output will be trimmed VirtualChan(virtChanN, "", 0, Binsize(memN%)); VirtualChan(virtChanN, ExpN$); 'Write the expression ChanTitle$(virtChanN, "Noise"); PlayWaveAdd("N", "Noise", DAC%, 0, noisePer%, virtChanN, 1); 'Add waveform to list ExpR$ := Print$("Ch(%d)", memR%); VirtualChan(virtChanR, "", 0, Binsize(memR%)); VirtualChan(virtChanR, ExpR$); ChanTitle$(virtChanN, "Prepulse"); PlayWaveAdd("R", "Prepulse", DAC%, 0, prePulseDur%, virtChanR, 1); ExpP$ := Print$("Ch(%d)", memP%); VirtualChan(virtChanP, "", 0, Binsize(memP%)); VirtualChan(virtChanP, ExpP$); ChanTitle$(virtChanP, "Pulse"); PlayWaveAdd("P", "Pulse", DAC%, 0, pulseDur%, virtChanP, 1); PrintLog(SampleHandle(0)); 'After data has been loaded, close the view with the virtual waveforms View(virtDataHandle%); FileClose(0, -1); return; end; 'Convert sound volume in decibels into voltage amplitude func Db2Amp(db) ' db - sound level in decibels set by user ' volume - percent of the maximal output voltage amplitude on the given apparatus ' amplitude - output voltage amplitude ' ' Calibration: ' 1. Assign ' var volume := db; ' 2. Set "Background volume" in the Settings dialogue box in the range 1-100 ' and measure the actual sound volume in the apparatus' chamber ' with a sound level meter ' ! It is important to measure the sound level on a steady noise ' rather than short pulses, as the time of the pulse might not be enough ' for a sound level meter to measure it correctly ' 2.1. Set "Background volume" as 100. ' If you use a sound amplifier, set its own volume to the lowest level ' that yields the maximal sound volume necessary in the experiment ' (e.g. 120 db) ' 2.2. Set "Background volume" in the range 1-100 with a small step. ' For each value measure the corresponding sound level ' ! It may be necessary to adjust the sound level meter's settings ' to different sound volume ranges ' 3. Plot sound level as a function of the "Background volume" in the range 1-100. ' Make a logarithmic approximation (e.g. in the MS Excel) ' 4. Take the values of "a" and "b" from the resulting equation in the format ' y=a*ln(x)+b, where y=db, and x=volume. ' 5. Assign the values to the variables "a" and "b" below, ' reassign "volume" to the formula var a := 9.7861; var b := 72.061; 'Comment for calibration: var volume := Pow(_e,(db-b)/a); 'Uncomment for calibration: 'var volume := db; var maxVoltage := 2; 'Maximal output voltage on the given apparatus var amplitude := maxVoltage * volume / 100; return amplitude; end; func WriteSequence%() var Seq%; 'Sequence file handle var Err%; 'Error check var Path$; Const Seq$ := "PPI_Seq.pls"; 'Path is the location of this script Path$ := View(App(3)).Filename$(1) + View(App(3)).Filename$(2); Seq% := Filenew(2, 0); If seq% < 0 then Message("Sequence file could not be produced. Halting"); halt; endif View(Seq%); var msPerStep := 1; Print(" SET %f,1,0\n", msPerStep); 'msPerStep, DACscale, DACoffset Print(" VAR V1\n"); 'Loop counter, noise periods in starting noise Print(" VAR V2\n"); 'Loop counter, number of prepulses Print(" VAR V3\n"); 'Loop counter, number of pulses Print(" VAR V4\n"); 'Loop counter, number of paired pulses Print(" VAR V5\n"); 'Loop counter, noise periods in ITI Print(" VAR V6\n"); 'Random variation of ITI Print(" VAR V7\n"); 'Noise period, ms Print(" VAR V8\n"); 'Loop counter, noise periods in ITI variation Print(" VAR V9\n"); 'V6 mod V7, remaining time of ITI variation Print("HH: 'h WAVEST S\n"); 'Set 'h' key for halt, stop the output Print(" HALT\n"); 'Stop the sequence 'Loop of background noise on start WriteNoise(); 'For automatic experiment program if auto% then 'Read sequence of blocks set by user var blockSeqLoop$ := blockSeq$; var a%; for a% := 0 to len(blockSeqLoop$) do docase case Left$(blockSeqLoop$, 1) = "R" then Print(" MOVI V2, %d\n", numPrep%); 'Set loop counter if numPrep% <> 0 then Print(" CALL RO\n"); 'Go to pRepulse Output endif; case Left$(blockSeqLoop$, 1) = "P" then Print(" MOVI V3, %d\n", numPulse%); 'Set loop counter if numPulse% <> 0 then Print(" CALL PO\n"); 'Go to Pulse Output endif; case Left$(blockSeqLoop$, 1) = "D" then Print(" MOVI V4, %d\n", numPair%); 'Set loop counter if numPair% <> 0 then Print(" CALL DO\n"); 'Go to Double Output endif; endcase; blockSeqLoop$ := DelStr$(blockSeqLoop$, 1, 1); 'Delete the leftmost character next; Print(" JUMP HH\n"); 'Halt WriteITI(msPerStep); 'For manual control of stimuli's presentation else Print("PP: 'r CALL RO\n"); 'Set 'r' key for pRepulse Print(" JUMP ST\n"); Print("PU: 'p CALL PO\n"); 'Set 'p' key for Pulse Print(" JUMP ST\n"); Print("PA: 'd CALL DO\n"); 'Set 'd' key for Double (prepulse+pulse pair) Print(" JUMP ST\n"); endif; WritePrepulses(); WritePulses(); WriteDoubles(); Err% := FileSaveAs(Path$+Seq$, 2, 1); FileClose(0); Err% := SampleSequencer(Path$ + Seq$); If err% < 0 then Message("Sequencer failed to load. Halting"); Halt endif return 1 end proc WriteNoise() 'Assign variable as a loop counter Print("ST: 's MOVI V1, %d\n", noiseLoop%); 'Play as many noise periods as fit in noiseDur if noiseLoop% <> 0 then Print("NL: WAVEGO N,TW\n"); 'Set noise waveform Print(" WAVEST T\n"); 'Start noise Print(" DELAY s(%d)\n", noisePer%); 'period of noise repetition Print(" WAVEST S\n"); 'Stop noise Print(" DBNZ V1, NL\n"); 'Go to the start of the loop endif; 'Play the remaining time of the starting background noise, 'if there is no whole number of noise periods in it if noiseDur% mod noisePer% <> 0 then Print(" WAVEGO N,TW\n"); Print(" WAVEST T\n"); Print(" DELAY s(%d)\n", noiseDur% mod noisePer%); Print(" WAVEST S\n"); endif; 'In manual control mode play background noise endlessly if not auto% then Print(" JUMP ST\n"); endif; return; end; proc WritePrepulses() 'Prepulse Print("RO: WAVEGO R,TW\n"); 'Set pulse waveform Print(" WAVEST T\n"); 'Start prepulse Print(" DELAY ms(%d)\n", PrePulseDur%); Print(" WAVEST S\n"); 'Stop prepulse if auto% then Print(" CALL IT\n"); 'Write ITI noise Print(" DBNZ V2,RO\n"); 'Repeat for the number of prepulses endif; Print(" RETURN\n"); return; end; proc WritePulses() 'Pulse Print("PO: WAVEGO P,TW\n"); 'Set pulse waveform Print(" WAVEST T\n"); 'Start pulse Print(" DELAY ms(%d)\n", pulseDur%); Print(" WAVEST S\n"); 'Stop pulse if auto% then Print(" CALL IT\n"); 'Write ITI noise Print(" DBNZ V3,PO\n"); 'Repeat for the number of pulses endif; Print(" RETURN\n"); return; end; proc WriteDoubles() 'Prepulse+pulse par 'Run prepulse Print("DO: WAVEGO R,TW\n"); 'Set prepulse waveform Print(" WAVEST T\n"); 'Start prepulse Print(" DELAY ms(%d)\n", PrePulseDur%); 'Run background noise during ISI Print(" WAVEGO N,TW\n"); 'Set noise waveform Print(" WAVEST T\n"); 'Start noise Print(" DELAY ms(%d)\n", ISI%); 'Run pulse Print(" WAVEGO P, TW\n"); 'Set pulse waveform Print(" WAVEST T\n"); 'Start pulse Print(" DELAY ms(%d)\n", pulseDur%); Print(" WAVEST S\n"); 'Stop pulse if auto% then Print(" CALL IT\n"); 'Write ITI noise Print(" DBNZ V4,DO\n"); 'Repeat for the number of pairs endif; Print(" RETURN\n"); return; end; proc WriteITI(msPerStep) 'Assign variable as a loop counter Print("IT: MOVI V5, %d\n", ITIloop%); 'Play as many noise periods as fit in ITI if ITIloop% <> 0 then Print("TL: WAVEGO N,TW\n"); 'Set noise waveform Print(" WAVEST T\n"); 'Start noise Print(" DELAY s(%d)\n", noisePer%); Print(" WAVEST S\n"); 'Stop noise Print(" DBNZ V5, TL\n"); 'Go to the start of the loop endif; 'Play the remaining time of ITI, if there is no whole number of noise periods in it if ITI mod (noisePer% * 1000) <> 0 then Print(" WAVEGO N,TW\n"); Print(" WAVEST T\n"); Print(" DELAY ms(%d)\n", ITI mod (noisePer% * 1000)); Print(" WAVEST S\n"); endif; 'Sequencer instruction MOVRND generates numbers in the range Pow(2,bits)-1 'Express absolute time of ITI variation as a binary logarithm 'Divide ITIvarVal by msPerStep of sequencer's SET directive to match DAC clock ticks var bits%; if ITIvarVal > 1 then '0 causes error in the logarithm bits% := Round(Ln(ITIvarVal/msPerStep)/Ln(2)); WriteITIvariation(bits%); else Print(" RETURN\n"); endif; return; end; proc WriteITIvariation(bits%) Print(" MOVRND V6,%d\n", bits%); 'V6 = random num in range Pow(2,bits)-1 Print(" BGT V6, ms(%d), TRIMVAR\n", ITIvarVal); 'if V6>ITIvarVal: TRIMVAR Print("CONTVAR: MOVI V7, ms(%d)\n", noisePer% * 1000); 'V7 := noisePer, ms Print(" BLT V6, V7, NORMVAR\n"); 'if V6 < V7: output V6 without loop 'Prepare variables for loop Print(" MOV V8, v6\n"); 'V8 := V6 (full variation) Print(" DIV V8, v7\n"); 'V8 /= V7 (whole num of noise periods in var) Print(" NEG V9, v8\n"); 'V9 := -V8 Print(" MUL V9, V7\n"); 'V9 *= V7 Print(" ADD V9, V6\n"); 'V9 += V6 (V9=V6 mod V7 - remaining time of var) 'Play as many noise periods as fit in ITIvarVal Print("VARLOOP: WAVEGO N,TW\n"); 'Set noise waveform Print(" WAVEST T\n"); 'Start noise Print(" DELAY V7\n"); 'Play noise for one noise period Print(" WAVEST S\n"); 'Stop noise Print(" DBNZ V8, VARLOOP\n");'Go to the start of the loop 'Check if there is any time of variation remained Print(" BEQ V9, 0, RET\n"); 'if V9=0: RETURN 'Play the remaining time of var, if there is no whole number of noise periods in it Print(" WAVEGO N,TW\n"); 'Set noise waveform Print(" WAVEST T\n"); 'Start noise Print(" DELAY V9\n"); 'Play the remainder of noise Print(" WAVEST S\n"); 'Stop noise Print(" JUMP RET\n"); 'RETURN 'Output var in one step without loop Print("NORMVAR: WAVEGO N,TW\n"); 'Set noise waveform Print(" WAVEST T\n"); 'Start noise Print(" DELAY V6\n"); 'Play full time of variation Print(" WAVEST S\n"); 'Stop noise Print("RET: RETURN\n"); 'V6 is a power of 2, if it is bigger than the max variation needed, V6:=ITIvarVal Print("TRIMVAR: MOVI V6, ms(%d), CONTVAR\n", ITIvarVal); return; end;