Skip to content

Commit 18798c0

Browse files
committed
Add support for full-depth compression and decompression of float and int32 timestreams
1 parent 74809df commit 18798c0

File tree

3 files changed

+121
-19
lines changed

3 files changed

+121
-19
lines changed

core/include/core/G3Timestream.h

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,21 @@ class G3Timestream : public G3FrameObject {
2727

2828
G3Timestream(const G3Timestream &r);
2929
G3Timestream(std::vector<double>::size_type s = 0, double val = 0) :
30-
units(None), use_flac_(0),
30+
units(None), use_flac_(0), flac_depth_(24),
3131
buffer_((s == 0) ? NULL : new std::vector<double>(s, val)),
3232
data_((s == 0) ? NULL : &(*buffer_)[0]), len_(s),
3333
data_type_(TS_DOUBLE) {}
3434
template <typename Iterator> G3Timestream(Iterator l, Iterator r) :
35-
units(None), use_flac_(0), buffer_(new std::vector<double>(l, r)),
35+
units(None), use_flac_(0), flac_depth_(24),
36+
buffer_(new std::vector<double>(l, r)),
3637
data_(&(*buffer_)[0]), len_(buffer_->size()), data_type_(TS_DOUBLE) {}
3738
virtual ~G3Timestream() {
3839
if (buffer_) delete buffer_;
3940
}
4041

4142
// FLAC compression levels range from 0-9. 0 means do not use FLAC.
4243
void SetFLACCompression(int compression_level);
44+
void SetFLACDepth(int bit_depth);
4345

4446
TimestreamUnits units;
4547
G3Time start, stop;
@@ -105,6 +107,7 @@ class G3Timestream : public G3FrameObject {
105107

106108
double GetSampleRate() const;
107109
uint8_t GetCompressionLevel() const{ return use_flac_; }
110+
uint8_t GetBitDepth() const { return flac_depth_; }
108111

109112
template <class A> void load(A &ar, unsigned v);
110113
template <class A> void save(A &ar, unsigned v) const;
@@ -135,17 +138,19 @@ class G3Timestream : public G3FrameObject {
135138
friend class G3TimestreamPythonHelpers;
136139

137140
uint8_t use_flac_;
141+
uint8_t flac_depth_;
138142

139143
std::vector<double> *buffer_;
140144
std::shared_ptr<void> root_data_ref_;
141145
void *data_;
142146
size_t len_;
143-
enum {
147+
enum DataType {
144148
TS_DOUBLE,
145149
TS_FLOAT,
146150
TS_INT32,
147151
TS_INT64
148-
} data_type_;
152+
};
153+
DataType data_type_;
149154

150155
template<typename T>
151156
struct TimeStreamTypeResolver{
@@ -208,7 +213,9 @@ class G3TimestreamMap : public G3FrameObject,
208213
void SetUnits(G3Timestream::TimestreamUnits units);
209214
/// FLAC compression levels range from 0-9. 0 means do not use FLAC.
210215
uint8_t GetCompressionLevel() const;
216+
uint8_t GetBitDepth() const;
211217
void SetFLACCompression(int compression_level);
218+
void SetFLACDepth(int bit_depth);
212219

213220
// Compact underlying data storage into a contiguous 2D block.
214221
// This invalidates any references to data inside any member
@@ -227,9 +234,10 @@ class G3TimestreamMap : public G3FrameObject,
227234
static G3TimestreamMap MakeCompact(const std::vector<std::string>& keys, std::size_t n_samples,
228235
G3Time start, G3Time stop,
229236
G3Timestream::TimestreamUnits units=G3Timestream::None,
230-
int compression_level=0){
237+
int compression_level=0, int bit_depth=24){
231238
std::shared_ptr<SampleType[]> data(new SampleType[n_samples*keys.size()]);
232-
return MakeCompact(keys, n_samples, data, start, stop, units, compression_level);
239+
return MakeCompact(keys, n_samples, data, start, stop, units,
240+
compression_level, bit_depth);
233241
}
234242

235243
/// Construct a map using an existing contiguous 2D block of data as the underlying storage.
@@ -246,16 +254,18 @@ class G3TimestreamMap : public G3FrameObject,
246254
std::shared_ptr<SampleType[]> data,
247255
G3Time start, G3Time stop,
248256
G3Timestream::TimestreamUnits units=G3Timestream::None,
249-
int compression_level=0){
257+
int compression_level=0, int bit_depth=24){
250258
G3TimestreamMap map;
251-
map.FromBuffer(keys, n_samples, data, start, stop, units, compression_level);
259+
map.FromBuffer(keys, n_samples, data, start, stop, units,
260+
compression_level, bit_depth);
252261
return map;
253262
}
254263

255264
template<typename SampleType>
256265
void FromBuffer(const std::vector<std::string>& keys, std::size_t n_samples,
257266
std::shared_ptr<SampleType[]> data, G3Time start, G3Time stop,
258-
G3Timestream::TimestreamUnits units=G3Timestream::None, int compression_level=0) {
267+
G3Timestream::TimestreamUnits units=G3Timestream::None, int compression_level=0,
268+
int bit_depth=24) {
259269
const auto data_type=G3Timestream::TimeStreamTypeResolver<SampleType>::type_tag;
260270
std::size_t offset = 0;
261271
for (const auto& key : keys) {
@@ -264,6 +274,7 @@ class G3TimestreamMap : public G3FrameObject,
264274
ts->stop = stop;
265275
ts->units = units;
266276
ts->use_flac_ = compression_level;
277+
ts->flac_depth_ = bit_depth;
267278
ts->root_data_ref_ = data;
268279
ts->data_ = data.get() + offset;
269280
ts->data_type_ = data_type;
@@ -284,7 +295,7 @@ namespace cereal {
284295
template <class A> struct specialize<A, G3TimestreamMap, cereal::specialization::member_serialize> {};
285296
}
286297

287-
G3_SERIALIZABLE(G3Timestream, 3);
298+
G3_SERIALIZABLE(G3Timestream, 4);
288299
G3_SERIALIZABLE(G3TimestreamMap, 4);
289300

290301
#endif

core/src/G3SuperTimestream.cxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,19 @@ template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
248248
FromBuffer(names, nsamp_filled, data, start, stop);
249249
buf = (char *)data.get();
250250
elsize = sizeof(int32_t);
251+
252+
SetFLACCompression(flac_level);
253+
SetFLACDepth(32);
251254
break;
252255
}
253256
case TYPE_NUM_FLOAT32: {
254257
std::shared_ptr<float[]> data(new float[size]);
255258
FromBuffer(names, nsamp_filled, data, start, stop);
256259
buf = (char *)data.get();
257260
elsize = sizeof(int32_t);
261+
262+
SetFLACCompression(flac_level);
263+
SetFLACDepth(32);
258264
break;
259265
}
260266
case TYPE_NUM_INT64: {

core/src/G3Timestream.cxx

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ extern "C"{
102102
}
103103
#endif
104104

105+
template <typename T>
106+
inline int32_t truncate(void *data, size_t i, int32_t bitmask, int32_t bitshift) {
107+
return ((int32_t(((T *)data)[i]) & bitmask) << bitshift) >> bitshift;
108+
}
109+
105110
template <class A> void G3Timestream::save(A &ar, unsigned v) const
106111
{
107112
ar & cereal::make_nvp("G3FrameObject",
@@ -122,30 +127,52 @@ template <class A> void G3Timestream::save(A &ar, unsigned v) const
122127
if (units != Counts && units != None)
123128
log_fatal("Cannot use FLAC on non-counts timestreams");
124129

130+
DataType data_type_out = TS_FLOAT;
131+
int32_t bitmask, bitshift;
132+
switch (flac_depth_) {
133+
case 24:
134+
bitmask = 0x00ffffff;
135+
bitshift = 8;
136+
break;
137+
case 32:
138+
bitmask = 0xffffffff;
139+
bitshift = 0;
140+
if (data_type_ == TS_INT64 || data_type_ == TS_INT32)
141+
data_type_out = TS_INT32;
142+
break;
143+
default:
144+
log_fatal("Invalid FLAC bit depth %d", (int) flac_depth_);
145+
}
146+
147+
ar & cereal::make_nvp("flac_depth", flac_depth_);
148+
ar & cereal::make_nvp("data_type", data_type_out);
149+
125150
// Copy to 24-bit integers
126151
inbuf.resize(size());
127152
switch (data_type_) {
128153
case TS_DOUBLE:
129154
for (size_t i = 0; i < size(); i++)
130-
inbuf[i] = ((int32_t(((double *)data_)[i]) & 0x00ffffff) << 8) >> 8;
155+
inbuf[i] = truncate<double>(data_, i, bitmask, bitshift);
131156
break;
132157
case TS_FLOAT:
133158
for (size_t i = 0; i < size(); i++)
134-
inbuf[i] = ((int32_t(((float *)data_)[i]) & 0x00ffffff) << 8) >> 8;
159+
inbuf[i] = truncate<float>(data_, i, bitmask, bitshift);
135160
break;
136161
case TS_INT32:
137-
{
162+
if (flac_depth_ == 24) {
138163
// Using this rather raw form for the loop can enable automatic
139164
// unrolling and vectorization.
140165
int32_t* in_ptr=(int32_t *)data_;
141166
int32_t* out_ptr=&inbuf[0];
142167
for(int32_t* end=in_ptr+size(); in_ptr!=end; in_ptr++,out_ptr++)
143168
*out_ptr = ((*in_ptr & 0x00ffffff) << 8) >> 8;
169+
} else {
170+
memcpy(inbuf.data(), data_, size() * sizeof(int32_t));
144171
}
145172
break;
146173
case TS_INT64:
147174
for (size_t i = 0; i < size(); i++)
148-
inbuf[i] = ((int32_t(((int64_t *)data_)[i]) & 0x00ffffff) << 8) >> 8;
175+
inbuf[i] = truncate<int64_t>(data_, i, bitmask, bitshift);
149176
break;
150177
default:
151178
log_fatal("Unknown timestream datatype %d", data_type_);
@@ -182,7 +209,7 @@ template <class A> void G3Timestream::save(A &ar, unsigned v) const
182209
FLAC__StreamEncoder *encoder = FLAC__stream_encoder_new();
183210
FLAC__stream_encoder_set_channels(encoder, 1);
184211
// XXX: should assert if high-order 8 bits are not clear
185-
FLAC__stream_encoder_set_bits_per_sample(encoder, 24);
212+
FLAC__stream_encoder_set_bits_per_sample(encoder, flac_depth_);
186213
FLAC__stream_encoder_set_compression_level(encoder, use_flac_);
187214
FLAC__stream_encoder_set_do_md5(encoder, false);
188215
FLAC__stream_encoder_init_stream(encoder,
@@ -262,6 +289,13 @@ template <class A> void G3Timestream::load(A &ar, unsigned v)
262289
if (units != Counts && units != None)
263290
log_fatal("Cannot use FLAC on non-counts timestreams");
264291

292+
if (v >= 4) {
293+
ar & cereal::make_nvp("flac_depth", flac_depth_);
294+
ar & cereal::make_nvp("data_type", data_type_);
295+
} else {
296+
data_type_ = TS_FLOAT;
297+
}
298+
265299
ar & cereal::make_nvp("nanflag", nanflag);
266300
if (nanflag == SomeNan)
267301
ar & cereal::make_nvp("nanmask", nanbuf);
@@ -281,14 +315,22 @@ template <class A> void G3Timestream::load(A &ar, unsigned v)
281315
FLAC__stream_decoder_finish(decoder);
282316
FLAC__stream_decoder_delete(decoder);
283317

318+
// Short-circuit for full-depth INT32.
319+
if (data_type_ == TS_INT32 && flac_depth_ == 32) {
320+
root_data_ref_ = std::shared_ptr<std::vector<int32_t> >(
321+
callback.outbuf);
322+
len_ = callback.outbuf->size();
323+
data_ = &(*callback.outbuf)[0];
324+
return;
325+
}
326+
284327
// Represent data as floats internally. These have the same
285328
// significand depth (24 bits) as the max. bit depth of the
286329
// reference FLAC encoder we use, so no data are lost, and
287330
// allow NaNs, unlike int32_ts, which we try to pull through
288331
// the process to signal missing data.
289332
float *data = new float[callback.outbuf->size()];
290333
root_data_ref_ = std::shared_ptr<float[]>(data);
291-
data_type_ = TS_FLOAT;
292334
len_ = callback.outbuf->size();
293335
data_ = data;
294336

@@ -361,7 +403,7 @@ template <class A> void G3Timestream::load(A &ar, unsigned v)
361403

362404
G3Timestream::G3Timestream(const G3Timestream &r) :
363405
units(r.units), start(r.start), stop(r.stop), use_flac_(r.use_flac_),
364-
len_(r.len_), data_type_(r.data_type_)
406+
flac_depth_(r.flac_depth_), len_(r.len_), data_type_(r.data_type_)
365407
{
366408
// Copy constructor needs to copy data, which always involves
367409
// allocating the internal buffer.
@@ -431,6 +473,14 @@ void G3Timestream::SetFLACCompression(int use_flac)
431473
#endif
432474
}
433475

476+
void G3Timestream::SetFLACDepth(int bit_depth)
477+
{
478+
if (bit_depth != 24 && bit_depth != 32)
479+
log_fatal("Invalid flac bit depth %d", bit_depth);
480+
481+
flac_depth_ = bit_depth;
482+
}
483+
434484
G3Timestream G3Timestream::operator +(const G3Timestream &r) const
435485
{
436486
G3Timestream ret(*this);
@@ -764,12 +814,32 @@ uint8_t G3TimestreamMap::GetCompressionLevel() const
764814
return begin()->second->use_flac_;
765815
}
766816

817+
uint8_t G3TimestreamMap::GetBitDepth() const
818+
{
819+
if (begin() == end())
820+
return 0;
821+
822+
return begin()->second->flac_depth_;
823+
}
824+
767825
void G3TimestreamMap::SetFLACCompression(int compression_level)
768826
{
827+
// Check for errors
828+
begin()->second->SetFLACCompression(compression_level);
829+
769830
for (auto& ts : *this)
770831
ts.second->use_flac_ = compression_level;
771832
}
772833

834+
void G3TimestreamMap::SetFLACDepth(int bit_depth)
835+
{
836+
// Check for errors
837+
begin()->second->SetFLACDepth(bit_depth);
838+
839+
for (auto& ts : *this)
840+
ts.second->flac_depth_ = bit_depth;
841+
}
842+
773843
void G3TimestreamMap::Compactify()
774844
{
775845
// Check if already compacted
@@ -1069,7 +1139,8 @@ struct PyBufferOwner {
10691139
static G3TimestreamMapPtr
10701140
G3TimestreamMap_from_numpy(std::vector<std::string> keys,
10711141
boost::python::object data, G3Time start, G3Time stop,
1072-
G3Timestream::TimestreamUnits units, int compression_level, bool copy_data)
1142+
G3Timestream::TimestreamUnits units, int compression_level,
1143+
bool copy_data, int bit_depth)
10731144
{
10741145
G3TimestreamMapPtr x(new G3TimestreamMap);
10751146

@@ -1116,6 +1187,7 @@ G3TimestreamMap_from_numpy(std::vector<std::string> keys,
11161187
templ.start = start;
11171188
templ.stop = stop;
11181189
templ.SetFLACCompression(compression_level);
1190+
templ.SetFLACDepth(bit_depth);
11191191
if (strcmp(v->v.format, "d") == 0) {
11201192
templ.data_type_ = G3Timestream::TS_DOUBLE;
11211193
} else if (strcmp(v->v.format, "f") == 0) {
@@ -1347,6 +1419,10 @@ PYBINDINGS("core") {
13471419
"Pass True to turn on FLAC compression when serialized. "
13481420
"FLAC compression only works if the timestream is in units of "
13491421
"counts.")
1422+
.def("SetFLACDepth", &G3Timestream::SetFLACDepth,
1423+
"Change the bit depth for FLAC compression. "
1424+
"FLAC compression only works if the timestream is in units of "
1425+
"counts.")
13501426
.def_readwrite("units", &G3Timestream::units,
13511427
"Units of the data in the timestream, stored as one of the "
13521428
"members of core.G3TimestreamUnits.")
@@ -1361,6 +1437,8 @@ PYBINDINGS("core") {
13611437
.add_property("compression_level", &G3Timestream::GetCompressionLevel,
13621438
&G3Timestream::SetFLACCompression, "Level of FLAC compression used for this timestream. "
13631439
"This can only be non-zero if the timestream is in units of counts.")
1440+
.add_property("bit_depth", &G3Timestream::GetBitDepth,
1441+
&G3Timestream::SetFLACDepth, "Bit depth of FLAC compression used for this timestream.")
13641442
.def("_assert_congruence", &G3Timestream::G3TimestreamPythonHelpers::G3Timestream_assert_congruence,
13651443
"log_fatal() if units, length, start, or stop times do not match")
13661444
.def("_cxxslice", &G3Timestream::G3TimestreamPythonHelpers::G3Timestream_getslice, "Slice-only __getitem__")
@@ -1382,7 +1460,7 @@ PYBINDINGS("core") {
13821460
bp::default_call_policies(),
13831461
(bp::arg("keys"), bp::arg("data"), bp::arg("start")=G3Time(0),
13841462
bp::arg("stop")=G3Time(0), bp::arg("units") = G3Timestream::TimestreamUnits::None,
1385-
bp::arg("compression_level") = 0, bp::arg("copy_data") = true)),
1463+
bp::arg("compression_level") = 0, bp::arg("copy_data") = true, bp::arg("bit_depth") = 24)),
13861464
"Create a timestream map from a numpy array or other numeric python iterable. "
13871465
"Each row of the 2D input array will correspond to a single timestream, with "
13881466
"the key set to the correspondingly-indexed entry of <keys>. If <copy_data> "
@@ -1398,6 +1476,10 @@ PYBINDINGS("core") {
13981476
"Pass True to turn on FLAC compression when serialized. "
13991477
"FLAC compression only works if the timestreams are in units of "
14001478
"counts.")
1479+
.def("SetFLACDepth", &G3TimestreamMap::SetFLACDepth,
1480+
"Change the bit depth for FLAC compression. "
1481+
"FLAC compression only works if the timestreams are in units of "
1482+
"counts.")
14011483
.add_property("start", &G3TimestreamMap::GetStartTime,
14021484
&G3TimestreamMap::SetStartTime,
14031485
"Time of the first sample in the time stream")
@@ -1417,6 +1499,9 @@ PYBINDINGS("core") {
14171499
&G3TimestreamMap::SetFLACCompression,
14181500
"Level of FLAC compression used for this timestream map. "
14191501
"This can only be non-zero if the timestream is in units of counts.")
1502+
.add_property("bit_depth", &G3TimestreamMap::GetBitDepth,
1503+
&G3TimestreamMap::SetFLACDepth,
1504+
"Bit depth of FLAC compression used for this timestream map.")
14201505
;
14211506
register_pointer_conversions<G3TimestreamMap>();
14221507

0 commit comments

Comments
 (0)