1+ /*
2+ ------------------------------------------------------------------
3+
4+ This file is part of the Open Ephys GUI
5+ Copyright (C) 2017 Open Ephys
6+
7+ ------------------------------------------------------------------
8+
9+ This program is free software: you can redistribute it and/or modify
10+ it under the terms of the GNU General Public License as published by
11+ the Free Software Foundation, either version 3 of the License, or
12+ (at your option) any later version.
13+
14+ This program is distributed in the hope that it will be useful,
15+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+ GNU General Public License for more details.
18+
19+ You should have received a copy of the GNU General Public License
20+ along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
22+ */
23+
24+ #include " NpyFile.h"
25+
26+ using namespace BinaryRecordingEngine ;
27+
28+ NpyFile::NpyFile (String path, const Array<NpyType>& typeList)
29+ {
30+ File file (path);
31+ Result res = file.create ();
32+ if (res.failed ())
33+ {
34+ std::cerr << " Error creating file " << path << " :" << res.getErrorMessage () << std::endl;
35+ return ;
36+ }
37+ m_file = file.createOutputStream ();
38+ if (!m_file)
39+ return ;
40+
41+ m_okOpen = true ;
42+
43+ String header = " {'descr': [" ;
44+
45+ int nTypes = typeList.size ();
46+ String name;
47+
48+ for (int i = 0 ; i < nTypes; i++)
49+ {
50+ NpyType& type = typeList.getReference (i);
51+ if (i > 0 ) header += " , " ;
52+ header += " ('" + type.getName () + " ', '" + type.getTypeString () + " ', (" + String (type.getTypeLength ()) + " ,))" ;
53+ }
54+ header += " ], 'fortran_order': False, 'shape': (1,), }" ;
55+
56+ int headLength = header.length ();
57+ int padding = (int ((headLength + 30 ) / 16 ) + 1 ) * 16 ;
58+ header = header.paddedRight (' ' , padding);
59+ header += ' \n ' ;
60+
61+ uint8 magicNum = 0x093 ;
62+ m_file->write (&magicNum, sizeof (uint8));
63+ String magic = " NUMPY" ;
64+ uint16 len = header.length ();
65+ m_file->write (magic.toUTF8 (), magic.getNumBytesAsUTF8 ());
66+ uint16 ver = 0x0001 ;
67+ m_file->write (&ver, sizeof (uint16));
68+ m_file->write (&len, sizeof (uint16));
69+ m_file->write (header.toUTF8 (), len);
70+ m_countPos = headLength + 4 ; // 10-6
71+
72+ }
73+
74+ NpyFile::~NpyFile ()
75+ {
76+ if (m_file->setPosition (m_countPos))
77+ {
78+ String newShape = String (m_recordCount) + " ,), }" ;
79+ m_file->write (newShape.toUTF8 (), newShape.getNumBytesAsUTF8 ());
80+ }
81+ else
82+ {
83+ std::cerr << " Error. Unable to seek to update header on file " << m_file->getFile ().getFullPathName () << std::endl;
84+ }
85+ }
86+
87+ void NpyFile::writeData (const void * data, size_t size)
88+ {
89+ m_file->write (data, size);
90+ }
91+
92+ void NpyFile::increaseRecordCount (int count)
93+ {
94+ m_recordCount += count;
95+ }
96+
97+
98+ NpyType::NpyType (String n, BaseType t, size_t l)
99+ : name(n), type(t), length(l)
100+ {
101+ }
102+
103+ String NpyType::getTypeString () const
104+ {
105+ switch (type)
106+ {
107+ case BaseType::CHAR:
108+ return " U" + String (length);
109+ case BaseType::INT8:
110+ return " <i1" ;
111+ case BaseType::UINT8:
112+ return " <u1" ;
113+ case BaseType::INT16:
114+ return " <i2" ;
115+ case BaseType::UINT16:
116+ return " <u2" ;
117+ case BaseType::INT32:
118+ return " <i4" ;
119+ case BaseType::UINT32:
120+ return " <u4" ;
121+ case BaseType::INT64:
122+ return " <i8" ;
123+ case BaseType::UINT64:
124+ return " <u8" ;
125+ case BaseType::FLOAT:
126+ return " <f4" ;
127+ case BaseType::DOUBLE:
128+ return " <f8" ;
129+ default :
130+ return " <b1" ;
131+ }
132+ }
133+
134+ int NpyType::getTypeLength () const
135+ {
136+ if (type == BaseType::CHAR)
137+ return 1 ;
138+ else
139+ return length;
140+ }
141+
142+ String NpyType::getName () const
143+ {
144+ return name;
145+ }
0 commit comments