'---------------------------------------------------- ' mustari converter S c r i p t ' ' Converts *.smr files to *.dig files so Mac-Saccade ' can analyze Spike2 files ' 'Unit activity must be in channel 30 as a WaveMark 'channel from SpikeEditor. SpikeEditor, as programmed 'by Robi seems to position the WM wavelet beginning 'at the trigger point on the unit waveform. ' 'Sactrack puts acceptance pulses on channel 29, but 'those pulses are less specifically determined 'on-line as the data is being collected. ' '12/4/06 '---------------------------------------------------- const kBlack% := 0, kRed% := 16, kRed2% := 16, kAcceptPulseCh% := 29, kUnitWMch% := 30, kFalse% := 0, kTrue% := 1; var gFileInnNum%, gFileOutNum%, gFileInnNam$,' := "HzR70test.1f.smr", gFileOutNam$, gFileInnDir$, gProgressView%, gHeaderChr%[448], gLastPercent%, gLastSpikeTime, gUnitPresent, gUnitWrittenFlag%, gNextSpikeTime, gTimeStart, gTimeEnd, gUnitMemPulseCh%, gChanToConvert%, gWaveFormChannels%[16], kUnitWMchMEM%, 'gChList[8], pLogX1 := 10, 'Log window pLogY1 := 80, pLogX2 := 90, pLogY2 := 100, pDataX1 := 0, 'scrolling data window pDataY1 := 0, pDataX2 := 100, pDataY2 := 80; '---------------------------------------------------- ' Main 'Where it all starts '12/7/06 '---------------------------------------------------- gFileInnDir$ := View(App(3)).FileName$(2); 'default, assume data is in the script directory Open(); 'Open the file to convert 'end; 'Main '---------------------------------------------------- ' Open 'Open the file and prepare traces '< 1/7/06 '---------------------------------------------------- proc Open() var i%, j%, ok%, aStr$, aIndex%, aNew%, aOld%, aLittleBit, aCenter, aResult%, item%, aChList[16], aChanToConvert%, aRow, aTab1 :=20, aFuchs%[32+1], aMusta%[32+1], 'aTo%[32+1], aCurrentTo% := 28, aSp$ := " "; 'arrconst(aTo%,0); 'initialize to "doesn't exist" 'aTo%[0] := 0; 'just a placeholder 'aTo%[1] := 1; 'analog unit 'aTo%[2] := 2; 'H eye1 'aTo%[3] := 3; 'V eye1 'aTo%[4] := 4; 'H tar1 'aTo%[5] := 5; 'V tar1 'aTo%[6] := 0; 'deleted HV 'aTo%[7] := 0; 'deleted VV 'aTo%[8] := 8; 'H tar2 'aTo%[9] := 9; 'V tar2 'aTo%[10] := 10; 'H head 'aTo%[11] := 11; 'V head 'aTo%[12] := 12; 'H eye2 'aTo%[13] := 13; 'V eye2 'aTo%[28] := 0; 'aTo%[29] := 29; FilePathSet(gFileInnDir$); gFileInnNum% := FileOpen("",0,1,"Select a file to analyze"); gFileInnNam$ := FileName$(); chanshow(-1); if gFileInnNum% < 0 then halt 'user clicked the CANCEL button else view(gFileInnNum%); 'the contents for [fuchs-1] is in mustari aFuchs%[00] := 11; aFuchs%[01] := 1; aFuchs%[02] := 2; aFuchs%[03] := 5; aFuchs%[04] := 6; aFuchs%[05] := 0; aFuchs%[06] := 0; aFuchs%[07] := 3; aFuchs%[08] := 4; aFuchs%[09] := 0; aFuchs%[10] := 0; aFuchs%[11] := 0; aFuchs%[12] := 0; aFuchs%[13] := 0; aFuchs%[14] := 0; aFuchs%[15] := 0; aFuchs%[16] := 0; aFuchs%[17] := 0; aFuchs%[18] := 0; aFuchs%[19] := 0; aFuchs%[20] := 0; aFuchs%[21] := 0; aFuchs%[22] := 0; aFuchs%[23] := 0; aFuchs%[24] := 0; aFuchs%[25] := 0; aFuchs%[26] := 0; aFuchs%[27] := 0; aFuchs%[28] := 12; aFuchs%[29] := 0; aFuchs%[30] := 0; aFuchs%[31] := 0; arrconst(aMusta%,0); 'query user for channel moves DlgCreate("Identify Screen Channel Assignments"); aRow := 1; DlgInteger(1,"Analog Unit", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(2,"H Eye1", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(3,"V Eye1", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(4,"H Target1", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(5,"V Target1", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(6,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(7,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(8,"H Eye2", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(9,"V Eye2", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(10,"H Target2", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(11,"V Target2", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(12,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(13,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(14,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(15,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(16,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(17,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(18,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(19,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(20,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(21,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(22,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(23,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(24,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(25,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(26,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(27,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(28,"-", 0, 32, aTab1, aRow); aRow := aRow + 1; DlgInteger(29,"Acceptance Pulse Unit", 0, 32, aTab1, aRow); aRow := aRow + 1; ok% := DlgShow(aFuchs%[]); if ok% = 0 then halt endif for i% := 0 to 31 do if aFuchs%[i%] <> 0 then aMusta%[aFuchs%[i%]] := aFuchs%[i%]; endif next gFileOutNam$ := gFileInnNam$; aIndex% := instr(gFileOutNam$,".smr"); gFileOutNam$ := delstr$(gFileOutNam$,aIndex%,4); 'chop off the file extension gFileOutNam$ := gFileOutNam$ + "_" + "fuchs" + ".smr"; 'add it back with format label view(gFileInnNum%); ExportChanList(1); 'Initialise the channel list ExportChanList(0,maxtime(),-1); aResult% := FileSaveAs(gFileOutNam$,0,0,"",32); 'Export and set number of channels aResult% := FileClose(); gFileInnNum% := FileOpen(gFileOutNam$,0,3); 'shift existing channels upward chanorder(1); chanshow(-1); xrange(0,10);'maxtime()); aNew% := 28; for aOld% := 30 to 1 step -1 do 're-position any ch in 30 and below in 28 and below if chankind(aOld%) > 0 then aResult% := chansave(aOld%, aNew%); chandelete(aOld%); chanshow(aNew%); if aMusta%[aOld%] > 0 then aMusta%[aOld%] := aNew%; endif aNew% := aNew% - 1; ' test if aCurrentTo% has a channel already ' if filled then ' message ' halt ' end ' copy aOld% to aCurrentTo% ' delete aOld% ' dec aCurrentTo% endif next 'halt 'selectively move channels down for i% := 0 to 31 do if aFuchs%[i%] <> 0 then aResult% := chansave(aMusta%[aFuchs%[i%]], i%+1); chandelete(aMusta%[aFuchs%[i%]]); chanshow(i%+1); endif next 'compress remaining channels up 'synthesize ch30 var aMem%, aUnitCh% := 1,'11, aAccCh% := 29,'12, aNumPts% := 10; aMem% := MemChan(6, aNumPts%, binsize(aUnitCh%), 0); 'create a wavemark aResult% := MemImport(aMem%, aUnitCh%, 0, maxtime(), aAccCh%); 'load Event ch from unit WM chan, assumes WM starts at spike threshold aResult% := chansave(aMem%,30); aResult% := chandelete(aMem%); ChanTitle$(30, "SpkEdit"); ChanUnits$(30, "WM"); chanshow(-1); 'show all halt; gFileInnDir$ := filePath$(); cursordelete(-1); 'eliminate all the cursors aResult% := chansave(aMem%,30); aResult% := chandelete(aMem%); chanshow(-1); 'show all View(LogHandle()); 'Make log view the current view EditSelectAll(); 'Select all text in log view EditClear(); 'Delete it Window(pLogX1,pLogY1,pLogX2,pLogY2); 'Display it at the bottom of the screen FrontView(logHandle()); View(gFileInnNum%); gFileInnNam$ := FileName$(); View(gFileInnNum%); Window(pDataX1,pDataY1,pDataX2,pDataY2); XRange(0,maxtime()); 'determine how many waveform channels there are '---------------------------------------------- aChanToConvert% := 0; arrconst(gWaveFormChannels%[],-1); '-1 will force an error if everything is not right for i% := 2 to 16 do 'don't show unit on ch 1 if chankind(i%) = 1 then 'only show waveform type 'ChanShow(i%); yrange(i%,-20,20); gWaveFormChannels%[aChanToConvert%] := i%; aChanToConvert% := aChanToConvert% + 1; endif next 'present a checkbox for every waveform channel '---------------------------------------------- item% := 1; DlgCreate("Deselect Unwanted Channels"); for i% := 0 to aChanToConvert%-1 do if gWaveFormChannels%[i%] >= 0 then aStr$ := aSp$ + str$(gWaveFormChannels%[i%]) + aSp$ + chantitle$(gWaveFormChannels%[i%]) + aSp$; Dlgcheck(item%,aStr$,5); item% := item% + 1; endif next dlglabel(item%, ""); for i% := 0 to aChanToConvert%-1 do aChList[i%] := gWaveFormChannels%[i%]; next; ok% := DlgShow(aChList); if ok% < 1 then halt 'user clicked the CANCEL button endif 'remember which checkboxes have been selected '---------------------------------------------- var howmany%; howmany% := aChanToConvert%; gChanToConvert% := aChanToConvert%; item% := 0; for i% := 0 to howmany% - 1 do if aChList[item%] = kFalse% then chanhide(gWaveFormChannels%[item%]); for j% := item% to aChanToConvert%-1-1 do gWaveFormChannels%[j%] := gWaveFormChannels%[j%+1]; aChList[j%] := aChList[j%+1]; next gWaveFormChannels%[howmany% - 1] := -1; aChList[howmany% - 1] := 0; gChanToConvert% := gChanToConvert% - 1; else item% := item% + 1; endif next if gChanToConvert% > 8 then message("PROBLEM: More than 8 ch to convert.");'Mac Saccade can only handle a maximum of 8 chan halt; endif ChanShow(gUnitMemPulseCh%); 'Create cursors to demarcate area to be converted HCursorDelete(-1); 'delete all horizontal cursors CursorDelete(-1); 'delete all vertical cursors aLittleBit := maxtime()/100; aCenter := maxtime()/2; gTimeStart := aCenter - aLittleBit; aResult% := CursorNew(gTimeStart, 1); 'position the cursor in the middle of the screen CursorLabel(4,1,"Move cursor to where you want to BEGIN the conversion"); gTimeEnd := aCenter + aLittleBit; aResult% := CursorNew(gTimeEnd, 2); 'position the cursor in the middle of the screen CursorLabel(4,2,"Move cursor to where you want to END the conversion"); CursorLabelPos(1,10); CursorLabelPos(2,12); View(gFileInnNum%); FrontView(gFileInnNum%); WindowVisible(1); endif SetToolbarBeginAnalysis(); DoToolbar(); 'Setup the toolbar ToolbarVisible(1); 'Make toolbar visible always end 'open '---------------------------------------------------- ' Convert 'Spike2 --> Fuchslab format conversion. '12/7/06 '---------------------------------------------------- func Convert%() var a%, aIndex%,aInitialStart, aResult%, aMax, aDone%, aInc, aReal, aStr$, aTime, aTemp; view(gFileInnNum%); aTime := Cursor(1); aMax := Cursor(2); if (gTimeStart = aTime) and (gTimeEnd = aMax) then message("Hey! You did not move either vertical cursor."); SetToolbarBeginAnalysis(); DoToolbar(); 'Setup the toolbar endif if aTime > aMax then 'test if the cursors are reversed aTemp := aMax; aTime := aMax; aMax := aTemp; endif aInitialStart := aTime; gFileOutNam$ := gFileInnNam$; aIndex% := instr(gFileOutNam$,".smr"); gFileOutNam$ := delstr$(gFileOutNam$,aIndex%,4); 'chop off the file extension gFileOutNam$ := gFileOutNam$ + "_" + print$("%.3f",cursor(1)) + "-" + print$("%.3f",cursor(2)) + ".dig"; gFileOutNum% := FileOpen(gFileOutNam$,9,1); if gFileOutNum% < 0 then message("Output file probably open by other application."); fileClose(-1); else aResult% := GetHeaderText(); if aResult% > 0 then aStr$ := "READ " + view(gFileInnNum%).FileName$(3) + view(gFileInnNum%).FileName$(5) + "\n" + " Spike2 PC format\n\n"; PrintLog(aStr$); ProgressBar("Converting...",1,0); 'toggle the ProgressBar closed WriteHeader(); view(gFileInnNum%); aInc := Binsize(2); gUnitWrittenFlag% := kFalse%; gLastPercent% := 0; aResult% := ChanKind(gUnitMemPulseCh%); if aResult% = 2 then gUnitPresent := kTrue%; gLastSpikeTime := 0; gNextSpikeTime := NextTime(gUnitMemPulseCh%,0); else gUnitPresent := kFalse%; endif while aTime <= aMax do if gUnitPresent = kTrue% then gUnitWrittenFlag% := kFalse%; if gNextSpikeTime > 0 then 'no spikes found = -1 if aTime >= gNextSpikeTime then WriteISI(gNextSpikeTime-gLastSpikeTime); 'write an ISI frame gLastSpikeTime := gNextSpikeTime; view(gFileInnNum%); gNextSpikeTime := NextTime(gUnitMemPulseCh%,gNextSpikeTime); 'find the next acceptance puls gUnitWrittenFlag% := kTrue%; endif endif endif if gUnitWrittenFlag% = kFalse% then 'this handles two spikes less than 1ms apart WriteData(aTime); aTime += aInc; aReal := ((aTime-aInitialStart)/(aMax-aInitialStart))*100; a% := aReal; if (gLastPercent% <> a%)then ProgressBar("Converting...",0,a%); gLastPercent% := a%; endif endif wend aStr$ := "WRITE " + view(gFileOutNum%).FileName$(3) + view(gFileOutNum%).FileName$(5) + "\n" + " Fuchs Lab Mac format\n\n\n"; PrintLog(aStr$); ProgressBar("Converting...",1,0); 'toggle the ProgressBar closed aStr$ := "Use Resedit to create a resource fork and set type:TEXT creator:SAC1 \n\n"; PrintLog(aStr$); fileClose(gFileInnNum%); endif endif aDone% := Query("\nFinished. What do you want to do now?\n", "Quit", "Convert Another File"); if aDone% = 1 then fileClose(-1); else Open(); endif end 'convert '---------------------------------------------------- ' GetHeaderText '12/4/06 '---------------------------------------------------- func GetHeaderText() var aLen%, i%, aOk%, gHeader$; dlgcreate("Enter Header Text"); gHeader$ := ""; '"digitized with Spike2 on a PC and converted to the Mac format with the ced2fuchs utility"; dlgString(1,"Header Text (up to 448 characters): ",448); aOk% := dlgshow(gHeader$); if aOk% > 0 then arrconst(gHeaderChr%[],0); 'fill with null characters aLen% := Len(gHeader$); for i% := 1 to aLen% do gHeaderChr%[i%-1] := asc(mid$(gHeader$,i%,1)); next endif return aOk%; end 'GetHeaderText '---------------------------------------------------- ' WholeFile 'This allows users to move the cursors to the 'beginning and end of a file with a single click so 'the whole file can be accepted. '12/11/06 '---------------------------------------------------- func WholeFile%() Cursor(1,0); Cursor(2,maxtime()); return kTrue%; end '---------------------------------------------------- ' WriteHeader '12/4/06 '---------------------------------------------------- proc WriteHeader() var ch%, HeaderNum%[32]; HeaderNum%[00] := 1000; 'sample rate HeaderNum%[02] := gChanToConvert%; '4; 'channels HeaderNum%[03] := 8000; 'H cal HeaderNum%[04] := 8000; 'V cal HeaderNum%[05] := 0; 'H off HeaderNum%[06] := 0; 'V off HeaderNum%[13] := -1; 'mac big endian byte arrangement view(gFileOutNum%); BrWEndian(1); 'big endian ... binary Mac-style BWriteSize(2,HeaderNum%[]); BWriteSize(1,gHeaderChr%[]); end 'WriteHeader ''---------------------------------------------------- ' WriteISI '12/4/06 '---------------------------------------------------- proc WriteISI(anISI) var i%, aFrame%[8], anIntISI%; anISI := anISI * 100000; 'convert seconds to 100kHz ticks anIntISI% := round(anISI); 'convert to integer aFrame%[0] := 32768; 'hex=8000, the flag representing a spike aFrame%[1] := anISI; 'the ISI, ticks of 100kHz for i% := 0+2 to gChanToConvert%-1 do aFrame%[i%] := 0; 'filler next 'write a frame with ISI information view(gFileOutNum%); BWriteSize(2,aFrame%[:gChanToConvert%]); 'truncate ISI down to two bytes (Mac-Saccade knows what to do) end '---------------------------------------------------- ' WriteData '12/4/06 '---------------------------------------------------- proc WriteData(aTime) var i%, aFrame%[8]; 'read a frame's worth of channels view(gFileInnNum%); for i% := 0 to gChanToConvert%-1 do aFrame%[i%] := round(chanValue(gWaveFormChannels%[i%],aTime) * 327.68); next 'write a frame's worth of channels view(gFileOutNum%); BWriteSize(2,aFrame%[:gChanToConvert%]); end 'WriteData '---------------------------------------------------- ' DoToolbar '6/20/05 '---------------------------------------------------- proc DoToolbar() Toolbar("", 1023); 'Wait here until quit is pressed end 'DoToolbar '---------------------------------------------------- ' SetToolbarBeginAnalysis '12/7/06 '---------------------------------------------------- proc SetToolbarBeginAnalysis() var a%; ToolbarClear(); 'eliminate all the buttons ToolbarSet(0,"",Idle%); 'Call Idle%() whenever there is free time ToolbarSet(1,"Quit",Quit%); ToolbarSet(2,"You are looking at the whole file. Position the cursors where you want to BEGIN and END the conversion. Change the X-axis scale if you need better resolution. Then click here.",Convert%); ToolbarSet(3,"Convert Whole File.",WholeFile%); end 'SetToolbarBeginAnalysis '---------------------------------------------------- ' Idle ' 'Control comes here when Spike2 has nothing to do. '12/7/06 '---------------------------------------------------- func Idle%() 'nothing to do return kTrue%; 'Stay in toolbar end 'Idle '---------------------------------------------------- ' Quit '6/20/05 '---------------------------------------------------- func Quit%() 'If "Quit" is pressed fileclose(-1); return kFalse%; 'leave toolbar end 'Quit '---------------------------------------------------- ' ProgressBar 'Gives the user a visual way to observe how a process 'is progressing. '12/4/06 '---------------------------------------------------- proc ProgressBar(aTitle$, aToggle, aPercent) var x[4], y[4], hMargin := 0.9, 'area above and below the bar wMargin := 5, 'area left and right of the bar h := 1, 'height of bar w := 100, 'length of bar aOffcenter := 0.25, 'three bars are used, one is centered the others are offset aResult; x[0] := wMargin - 1 ; 'load the data to draw the box y[0] := hMargin; x[1] := wMargin + w; y[1] := hMargin; x[2] := wMargin + w; y[2] := hMargin + h; x[3] := wMargin - 1; y[3] := hMargin + h; 'open or close the view if aToggle = 1 then if ViewKind(gProgressView%) <> 12 then 'if the view does not exist create it gProgressView% := FileNew(12); 'keep window hidden Window(35,46,65,53); 'using percent units WindowTitle$(aTitle$); XRange(0,wMargin*2 + w); 'progress bar plus margins YRange(1,0,hMargin*2 + h); 'progress bar plus margins XYColour(1,kBlack%); 'black lines XAxis(0); 'no axis marks YAxis(0); XYJoin(1,2); 'connect the dots XYDrawMode(1,2,0); 'no points XYDrawMode(1,4,1); 'line thickness 1 XYSetChan(0); 'channel one XYSetChan(0); 'channel two XYSetChan(0); 'channel three XYColour(2,kRed2%); XYColour(3,kRed2%); XYColour(4,kRed2%); 'make all the bars red aResult := XYAddData(1,x[],y[]); 'draw the box; WindowVisible(1); 'now show the window with all of its changes else view(gProgressView%); FileClose(0,-1); 'if the view exists, close it endif 'advance the bar else 'added for safety, but doesn't seem to be required since I can't click away the progress bar window until it is done if ViewKind(gProgressView%) = 12 then 'only if the progress bar window is present, not clicked away var i, aCount, aCenter; FrontView(gProgressView%); 'aim at the progress bar view View(gProgressView%); WindowTitle$(aTitle$); 'refresh the window title in case it was changed Yield(); 'required to update video display aCenter := (y[0] + y[2]) / 2; if aPercent > 0 then 'protect against negative percent aPercent := aPercent - 1; 'one percent really draws a box from 0 up to 1, 99 up to 100 aCount := XYCount(2); 'only add new data, do not over draw anything for i := aCount to aPercent do aResult := XYAddData(2, i+wMargin, aCenter-aOffcenter); 'draw the progress bars aResult := XYAddData(3, i+wMargin, aCenter); aResult := XYAddData(4, i+wMargin, aCenter+aOffcenter); next endif endif endif Yield(); 'required to update video display end 'ProgressBar