From 49defa9d31a90496c5ef04f5f6ff66028c8dc53f Mon Sep 17 00:00:00 2001 From: shayo <shay.ohayon@gmail.com> Date: Thu, 8 Nov 2012 16:20:04 -0800 Subject: [PATCH] Modified fopen to be "binary" in RecordNode Weird behavior was observed during fwrite when files were only opened with "a". fwrite did not write the expected number of bytes (probably augumented things with a \n or \r). This caused serious problems parsing the dumped files in matlab. Forcing a binary fopen option fixed things. --- Source/Processors/RecordNode.cpp | 842 +++++++++++++++---------------- 1 file changed, 421 insertions(+), 421 deletions(-) diff --git a/Source/Processors/RecordNode.cpp b/Source/Processors/RecordNode.cpp index d63342534..b7e8483a6 100755 --- a/Source/Processors/RecordNode.cpp +++ b/Source/Processors/RecordNode.cpp @@ -1,421 +1,421 @@ -/* - ------------------------------------------------------------------ - - 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 "RecordNode.h" -#include "ProcessorGraph.h" - -RecordNode::RecordNode() - : GenericProcessor("Record Node"), isRecording(false), isProcessing(false), - timestamp(0), signalFilesShouldClose(false) -{ - - - continuousDataIntegerBuffer = new int16[10000]; - continuousDataFloatBuffer = new float[10000]; - signalFilesShouldClose = false; - - settings.numInputs = 128; - settings.numOutputs = 0; - - // 128 inputs, 0 outputs - setPlayConfigDetails(getNumInputs(),getNumOutputs(),44100.0,128); - -} - - -RecordNode::~RecordNode() { - -} - -void RecordNode::setChannel(int id, int chan) -{ - - std::cout << "Record node setting channel." << std::endl; - - for (int i = 0; i < continuousChannels.size(); i++) - { - - if (continuousChannels[i].nodeId == id && - continuousChannels[i].chan == chan) - { - std::cout << "Found channel " << i << std::endl; - setCurrentChannel(i); - break; - } - - } -} - -void RecordNode::setChannelStatus(int chan, bool status) -{ - - std::cout << "Setting channel status!" << std::endl; - setCurrentChannel(chan); - - if (status) - setParameter(2, 1.0f); - else - setParameter(2, 0.0f); - -} - -// void RecordNode::enableCurrentChannel(bool state) -// { -// continuousChannels[nextAvailableChannel].isRecording = state; -// } - -void RecordNode::resetConnections() -{ - //std::cout << "Resetting connections" << std::endl; - nextAvailableChannel = 0; - wasConnected = false; - - continuousChannels.clear(); - eventChannels.clear(); -} - -void RecordNode::filenameComponentChanged(FilenameComponent* fnc) -{ - - std::cout << "Got a new file" << std::endl; - dataDirectory = fnc->getCurrentFile(); - std::cout << "File name: " << dataDirectory.getFullPathName(); - if (dataDirectory.isDirectory()) - std::cout << " is a directory." << std::endl; - else - std::cout << " is NOT a directory." << std::endl; - - createNewDirectory(); - -} - - -void RecordNode::addInputChannel(GenericProcessor* sourceNode, int chan) -{ - - if (chan != getProcessorGraph()->midiChannelIndex) - { - - setPlayConfigDetails(getNextChannel(false)+1,0,44100.0,128); - - Channel newChannel; - - std::cout << "Record node adding channel." << std::endl; - - newChannel.nodeId = sourceNode->getNodeId(); - newChannel.chan = chan; - newChannel.name = sourceNode->getOutputChannelName(chan); - newChannel.isRecording = sourceNode->recordStatus(chan); - - String filename = rootFolder.getFullPathName(); - filename += "/"; - filename += newChannel.nodeId; - filename += "_"; - filename += newChannel.name; - filename += ".continuous"; - - newChannel.filename = filename; - newChannel.file = 0; - - if (newChannel.isRecording) - std::cout << " This channel will be recorded." << std::endl; - else - std::cout << " This channel will NOT be recorded." << std::endl; - - std::cout << "adding channel " << getNextChannel(false) << std::endl; - - std::pair<int, Channel> newPair (getNextChannel(false), newChannel); - - //std::cout << "adding channel " << getNextChannel(false) << std::endl; - - continuousChannels.insert(newPair); - - - } else { - - std::map<int, Channel> eventChans; - - int ID = sourceNode->getNodeId(); - - for (int n = 0; n < sourceNode->settings.eventChannelIds.size(); n++) - { - - Channel newChannel; - - newChannel.nodeId = ID; - newChannel.chan = sourceNode->settings.eventChannelIds[n]; - newChannel.name = sourceNode->settings.eventChannelNames[n]; - newChannel.isRecording = true; - newChannel.file = 0; - - std::pair<int, Channel> newPair (newChannel.chan, newChannel); - - eventChans.insert(newPair); - - } - - std::pair<int, std::map<int, Channel> > newPair (ID, eventChans); - - eventChannels.insert(newPair); - - } - -} - -void RecordNode::createNewDirectory() -{ - std::cout << "Creating new directory." << std::endl; - - Time calendar = Time::getCurrentTime(); - - Array<int> t; - t.add(calendar.getYear()-2000); - t.add(calendar.getMonth()+1); // January = 0 - t.add(calendar.getDayOfMonth()); - t.add(calendar.getHours()); - t.add(calendar.getMinutes()); - t.add(calendar.getSeconds()); - - String filename = ""; - - for (int n = 0; n < t.size(); n++) - { - if (t[n] < 10) - filename += "0"; - - filename += t[n]; - - if (n == 2) - filename += "_"; - else if (n < 5) - filename += "-"; - } - - rootFolder = File(dataDirectory.getFullPathName() + File::separator + filename); - -} - - -void RecordNode::setParameter (int parameterIndex, float newValue) -{ - - // 0 = stop recording - // 1 = start recording - // 2 = toggle individual channel (0.0f = OFF, anything else = ON) - - if (parameterIndex == 1) { - - isRecording = true; - std::cout << "START RECORDING." << std::endl; - - if (!rootFolder.exists()) - rootFolder.createDirectory(); - - // create / open necessary files - for (int i = 0; i < continuousChannels.size(); i++) - { - if (continuousChannels[i].isRecording) - { - std::cout << "OPENING FILE: " << continuousChannels[i].filename << std::endl; - - File f = File(continuousChannels[i].filename); - - continuousChannels[i].file = fopen(continuousChannels[i].filename.toUTF8(), "a"); - - if (!f.exists()) - { - // create header (needs more details, obviously) - String header = "THIS IS A HEADER."; - fwrite(header.toUTF8(), 1, header.getNumBytesAsUTF8(), continuousChannels[i].file); - } - - } - } - - - } else if (parameterIndex == 0) { - - - std::cout << "STOP RECORDING." << std::endl; - - if (isRecording) { - // close necessary files - - signalFilesShouldClose = true; - - } - - isRecording = false; - - - } else if (parameterIndex == 2) { - - if (isProcessing) { - - std::cout << "Toggling channel " << currentChannel << std::endl; - - if (newValue == 0.0f) { - continuousChannels[currentChannel].isRecording = false; - - if (isRecording) { - std::cout << "CLOSING FILE: " << continuousChannels[currentChannel].filename << std::endl; - fclose(continuousChannels[currentChannel].file); - } - - } - else { - continuousChannels[currentChannel].isRecording = true; - - if (isRecording) { - std::cout << "OPENING FILE: " << continuousChannels[currentChannel].filename << std::endl; - continuousChannels[currentChannel].file = - fopen(continuousChannels[currentChannel].filename.toUTF8(), "a"); - } - } - } - } -} - -void RecordNode::closeAllFiles() -{ - - for (int i = 0; i < continuousChannels.size(); i++) - { - if (continuousChannels[i].isRecording) - { - std::cout << "CLOSING FILE: " << continuousChannels[i].filename << std::endl; - fclose(continuousChannels[i].file); - } - } -} - -bool RecordNode::enable() -{ - isProcessing = true; - return true; -} - - -bool RecordNode::disable() -{ - // close files if necessary - setParameter(0, 10.0f); - - isProcessing = false; - - return true; -} - -float RecordNode::getFreeSpace() -{ - return 1.0f - float(dataDirectory.getBytesFreeOnVolume())/float(dataDirectory.getVolumeTotalSize()); -} - -void RecordNode::writeContinuousBuffer(float* data, int nSamples, int channel) -{ - - // scale the data appropriately -- currently just getting it into the right - // range; actually need to take into account the gain of each channel - for (int n = 0; n < nSamples; n++) - { - *(continuousDataFloatBuffer+n) = *(data+n) / 10000.0f; - } - - // find file and write samples to disk - - AudioDataConverters::convertFloatToInt16BE(continuousDataFloatBuffer, continuousDataIntegerBuffer, nSamples); - - //int16 samps = nSamples; - - fwrite(×tamp, // ptr - 8, // size of each element - 1, // count - continuousChannels[channel].file); // ptr to FILE object - - fwrite(&nSamples, // ptr - sizeof(nSamples), // size of each element - 1, // count - continuousChannels[channel].file); // ptr to FILE object - - int n = fwrite(continuousDataIntegerBuffer, // ptr - 2, // size of each element - nSamples, // count - continuousChannels[channel].file); // ptr to FILE object - // n must equal "count", otherwise there was an error -} - -void RecordNode::writeEventBuffer(MidiMessage& event, int node, int channel) -{ - // find file and write samples to disk - -} - -void RecordNode::process(AudioSampleBuffer &buffer, - MidiBuffer &midiMessages, - int& nSamples) -{ - - //std::cout << "Record node processing block." << std::endl; - //std::cout << "Num channels: " << buffer.getNumChannels() << std::endl; - - - if (isRecording) { - - timestamp = timer.getHighResolutionTicks(); - - // WHY IS THIS AFFECTING THE LFP DISPLAY? - //buffer.applyGain(0, nSamples, 5.2438f); - - // cycle through events -- extract the samples per channel - // NOT YET IMPLEMENTED - - // cycle through buffer channels - for (int i = 0; i < buffer.getNumChannels(); i++) - { - - if (continuousChannels[i].isRecording) - { - // write buffer to disk! - writeContinuousBuffer(buffer.getSampleData(i), - nSamples, - i); - - //std::cout << "Record channel " << i << std::endl; - } - - - } - - return; - - } - - // this is intended to prevent parameter changes from closing files - // before recording stops - if (signalFilesShouldClose) - { - closeAllFiles(); - signalFilesShouldClose = false; - } - -} +/* + ------------------------------------------------------------------ + + 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 "RecordNode.h" +#include "ProcessorGraph.h" + +RecordNode::RecordNode() + : GenericProcessor("Record Node"), isRecording(false), isProcessing(false), + timestamp(0), signalFilesShouldClose(false) +{ + + + continuousDataIntegerBuffer = new int16[10000]; + continuousDataFloatBuffer = new float[10000]; + signalFilesShouldClose = false; + + settings.numInputs = 128; + settings.numOutputs = 0; + + // 128 inputs, 0 outputs + setPlayConfigDetails(getNumInputs(),getNumOutputs(),44100.0,128); + +} + + +RecordNode::~RecordNode() { + +} + +void RecordNode::setChannel(int id, int chan) +{ + + std::cout << "Record node setting channel." << std::endl; + + for (int i = 0; i < continuousChannels.size(); i++) + { + + if (continuousChannels[i].nodeId == id && + continuousChannels[i].chan == chan) + { + std::cout << "Found channel " << i << std::endl; + setCurrentChannel(i); + break; + } + + } +} + +void RecordNode::setChannelStatus(int chan, bool status) +{ + + std::cout << "Setting channel status!" << std::endl; + setCurrentChannel(chan); + + if (status) + setParameter(2, 1.0f); + else + setParameter(2, 0.0f); + +} + +// void RecordNode::enableCurrentChannel(bool state) +// { +// continuousChannels[nextAvailableChannel].isRecording = state; +// } + +void RecordNode::resetConnections() +{ + //std::cout << "Resetting connections" << std::endl; + nextAvailableChannel = 0; + wasConnected = false; + + continuousChannels.clear(); + eventChannels.clear(); +} + +void RecordNode::filenameComponentChanged(FilenameComponent* fnc) +{ + + std::cout << "Got a new file" << std::endl; + dataDirectory = fnc->getCurrentFile(); + std::cout << "File name: " << dataDirectory.getFullPathName(); + if (dataDirectory.isDirectory()) + std::cout << " is a directory." << std::endl; + else + std::cout << " is NOT a directory." << std::endl; + + createNewDirectory(); + +} + + +void RecordNode::addInputChannel(GenericProcessor* sourceNode, int chan) +{ + + if (chan != getProcessorGraph()->midiChannelIndex) + { + + setPlayConfigDetails(getNextChannel(false)+1,0,44100.0,128); + + Channel newChannel; + + std::cout << "Record node adding channel." << std::endl; + + newChannel.nodeId = sourceNode->getNodeId(); + newChannel.chan = chan; + newChannel.name = sourceNode->getOutputChannelName(chan); + newChannel.isRecording = sourceNode->recordStatus(chan); + + String filename = rootFolder.getFullPathName(); + filename += "/"; + filename += newChannel.nodeId; + filename += "_"; + filename += newChannel.name; + filename += ".continuous"; + + newChannel.filename = filename; + newChannel.file = 0; + + if (newChannel.isRecording) + std::cout << " This channel will be recorded." << std::endl; + else + std::cout << " This channel will NOT be recorded." << std::endl; + + std::cout << "adding channel " << getNextChannel(false) << std::endl; + + std::pair<int, Channel> newPair (getNextChannel(false), newChannel); + + //std::cout << "adding channel " << getNextChannel(false) << std::endl; + + continuousChannels.insert(newPair); + + + } else { + + std::map<int, Channel> eventChans; + + int ID = sourceNode->getNodeId(); + + for (int n = 0; n < sourceNode->settings.eventChannelIds.size(); n++) + { + + Channel newChannel; + + newChannel.nodeId = ID; + newChannel.chan = sourceNode->settings.eventChannelIds[n]; + newChannel.name = sourceNode->settings.eventChannelNames[n]; + newChannel.isRecording = true; + newChannel.file = 0; + + std::pair<int, Channel> newPair (newChannel.chan, newChannel); + + eventChans.insert(newPair); + + } + + std::pair<int, std::map<int, Channel> > newPair (ID, eventChans); + + eventChannels.insert(newPair); + + } + +} + +void RecordNode::createNewDirectory() +{ + std::cout << "Creating new directory." << std::endl; + + Time calendar = Time::getCurrentTime(); + + Array<int> t; + t.add(calendar.getYear()-2000); + t.add(calendar.getMonth()+1); // January = 0 + t.add(calendar.getDayOfMonth()); + t.add(calendar.getHours()); + t.add(calendar.getMinutes()); + t.add(calendar.getSeconds()); + + String filename = ""; + + for (int n = 0; n < t.size(); n++) + { + if (t[n] < 10) + filename += "0"; + + filename += t[n]; + + if (n == 2) + filename += "_"; + else if (n < 5) + filename += "-"; + } + + rootFolder = File(dataDirectory.getFullPathName() + File::separator + filename); + +} + + +void RecordNode::setParameter (int parameterIndex, float newValue) +{ + + // 0 = stop recording + // 1 = start recording + // 2 = toggle individual channel (0.0f = OFF, anything else = ON) + + if (parameterIndex == 1) { + + isRecording = true; + std::cout << "START RECORDING." << std::endl; + + if (!rootFolder.exists()) + rootFolder.createDirectory(); + + // create / open necessary files + for (int i = 0; i < continuousChannels.size(); i++) + { + if (continuousChannels[i].isRecording) + { + std::cout << "OPENING FILE: " << continuousChannels[i].filename << std::endl; + + File f = File(continuousChannels[i].filename); + + continuousChannels[i].file = fopen(continuousChannels[i].filename.toUTF8(), "a+b"); + + if (!f.exists()) + { + // create header (needs more details, obviously) + String header = "THIS IS A HEADER."; + fwrite(header.toUTF8(), 1, header.getNumBytesAsUTF8(), continuousChannels[i].file); + } + + } + } + + + } else if (parameterIndex == 0) { + + + std::cout << "STOP RECORDING." << std::endl; + + if (isRecording) { + // close necessary files + + signalFilesShouldClose = true; + + } + + isRecording = false; + + + } else if (parameterIndex == 2) { + + if (isProcessing) { + + std::cout << "Toggling channel " << currentChannel << std::endl; + + if (newValue == 0.0f) { + continuousChannels[currentChannel].isRecording = false; + + if (isRecording) { + std::cout << "CLOSING FILE: " << continuousChannels[currentChannel].filename << std::endl; + fclose(continuousChannels[currentChannel].file); + } + + } + else { + continuousChannels[currentChannel].isRecording = true; + + if (isRecording) { + std::cout << "OPENING FILE: " << continuousChannels[currentChannel].filename << std::endl; + continuousChannels[currentChannel].file = + fopen(continuousChannels[currentChannel].filename.toUTF8(), "a+b"); + } + } + } + } +} + +void RecordNode::closeAllFiles() +{ + + for (int i = 0; i < continuousChannels.size(); i++) + { + if (continuousChannels[i].isRecording) + { + std::cout << "CLOSING FILE: " << continuousChannels[i].filename << std::endl; + fclose(continuousChannels[i].file); + } + } +} + +bool RecordNode::enable() +{ + isProcessing = true; + return true; +} + + +bool RecordNode::disable() +{ + // close files if necessary + setParameter(0, 10.0f); + + isProcessing = false; + + return true; +} + +float RecordNode::getFreeSpace() +{ + return 1.0f - float(dataDirectory.getBytesFreeOnVolume())/float(dataDirectory.getVolumeTotalSize()); +} + +void RecordNode::writeContinuousBuffer(float* data, int nSamples, int channel) +{ + + // scale the data appropriately -- currently just getting it into the right + // range; actually need to take into account the gain of each channel + for (int n = 0; n < nSamples; n++) + { + *(continuousDataFloatBuffer+n) = *(data+n) / 10000.0f; + } + + // find file and write samples to disk + + AudioDataConverters::convertFloatToInt16BE(continuousDataFloatBuffer, continuousDataIntegerBuffer, nSamples); + + //int16 samps = nSamples; + + fwrite(×tamp, // ptr + 8, // size of each element + 1, // count + continuousChannels[channel].file); // ptr to FILE object + + fwrite(&nSamples, // ptr + sizeof(nSamples), // size of each element + 1, // count + continuousChannels[channel].file); // ptr to FILE object + + int n = fwrite(continuousDataIntegerBuffer, // ptr + 2, // size of each element + nSamples, // count + continuousChannels[channel].file); // ptr to FILE object + // n must equal "count", otherwise there was an error +} + +void RecordNode::writeEventBuffer(MidiMessage& event, int node, int channel) +{ + // find file and write samples to disk + +} + +void RecordNode::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + + //std::cout << "Record node processing block." << std::endl; + //std::cout << "Num channels: " << buffer.getNumChannels() << std::endl; + + + if (isRecording) { + + timestamp = timer.getHighResolutionTicks(); + + // WHY IS THIS AFFECTING THE LFP DISPLAY? + //buffer.applyGain(0, nSamples, 5.2438f); + + // cycle through events -- extract the samples per channel + // NOT YET IMPLEMENTED + + // cycle through buffer channels + for (int i = 0; i < buffer.getNumChannels(); i++) + { + + if (continuousChannels[i].isRecording) + { + // write buffer to disk! + writeContinuousBuffer(buffer.getSampleData(i), + nSamples, + i); + + //std::cout << "Record channel " << i << std::endl; + } + + + } + + return; + + } + + // this is intended to prevent parameter changes from closing files + // before recording stops + if (signalFilesShouldClose) + { + closeAllFiles(); + signalFilesShouldClose = false; + } + +} -- GitLab