diff --git a/Builds/Linux/Makefile b/Builds/Linux/Makefile index 66d62dd8dc52ae67c4f0eb3a8f3b81a11e87b194..e92783d4d0b25efb9ff5920501adfc77f1fa251d 100644 --- a/Builds/Linux/Makefile +++ b/Builds/Linux/Makefile @@ -63,6 +63,7 @@ OBJECTS := \ $(OBJDIR)/RootFinder_239a995f.o \ $(OBJDIR)/State_22979684.o \ $(OBJDIR)/AudioComponent_521bd9c9.o \ + $(OBJDIR)/ArduinoOutput_391e90c4.o \ $(OBJDIR)/Parameter_ae008024.o \ $(OBJDIR)/SpikeDisplayNode_9c52e4ad.o \ $(OBJDIR)/WiFiOutput_fa464ec5.o \ @@ -248,6 +249,11 @@ $(OBJDIR)/AudioComponent_521bd9c9.o: ../../Source/Audio/AudioComponent.cpp @echo "Compiling AudioComponent.cpp" @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" +$(OBJDIR)/ArduinoOutput_391e90c4.o: ../../Source/Processors/ArduinoOutput.cpp + -@mkdir -p $(OBJDIR) + @echo "Compiling ArduinoOutput.cpp" + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + $(OBJDIR)/Parameter_ae008024.o: ../../Source/Processors/Parameter.cpp -@mkdir -p $(OBJDIR) @echo "Compiling Parameter.cpp" diff --git a/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj b/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj index 1d6614e0067b52ab825cb1b73a8c92eb543ac83f..974c112c0cea2377279c0c49ed7bbca5cde163d5 100644 --- a/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/open-ephys.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 8E138283FC265B58D252AAC3 = { isa = PBXBuildFile; fileRef = F4A53064BA75472765338C1D; }; EE1DC0B09AE0727BC7A5A99C = { isa = PBXBuildFile; fileRef = 0D20C3399D0492771F7A808A; }; 4ACF816CB5CDB285D8005AB8 = { isa = PBXBuildFile; fileRef = F74662D3D82975EDB5AD42E0; }; + AEB65E53845FA668D89CE15E = { isa = PBXBuildFile; fileRef = C42446F8ABB3627870E9677D; }; 717D108DC8B2379D556C4B2F = { isa = PBXBuildFile; fileRef = 751C52F2BEA7F1328ED13333; }; 1F67A9ACD509FB4DC5A633DF = { isa = PBXBuildFile; fileRef = 4AEDD076CCA918481C6F9CF2; }; B992DDBFF8928A985EEE1557 = { isa = PBXBuildFile; fileRef = 268005410FB62BCB9099A762; }; @@ -219,6 +220,8 @@ E27B5891A52FDAB2B00901A0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Utilities.h; path = ../../Source/Dsp/Utilities.h; sourceTree = SOURCE_ROOT; }; F74662D3D82975EDB5AD42E0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AudioComponent.cpp; path = ../../Source/Audio/AudioComponent.cpp; sourceTree = SOURCE_ROOT; }; FA55B9FDE138CCB1F16BA905 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AudioComponent.h; path = ../../Source/Audio/AudioComponent.h; sourceTree = SOURCE_ROOT; }; + C42446F8ABB3627870E9677D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ArduinoOutput.cpp; path = ../../Source/Processors/ArduinoOutput.cpp; sourceTree = SOURCE_ROOT; }; + 5779673F042A62E02C4AC06B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ArduinoOutput.h; path = ../../Source/Processors/ArduinoOutput.h; sourceTree = SOURCE_ROOT; }; 751C52F2BEA7F1328ED13333 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Parameter.cpp; path = ../../Source/Processors/Parameter.cpp; sourceTree = SOURCE_ROOT; }; 7B825983F25D8984E02F6FFB = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Parameter.h; path = ../../Source/Processors/Parameter.h; sourceTree = SOURCE_ROOT; }; 4AEDD076CCA918481C6F9CF2 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SpikeDisplayNode.cpp; path = ../../Source/Processors/SpikeDisplayNode.cpp; sourceTree = SOURCE_ROOT; }; @@ -552,6 +555,8 @@ 72123888A7DD78159AA032AF, 2164BFCDF57A5AA752CAA3A2 ); name = DataThreads; sourceTree = "<group>"; }; 33A88A7C3FF426F051834D6A = { isa = PBXGroup; children = ( + C42446F8ABB3627870E9677D, + 5779673F042A62E02C4AC06B, 751C52F2BEA7F1328ED13333, 7B825983F25D8984E02F6FFB, 4AEDD076CCA918481C6F9CF2, @@ -652,7 +657,7 @@ C3E8FB47D6069235EA9D6FD7 = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; PREBINDING = NO; - HEADER_SEARCH_PATHS = "/usr/local/include /usr/local/include/freetype2 $(inherited)"; + HEADER_SEARCH_PATHS = " $(inherited)"; GCC_OPTIMIZATION_LEVEL = 0; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; @@ -670,7 +675,7 @@ 5D7484BAF16E272FF0E9EEAE = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; PREBINDING = NO; - HEADER_SEARCH_PATHS = "/usr/local/include /usr/local/include/freetype2 $(inherited)"; + HEADER_SEARCH_PATHS = " $(inherited)"; GCC_OPTIMIZATION_LEVEL = 3; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; @@ -741,6 +746,7 @@ 8E138283FC265B58D252AAC3, EE1DC0B09AE0727BC7A5A99C, 4ACF816CB5CDB285D8005AB8, + AEB65E53845FA668D89CE15E, 717D108DC8B2379D556C4B2F, 1F67A9ACD509FB4DC5A633DF, B992DDBFF8928A985EEE1557, diff --git a/Builds/VisualStudio2010/open-ephys.vcxproj b/Builds/VisualStudio2010/open-ephys.vcxproj index 3b1150b350d2daed4871c3ec3e1796368ceed61b..ad6aaeaa2d7ac8394ab9972436502051d8359528 100644 --- a/Builds/VisualStudio2010/open-ephys.vcxproj +++ b/Builds/VisualStudio2010/open-ephys.vcxproj @@ -142,6 +142,7 @@ <ClCompile Include="..\..\Source\Dsp\RootFinder.cpp"/> <ClCompile Include="..\..\Source\Dsp\State.cpp"/> <ClCompile Include="..\..\Source\Audio\AudioComponent.cpp"/> + <ClCompile Include="..\..\Source\Processors\ArduinoOutput.cpp"/> <ClCompile Include="..\..\Source\Processors\Parameter.cpp"/> <ClCompile Include="..\..\Source\Processors\SpikeDisplayNode.cpp"/> <ClCompile Include="..\..\Source\Processors\WiFiOutput.cpp"/> @@ -239,6 +240,7 @@ <ClInclude Include="..\..\Source\Dsp\Types.h"/> <ClInclude Include="..\..\Source\Dsp\Utilities.h"/> <ClInclude Include="..\..\Source\Audio\AudioComponent.h"/> + <ClInclude Include="..\..\Source\Processors\ArduinoOutput.h"/> <ClInclude Include="..\..\Source\Processors\Parameter.h"/> <ClInclude Include="..\..\Source\Processors\SpikeDisplayNode.h"/> <ClInclude Include="..\..\Source\Processors\WiFiOutput.h"/> diff --git a/Builds/VisualStudio2010/open-ephys.vcxproj.filters b/Builds/VisualStudio2010/open-ephys.vcxproj.filters index a5a0e0bac27099074fdd15f2e9cf8a464183a4e4..4ba9e28914a412ce685c4ea7521bed3d65a2b3e6 100644 --- a/Builds/VisualStudio2010/open-ephys.vcxproj.filters +++ b/Builds/VisualStudio2010/open-ephys.vcxproj.filters @@ -283,6 +283,9 @@ <ClCompile Include="..\..\Source\Audio\AudioComponent.cpp"> <Filter>open-ephys\Source\Audio</Filter> </ClCompile> + <ClCompile Include="..\..\Source\Processors\ArduinoOutput.cpp"> + <Filter>open-ephys\Source\Processors</Filter> + </ClCompile> <ClCompile Include="..\..\Source\Processors\Parameter.cpp"> <Filter>open-ephys\Source\Processors</Filter> </ClCompile> @@ -570,6 +573,9 @@ <ClInclude Include="..\..\Source\Audio\AudioComponent.h"> <Filter>open-ephys\Source\Audio</Filter> </ClInclude> + <ClInclude Include="..\..\Source\Processors\ArduinoOutput.h"> + <Filter>open-ephys\Source\Processors</Filter> + </ClInclude> <ClInclude Include="..\..\Source\Processors\Parameter.h"> <Filter>open-ephys\Source\Processors</Filter> </ClInclude> diff --git a/Source/Processors/ArduinoOutput.cpp b/Source/Processors/ArduinoOutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2c2841e74977eae1ed8cd5869cb1a62ba0e6f2a --- /dev/null +++ b/Source/Processors/ArduinoOutput.cpp @@ -0,0 +1,131 @@ +/* + ------------------------------------------------------------------ + + This file is part of the Open Ephys GUI + Copyright (C) 2012 Open Ephys + + ------------------------------------------------------------------ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ArduinoOutput.h" + +#include <stdio.h> +#include <unistd.h> /*UNIX standard function definitions */ +#include <termios.h> /*POSIX terminal control definitions */ +#include <fcntl.h> /*File control definitions */ + + +ArduinoOutput::ArduinoOutput() + : GenericProcessor("Arduino Output"), serialport("/dev/ttyACM0") +{ + +} + +ArduinoOutput::~ArduinoOutput() +{ + +} + +// AudioProcessorEditor* ArduinoOutput::createEditor() +// { +// editor = new ArduinoOutputEditor(this); +// return editor; +// } + +void ArduinoOutput::handleEvent(int eventType, MidiMessage& event) +{ + if (eventType == TTL) + { + std::cout << "Received event!" << std::endl; + + const char byte = 0; + write(handle, &byte, 1); + //startTimer((int) float(event.getTimeStamp())/getSampleRate()*1000.0); + } + +} + +void ArduinoOutput::setParameter (int parameterIndex, float newValue) +{ + +} + +bool ArduinoOutput::enable() +{ + struct termios toptions; + int fd; + + handle = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY); + + if (handle == -1) + { + std::cout << "Arduino Output unable to open port." << std::endl; + return false; + } + + if (tcgetattr(handle, &toptions) < 0) + { + std::cout << "Arduino Output couldn't get term attributes" << std::endl; + return false; + } + + speed_t brate = B9600; + + cfsetispeed(&toptions, brate); + cfsetospeed(&toptions, brate); + + // 8N1 + toptions.c_cflag &= ~PARENB; + toptions.c_cflag &= ~CSTOPB; + toptions.c_cflag &= ~CSIZE; + toptions.c_cflag |= CS8; + // no flow control + toptions.c_cflag &= ~CRTSCTS; + + toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines + toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl + + toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw + toptions.c_oflag &= ~OPOST; // make raw + + // see: http://unixwiz.net/techtips/termios-vmin-vtime.html + toptions.c_cc[VMIN] = 0; + toptions.c_cc[VTIME] = 20; + + if( tcsetattr(handle, TCSANOW, &toptions) < 0) { + std::cout << "Arduino Output couldn't set term attributes" << std::endl; + return false; + } +} + +bool ArduinoOutput::disable() +{ + + + +} + +void ArduinoOutput::process(AudioSampleBuffer &buffer, + MidiBuffer &events, + int& nSamples) +{ + + + checkForEvents(events); + + +} \ No newline at end of file diff --git a/Source/Processors/ArduinoOutput.h b/Source/Processors/ArduinoOutput.h new file mode 100644 index 0000000000000000000000000000000000000000..0d42717e69ea298b7ba44e04588ad0acc00606fb --- /dev/null +++ b/Source/Processors/ArduinoOutput.h @@ -0,0 +1,75 @@ +/* + ------------------------------------------------------------------ + + This file is part of the Open Ephys GUI + Copyright (C) 2012 Open Ephys + + ------------------------------------------------------------------ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef __ARDUINOOUTPUT_H_F7BDA585__ +#define __ARDUINOOUTPUT_H_F7BDA585__ + +#include "../../JuceLibraryCode/JuceHeader.h" + +#include "GenericProcessor.h" + +/** + + Provides a serial interface to an Arduino board. + + Based on arduino-serial.c (http://todbot.com/blog/2006/12/06/arduino-serial-c-code-to-talk-to-arduino/) + + @see GenericProcessor + +*/ + +class ArduinoOutput : public GenericProcessor +{ +public: + + ArduinoOutput(); + ~ArduinoOutput(); + + void process(AudioSampleBuffer &buffer, MidiBuffer &events, int& nSamples); + + void setParameter (int parameterIndex, float newValue); + + void handleEvent(int eventType, MidiMessage& event); + + bool enable(); + bool disable(); + + //AudioProcessorEditor* createEditor(); + + bool isSink() {return true;} + +private: + + //void timerCallback(); + int handle; + + const char* serialport; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArduinoOutput); + +}; + + + + +#endif // __ARDUINOOUTPUT_H_F7BDA585__ diff --git a/Source/Processors/DataThreads/DataBuffer.cpp b/Source/Processors/DataThreads/DataBuffer.cpp index 0fa864fc67deafc38868bf132bbc8b1b7dde9566..acb6e8f7955c63750eb6fabf49445f2b5e906fa2 100644 --- a/Source/Processors/DataThreads/DataBuffer.cpp +++ b/Source/Processors/DataThreads/DataBuffer.cpp @@ -24,7 +24,12 @@ #include "DataBuffer.h" DataBuffer::DataBuffer(int chans, int size) - : abstractFifo (size), buffer(chans, size), numChans(chans) {} + : abstractFifo (size), buffer(chans, size), numChans(chans) +{ + timestampBuffer = new int64[size]; + eventCodeBuffer = new int16[size]; + +} DataBuffer::~DataBuffer() {} @@ -33,7 +38,7 @@ void DataBuffer::clear() { buffer.clear(); } -void DataBuffer::addToBuffer(float* data, int numItems) { +void DataBuffer::addToBuffer(float* data, int64* timestamps, int16* eventCodes, int numItems) { // writes one sample for all channels int startIndex1, blockSize1, startIndex2, blockSize2; abstractFifo.prepareToWrite(numItems, startIndex1, blockSize1, startIndex2, blockSize2); @@ -45,6 +50,10 @@ void DataBuffer::addToBuffer(float* data, int numItems) { data + chan, // const float* source 1); // int num samples } + + *(timestampBuffer + startIndex1) = *timestamps; + *(eventCodeBuffer + startIndex1) = *eventCodes; + abstractFifo.finishedWrite(numItems); } @@ -53,7 +62,7 @@ int DataBuffer::getNumSamples() { } -int DataBuffer::readAllFromBuffer (AudioSampleBuffer& data, int maxSize) +int DataBuffer::readAllFromBuffer (AudioSampleBuffer& data, int64* timestamps, int16* eventCodes, int maxSize) { // check to see if the maximum size is smaller than the total number of available ints int numItems = (maxSize < abstractFifo.getNumReady()) ? @@ -63,6 +72,7 @@ int DataBuffer::readAllFromBuffer (AudioSampleBuffer& data, int maxSize) abstractFifo.prepareToRead(numItems, startIndex1, blockSize1, startIndex2, blockSize2); if (blockSize1 > 0) { + for (int chan = 0; chan < data.getNumChannels(); chan++) { data.copyFrom(chan, // destChan 0, // destStartSample @@ -71,11 +81,14 @@ int DataBuffer::readAllFromBuffer (AudioSampleBuffer& data, int maxSize) startIndex1, // sourceStartSample blockSize1); // numSamples } + + memcpy(timestamps, timestampBuffer+startIndex1, blockSize1*4); + memcpy(eventCodes, eventCodeBuffer+startIndex1, blockSize1*2); } - if (blockSize2 > 0) { + if (blockSize2 > 0) { - for (int chan = 0; chan < data.getNumChannels(); chan++) { + for (int chan = 0; chan < data.getNumChannels(); chan++) { data.copyFrom(chan, // destChan blockSize1, // destStartSample buffer, // source @@ -83,6 +96,9 @@ int DataBuffer::readAllFromBuffer (AudioSampleBuffer& data, int maxSize) startIndex2, // sourceStartSample blockSize2); // numSamples } + + memcpy(timestamps + blockSize1, timestampBuffer+startIndex2, blockSize2*4); + memcpy(eventCodes + blockSize1, eventCodeBuffer+startIndex2, blockSize2*2); } abstractFifo.finishedRead(numItems); diff --git a/Source/Processors/DataThreads/DataBuffer.h b/Source/Processors/DataThreads/DataBuffer.h index 290309b702ee0838e17a413e9939f77eb1fdc28c..4992331efcb734458d782ca09cffee0322e76153 100644 --- a/Source/Processors/DataThreads/DataBuffer.h +++ b/Source/Processors/DataThreads/DataBuffer.h @@ -39,13 +39,17 @@ public: DataBuffer(int chans, int size); ~DataBuffer(); void clear(); - void addToBuffer(float* data, int numItems); + void addToBuffer(float* data, int64* ts, int16* eventCodes, int numItems); int getNumSamples(); - int readAllFromBuffer(AudioSampleBuffer& data, int maxSize); + int readAllFromBuffer(AudioSampleBuffer& data, int64* ts, int16* eventCodes, int maxSize); private: AbstractFifo abstractFifo; AudioSampleBuffer buffer; + + int64* timestampBuffer; + int16* eventCodeBuffer; + int numChans; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DataBuffer); diff --git a/Source/Processors/DataThreads/DataThread.h b/Source/Processors/DataThreads/DataThread.h index d5d9084e13a6a2c7085e1bd9d2da52da7155a114..1c607cba3a2d9d2a0263caf8775bb7b85e1f48ea 100644 --- a/Source/Processors/DataThreads/DataThread.h +++ b/Source/Processors/DataThreads/DataThread.h @@ -60,11 +60,19 @@ public: virtual int getNumChannels() = 0; virtual float getSampleRate() = 0; virtual float getBitVolts() = 0; + virtual int getNumEventChannels() {return 0;} SourceNode* sn; + int16 eventCode; + int64 timestamp; + + Time timer; + private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DataThread); diff --git a/Source/Processors/DataThreads/FPGAThread.cpp b/Source/Processors/DataThreads/FPGAThread.cpp index fba32709739c59e1a17ff40786db3be2cbd4e14d..4ccf07c4382d1a7eb4c698a9079beede1102a8a0 100644 --- a/Source/Processors/DataThreads/FPGAThread.cpp +++ b/Source/Processors/DataThreads/FPGAThread.cpp @@ -59,6 +59,8 @@ FPGAThread::FPGAThread(SourceNode* sn) : DataThread(sn), dataBuffer = new DataBuffer(32, 10000); + eventCode = 0; + } @@ -173,7 +175,11 @@ bool FPGAThread::updateBuffer() { thisSample[n] = (float(samp) - 32768.0f)/92768.0f; } - dataBuffer->addToBuffer(thisSample,1); + + // should actually be converting timecode to timestamp: + timestamp = timer.getHighResolutionTicks(); + + dataBuffer->addToBuffer(thisSample, ×tamp, &eventCode, 1); } j++; // keep scanning for timecodes } diff --git a/Source/Processors/DataThreads/FileReaderThread.cpp b/Source/Processors/DataThreads/FileReaderThread.cpp index 91f1dc20cf2c96b7700e451e60781ec68e9bbc48..be0eb49b3313341978c3968f41ef69aff19443ac 100644 --- a/Source/Processors/DataThreads/FileReaderThread.cpp +++ b/Source/Processors/DataThreads/FileReaderThread.cpp @@ -43,7 +43,7 @@ FileReaderThread::FileReaderThread(SourceNode* sn) : DataThread(sn) dataBuffer = new DataBuffer(16, bufferSize*3); - + eventCode = 0; std::cout << "File Reader Thread initialized." << std::endl; @@ -117,7 +117,8 @@ bool FileReaderThread::updateBuffer() if (chan == 15) { - dataBuffer->addToBuffer(thisSample,1); + timestamp = timer.getHighResolutionTicks(); + dataBuffer->addToBuffer(thisSample, ×tamp, &eventCode, 1); chan = 0; } else { chan++; diff --git a/Source/Processors/DataThreads/IntanThread.cpp b/Source/Processors/DataThreads/IntanThread.cpp index 0d1d130ea7afc5634e87c4789be276e2401d1838..98efed098db2db83360ad1a47e87cd62b7a60596 100644 --- a/Source/Processors/DataThreads/IntanThread.cpp +++ b/Source/Processors/DataThreads/IntanThread.cpp @@ -34,10 +34,12 @@ IntanThread::IntanThread(SourceNode* sn) : DataThread(sn), { - dataBuffer = new DataBuffer(16,4096); + dataBuffer = new DataBuffer(17,4096); deviceFound = initializeUSB(true); + eventCode = 0; + } IntanThread::~IntanThread() @@ -51,6 +53,11 @@ int IntanThread::getNumChannels() return 16; } +int IntanThread::getNumEventChannels() +{ + return 6; +} + float IntanThread::getSampleRate() { return 25000.0; @@ -208,13 +215,13 @@ bool IntanThread::updateBuffer() ++ch; - for (int n = 0; n < 1; n++) { // + // for (int n = 0; n < 1; n++) { // // after accounting for bit volts: - thisSample[ch%16+n*16] = float((buffer[index] & 127) + + thisSample[ch%16] = float((buffer[index] & 127) + ((buffer[index+1] & 127) << 7) + ((buffer[index+2] & 3) << 14)) * 0.1907f - 6175.0f; - // these samples should now be in microvolts! + // these samples should now be in microvolts! // bit volt calculation: // 2.5 V range / 2^16 = 38.14 uV @@ -227,14 +234,27 @@ bool IntanThread::updateBuffer() // ((buffer[index+1] & 127) << 7) + // ((buffer[index+2] & 3) << 14) - 32768)/32768; - } - - TTLval = (buffer[index+2] & 4) >> 2; // extract TTL value (bit 3) + //} + + if (ch > 0 && ch < 7) // event channels + { + TTLval = (buffer[index+2] & 4) >> 2; // extract TTL value (bit 3) + + eventCode += (TTLval << (ch-1)); + + } + channelVal = buffer[index+2] & 60; // extract channel value if (channelVal == 60) { - dataBuffer->addToBuffer(thisSample,1); + + timestamp = timer.getHighResolutionTicks(); + + dataBuffer->addToBuffer(thisSample, ×tamp, &eventCode, 1); + + // reset values ch = -1; + eventCode = 0; } } diff --git a/Source/Processors/DataThreads/IntanThread.h b/Source/Processors/DataThreads/IntanThread.h index 254e2bbb36dbf1021b3811299a807c4d0b66f9a7..4a7b17c5e052490ba197ea7bab805e675c882829 100644 --- a/Source/Processors/DataThreads/IntanThread.h +++ b/Source/Processors/DataThreads/IntanThread.h @@ -50,6 +50,7 @@ public: int getNumChannels(); float getSampleRate(); float getBitVolts(); + int getNumEventChannels(); private: @@ -68,7 +69,7 @@ private: unsigned char startCode, stopCode; unsigned char buffer[240]; // should be 5 samples per channel - float thisSample[16]; + float thisSample[17]; // 17 continuous channels and one event channel int ch; diff --git a/Source/Processors/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph.cpp index f2c20aff089a9e5d18524c0ba1ba36af83150db3..b00bcc2d417debe12f92f2e402ae09b0fc852545 100644 --- a/Source/Processors/ProcessorGraph.cpp +++ b/Source/Processors/ProcessorGraph.cpp @@ -37,6 +37,7 @@ #include "SourceNode.h" #include "SpikeDetector.h" #include "WiFiOutput.h" +#include "ArduinoOutput.h" #include "Utilities/Splitter.h" #include "Utilities/Merger.h" #include "../UI/UIComponent.h" @@ -455,6 +456,10 @@ GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& descrip std::cout << "Creating a WiFi node." << std::endl; processor = new WiFiOutput(); } + else if (subProcessorType.equalsIgnoreCase("Arduino Output")) { + std::cout << "Creating an Arduino node." << std::endl; + processor = new ArduinoOutput(); + } //sendActionMessage("New sink created."); } diff --git a/Source/Processors/SourceNode.cpp b/Source/Processors/SourceNode.cpp index 4e1e5aa0f88de2aedf480c8eae75a26e33822d89..0a4f33b66f1415cf0e43b2190868cb607ff6db49 100644 --- a/Source/Processors/SourceNode.cpp +++ b/Source/Processors/SourceNode.cpp @@ -47,13 +47,26 @@ SourceNode::SourceNode(const String& name_) { enabledState(false); } + + numEventChannels = dataThread->getNumEventChannels(); + eventChannelState = new int[numEventChannels]; + for (int i = 0; i < numEventChannels; i++) + { + eventChannelState[i] = 0; + } + } else { enabledState(false); + numEventChannels = 0; } // check for input source every few seconds startTimer(sourceCheckInterval); + timestampBuffer = new int64[10000]; // 10000 samples per buffer max? + eventCodeBuffer = new int16[10000]; + + } SourceNode::~SourceNode() @@ -213,8 +226,33 @@ void SourceNode::process(AudioSampleBuffer &buffer, //std::cout << "SOURCE NODE" << std::endl; buffer.clear(); - nSamples = inputBuffer->readAllFromBuffer(buffer,buffer.getNumSamples()); + nSamples = inputBuffer->readAllFromBuffer(buffer, timestampBuffer, eventCodeBuffer, buffer.getNumSamples()); + // generate timestamp + + // generate events + + for (int i = 0; i < nSamples; i++) + { + for (int c = 0; c < numEventChannels; c++) + { + int state = eventCodeBuffer[i] & (1 << c); + + if (eventChannelState[c] != state) + { + if (state == 0) + std::cout << "Channel " << c << " off!" << std::endl; + else + addEvent(events, TTL, state); + //std::cout << "Channel " << c << " on!" << std::endl; + + eventChannelState[c] = state; + } + } + } + + //std::cout << *eventCodeBuffer << std::endl; + } diff --git a/Source/Processors/SourceNode.h b/Source/Processors/SourceNode.h index be72bcd8b60b0ce83195a49e72b56f0a4a2fef61..3a025002971d5beb861f2eb911376c4f85bda2b2 100644 --- a/Source/Processors/SourceNode.h +++ b/Source/Processors/SourceNode.h @@ -77,6 +77,8 @@ public: private: + int numEventChannels; + int sourceCheckInterval; bool wasDisabled; @@ -86,6 +88,10 @@ private: ScopedPointer<DataThread> dataThread; DataBuffer* inputBuffer; + int64* timestampBuffer; + int16* eventCodeBuffer; + int* eventChannelState; + void updateSettings(); int* numSamplesInThisBuffer; diff --git a/Source/UI/ProcessorList.cpp b/Source/UI/ProcessorList.cpp index 5f135a2c041b9df4bf5910f449471ea1775cbee4..fa34aae64aa99dec3cbca0bb163bf6512496e4a6 100644 --- a/Source/UI/ProcessorList.cpp +++ b/Source/UI/ProcessorList.cpp @@ -59,6 +59,7 @@ ProcessorList::ProcessorList() : isDragging(false), sinks->addSubItem(new ProcessorListItem("LFP Viewer")); sinks->addSubItem(new ProcessorListItem("Spike Viewer")); sinks->addSubItem(new ProcessorListItem("WiFi Output")); + sinks->addSubItem(new ProcessorListItem("Arduino Output")); ProcessorListItem* utilities = new ProcessorListItem("Utilities"); utilities->addSubItem(new ProcessorListItem("Splitter")); diff --git a/open-ephys.jucer b/open-ephys.jucer index 314b3828c2e43567ae5e6ea13b8df08c4fd345a0..7d7ee7dba3392bd54696c6c1f872e033e5e9d446 100644 --- a/open-ephys.jucer +++ b/open-ephys.jucer @@ -187,6 +187,10 @@ file="Source/Audio/AudioComponent.h"/> </GROUP> <GROUP id="yQmqZWk" name="Processors"> + <FILE id="wBm4y2" name="ArduinoOutput.cpp" compile="1" resource="0" + file="Source/Processors/ArduinoOutput.cpp"/> + <FILE id="1sbSwS7" name="ArduinoOutput.h" compile="0" resource="0" + file="Source/Processors/ArduinoOutput.h"/> <FILE id="pQaYQiE" name="Parameter.cpp" compile="1" resource="0" file="Source/Processors/Parameter.cpp"/> <FILE id="0jxvc4H" name="Parameter.h" compile="0" resource="0" file="Source/Processors/Parameter.h"/> <FILE id="arRy5R" name="SpikeDisplayNode.cpp" compile="1" resource="0"