diff --git a/Source/Plugins/BinaryWriter/NpyFile.cpp b/Source/Plugins/BinaryWriter/NpyFile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f557db787566601c1ed773dacce9be24f67544cb --- /dev/null +++ b/Source/Plugins/BinaryWriter/NpyFile.cpp @@ -0,0 +1,145 @@ +/* +------------------------------------------------------------------ + +This file is part of the Open Ephys GUI +Copyright (C) 2017 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 "NpyFile.h" + +using namespace BinaryRecordingEngine; + +NpyFile::NpyFile(String path, const Array<NpyType>& typeList) +{ + File file(path); + Result res = file.create(); + if (res.failed()) + { + std::cerr << "Error creating file " << path << ":" << res.getErrorMessage() << std::endl; + return; + } + m_file = file.createOutputStream(); + if (!m_file) + return; + + m_okOpen = true; + + String header = "{'descr': ["; + + int nTypes = typeList.size(); + String name; + + for (int i = 0; i < nTypes; i++) + { + NpyType& type = typeList.getReference(i); + if (i > 0) header += ", "; + header += "('" + type.getName() + "', '" + type.getTypeString() + "', (" + String(type.getTypeLength()) + ",))"; + } + header += "], 'fortran_order': False, 'shape': (1,), }"; + + int headLength = header.length(); + int padding = (int((headLength + 30 ) / 16) + 1) * 16; + header = header.paddedRight(' ', padding); + header += '\n'; + + uint8 magicNum = 0x093; + m_file->write(&magicNum, sizeof(uint8)); + String magic = "NUMPY"; + uint16 len = header.length(); + m_file->write(magic.toUTF8(), magic.getNumBytesAsUTF8()); + uint16 ver = 0x0001; + m_file->write(&ver, sizeof(uint16)); + m_file->write(&len, sizeof(uint16)); + m_file->write(header.toUTF8(), len); + m_countPos = headLength + 4; //10-6 + +} + +NpyFile::~NpyFile() +{ + if (m_file->setPosition(m_countPos)) + { + String newShape = String(m_recordCount) + ",), }"; + m_file->write(newShape.toUTF8(), newShape.getNumBytesAsUTF8()); + } + else + { + std::cerr << "Error. Unable to seek to update header on file " << m_file->getFile().getFullPathName() << std::endl; + } +} + +void NpyFile::writeData(const void* data, size_t size) +{ + m_file->write(data, size); +} + +void NpyFile::increaseRecordCount(int count) +{ + m_recordCount += count; +} + + +NpyType::NpyType(String n, BaseType t, size_t l) + : name(n), type(t), length(l) +{ +} + +String NpyType::getTypeString() const +{ + switch (type) + { + case BaseType::CHAR: + return "U" + String(length); + case BaseType::INT8: + return "<i1"; + case BaseType::UINT8: + return "<u1"; + case BaseType::INT16: + return "<i2"; + case BaseType::UINT16: + return "<u2"; + case BaseType::INT32: + return "<i4"; + case BaseType::UINT32: + return "<u4"; + case BaseType::INT64: + return "<i8"; + case BaseType::UINT64: + return "<u8"; + case BaseType::FLOAT: + return "<f4"; + case BaseType::DOUBLE: + return "<f8"; + default: + return "<b1"; + } +} + +int NpyType::getTypeLength() const +{ + if (type == BaseType::CHAR) + return 1; + else + return length; +} + +String NpyType::getName() const +{ + return name; +} \ No newline at end of file diff --git a/Source/Plugins/BinaryWriter/NpyFile.h b/Source/Plugins/BinaryWriter/NpyFile.h new file mode 100644 index 0000000000000000000000000000000000000000..a3bcda482fb66640e8ed951ab52d60db2a5cda71 --- /dev/null +++ b/Source/Plugins/BinaryWriter/NpyFile.h @@ -0,0 +1,60 @@ +/* +------------------------------------------------------------------ + +This file is part of the Open Ephys GUI +Copyright (C) 2017 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 NPYFILE_H +#define NPYFILE_H + +#include <RecordingLib.h> + +namespace BinaryRecordingEngine +{ + + class NpyType + { + public: + NpyType(String, BaseType, size_t); + String getName() const; + String getTypeString() const; + int getTypeLength() const; + private: + String name; + BaseType type; + size_t length; + }; + + class NpyFile + { + public: + NpyFile(String path, const Array<NpyType>& typeList); + ~NpyFile(); + void writeData(const void* data, size_t size); + void increaseRecordCount(int count = 1); + private: + ScopedPointer<FileOutputStream> m_file; + bool m_okOpen{ false }; + int64 m_recordCount{ 0 }; + size_t m_countPos; + }; + +}; +#endif \ No newline at end of file