Skip to content
Snippets Groups Projects
Commit 49defa9d authored by shayo's avatar shayo
Browse files

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.
parent b4517beb
No related branches found
No related tags found
No related merge requests found
/*
------------------------------------------------------------------
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(&timestamp, // 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(&timestamp, // 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;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment