@@ -122,9 +122,20 @@ template <class A> void G3Timestream::save(A &ar, unsigned v) const
122122 if (units != Counts && units != None)
123123 log_fatal (" Cannot use FLAC on non-counts timestreams" );
124124
125+ DataType data_type_out = TS_FLOAT;
126+ if (flac_depth_ == 32 ) {
127+ if (data_type_ == TS_INT64 || data_type_ == TS_INT32)
128+ data_type_out = TS_INT32;
129+ }
130+
131+ ar & cereal::make_nvp (" flac_depth" , flac_depth_);
132+ ar & cereal::make_nvp (" data_type" , data_type_out);
133+
125134 // Copy to 24-bit integers
126135 inbuf.resize (size ());
127- switch (data_type_) {
136+ switch (flac_depth_) {
137+ case 24 :
138+ switch (data_type_) {
128139 case TS_DOUBLE:
129140 for (size_t i = 0 ; i < size (); i++)
130141 inbuf[i] = ((int32_t (((double *)data_)[i]) & 0x00ffffff ) << 8 ) >> 8 ;
@@ -149,6 +160,31 @@ template <class A> void G3Timestream::save(A &ar, unsigned v) const
149160 break ;
150161 default :
151162 log_fatal (" Unknown timestream datatype %d" , data_type_);
163+ }
164+ break ;
165+ case 32 :
166+ switch (data_type_) {
167+ case TS_DOUBLE:
168+ for (size_t i = 0 ; i < size (); i++)
169+ inbuf[i] = int32_t (float (((double *)data_)[i]));
170+ break ;
171+ case TS_FLOAT:
172+ for (size_t i = 0 ; i < size (); i++)
173+ inbuf[i] = int32_t (((float *)data_)[i]);
174+ break ;
175+ case TS_INT32:
176+ memcpy (inbuf.data (), data_, size () * sizeof (int32_t ));
177+ break ;
178+ case TS_INT64:
179+ for (size_t i = 0 ; i < size (); i++)
180+ inbuf[i] = int32_t (((int64_t *)data_)[i]);
181+ break ;
182+ default :
183+ log_fatal (" Unknown timestream datatype %d" , data_type_);
184+ }
185+ break ;
186+ default :
187+ log_fatal (" Invalid FLAC bit depth %d" , flac_depth_);
152188 }
153189 chanmap[0 ] = &inbuf[0 ];
154190
@@ -182,7 +218,7 @@ template <class A> void G3Timestream::save(A &ar, unsigned v) const
182218 FLAC__StreamEncoder *encoder = FLAC__stream_encoder_new ();
183219 FLAC__stream_encoder_set_channels (encoder, 1 );
184220 // XXX: should assert if high-order 8 bits are not clear
185- FLAC__stream_encoder_set_bits_per_sample (encoder, 24 );
221+ FLAC__stream_encoder_set_bits_per_sample (encoder, flac_depth_ );
186222 FLAC__stream_encoder_set_compression_level (encoder, use_flac_);
187223 FLAC__stream_encoder_set_do_md5 (encoder, false );
188224 FLAC__stream_encoder_init_stream (encoder,
@@ -262,6 +298,13 @@ template <class A> void G3Timestream::load(A &ar, unsigned v)
262298 if (units != Counts && units != None)
263299 log_fatal (" Cannot use FLAC on non-counts timestreams" );
264300
301+ if (v >= 4 ) {
302+ ar & cereal::make_nvp (" flac_depth" , flac_depth_);
303+ ar & cereal::make_nvp (" data_type" , data_type_);
304+ } else {
305+ data_type_ = TS_FLOAT;
306+ }
307+
265308 ar & cereal::make_nvp (" nanflag" , nanflag);
266309 if (nanflag == SomeNan)
267310 ar & cereal::make_nvp (" nanmask" , nanbuf);
@@ -281,14 +324,22 @@ template <class A> void G3Timestream::load(A &ar, unsigned v)
281324 FLAC__stream_decoder_finish (decoder);
282325 FLAC__stream_decoder_delete (decoder);
283326
327+ // Short-circuit for full-depth INT32.
328+ if (data_type_ == TS_INT32 && flac_depth_ == 32 ) {
329+ root_data_ref_ = std::shared_ptr<std::vector<int32_t > >(
330+ callback.outbuf );
331+ len_ = callback.outbuf ->size ();
332+ data_ = &(*callback.outbuf )[0 ];
333+ return ;
334+ }
335+
284336 // Represent data as floats internally. These have the same
285337 // significand depth (24 bits) as the max. bit depth of the
286338 // reference FLAC encoder we use, so no data are lost, and
287339 // allow NaNs, unlike int32_ts, which we try to pull through
288340 // the process to signal missing data.
289341 float *data = new float [callback.outbuf ->size ()];
290342 root_data_ref_ = std::shared_ptr<float []>(data);
291- data_type_ = TS_FLOAT;
292343 len_ = callback.outbuf ->size ();
293344 data_ = data;
294345
@@ -361,7 +412,7 @@ template <class A> void G3Timestream::load(A &ar, unsigned v)
361412
362413G3Timestream::G3Timestream (const G3Timestream &r) :
363414 units (r.units ), start (r.start ), stop (r.stop ), use_flac_ (r.use_flac_ ),
364- len_ (r.len_ ), data_type_ (r.data_type_ )
415+ flac_depth_ (r. flac_depth_ ), len_ (r.len_ ), data_type_ (r.data_type_ )
365416{
366417 // Copy constructor needs to copy data, which always involves
367418 // allocating the internal buffer.
@@ -431,6 +482,14 @@ void G3Timestream::SetFLACCompression(int use_flac)
431482#endif
432483}
433484
485+ void G3Timestream::SetFLACDepth (int bit_depth)
486+ {
487+ if (bit_depth != 24 && bit_depth != 32 )
488+ log_fatal (" Invalid flac bit depth %d" , bit_depth);
489+
490+ flac_depth_ = bit_depth;
491+ }
492+
434493G3Timestream G3Timestream::operator +(const G3Timestream &r) const
435494{
436495 G3Timestream ret (*this );
@@ -760,12 +819,32 @@ uint8_t G3TimestreamMap::GetCompressionLevel() const
760819 return begin ()->second ->use_flac_ ;
761820}
762821
822+ uint8_t G3TimestreamMap::GetBitDepth () const
823+ {
824+ if (begin () == end ())
825+ return 0 ;
826+
827+ return begin ()->second ->flac_depth_ ;
828+ }
829+
763830void G3TimestreamMap::SetFLACCompression (int compression_level)
764831{
832+ // Check for errors
833+ begin ()->second ->SetFLACCompression (compression_level);
834+
765835 for (auto & ts : *this )
766836 ts.second ->use_flac_ = compression_level;
767837}
768838
839+ void G3TimestreamMap::SetFLACDepth (int bit_depth)
840+ {
841+ // Check for errors
842+ begin ()->second ->SetFLACDepth (bit_depth);
843+
844+ for (auto & ts : *this )
845+ ts.second ->flac_depth_ = bit_depth;
846+ }
847+
769848void G3TimestreamMap::Compactify ()
770849{
771850 // Check if already compacted
@@ -1065,7 +1144,8 @@ struct PyBufferOwner {
10651144static G3TimestreamMapPtr
10661145G3TimestreamMap_from_numpy (std::vector<std::string> keys,
10671146 boost::python::object data, G3Time start, G3Time stop,
1068- G3Timestream::TimestreamUnits units, int compression_level, bool copy_data)
1147+ G3Timestream::TimestreamUnits units, int compression_level,
1148+ bool copy_data, int bit_depth)
10691149{
10701150 G3TimestreamMapPtr x (new G3TimestreamMap);
10711151
@@ -1112,6 +1192,7 @@ G3TimestreamMap_from_numpy(std::vector<std::string> keys,
11121192 templ.start = start;
11131193 templ.stop = stop;
11141194 templ.SetFLACCompression (compression_level);
1195+ templ.SetFLACDepth (bit_depth);
11151196 if (strcmp (v->v .format , " d" ) == 0 ) {
11161197 templ.data_type_ = G3Timestream::TS_DOUBLE;
11171198 } else if (strcmp (v->v .format , " f" ) == 0 ) {
@@ -1343,6 +1424,10 @@ PYBINDINGS("core") {
13431424 " Pass True to turn on FLAC compression when serialized. "
13441425 " FLAC compression only works if the timestream is in units of "
13451426 " counts." )
1427+ .def (" SetFLACDepth" , &G3Timestream::SetFLACDepth,
1428+ " Change the bit depth for FLAC compression. "
1429+ " FLAC compression only works if the timestream is in units of "
1430+ " counts." )
13461431 .def_readwrite (" units" , &G3Timestream::units,
13471432 " Units of the data in the timestream, stored as one of the "
13481433 " members of core.G3TimestreamUnits." )
@@ -1357,6 +1442,8 @@ PYBINDINGS("core") {
13571442 .add_property (" compression_level" , &G3Timestream::GetCompressionLevel,
13581443 &G3Timestream::SetFLACCompression, " Level of FLAC compression used for this timestream. "
13591444 " This can only be non-zero if the timestream is in units of counts." )
1445+ .add_property (" bit_depth" , &G3Timestream::GetBitDepth,
1446+ &G3Timestream::SetFLACDepth, " Bit depth of FLAC compression used for this timestream." )
13601447 .def (" _assert_congruence" , &G3Timestream::G3TimestreamPythonHelpers::G3Timestream_assert_congruence,
13611448 " log_fatal() if units, length, start, or stop times do not match" )
13621449 .def (" _cxxslice" , &G3Timestream::G3TimestreamPythonHelpers::G3Timestream_getslice, " Slice-only __getitem__" )
@@ -1378,7 +1465,7 @@ PYBINDINGS("core") {
13781465 bp::default_call_policies (),
13791466 (bp::arg (" keys" ), bp::arg (" data" ), bp::arg (" start" )=G3Time (0 ),
13801467 bp::arg (" stop" )=G3Time (0 ), bp::arg (" units" ) = G3Timestream::TimestreamUnits::None,
1381- bp::arg (" compression_level" ) = 0 , bp::arg (" copy_data" ) = true )),
1468+ bp::arg (" compression_level" ) = 0 , bp::arg (" copy_data" ) = true , bp::arg ( " bit_depth " ) = 24 )),
13821469 " Create a timestream map from a numpy array or other numeric python iterable. "
13831470 " Each row of the 2D input array will correspond to a single timestream, with "
13841471 " the key set to the correspondingly-indexed entry of <keys>. If <copy_data> "
@@ -1394,6 +1481,10 @@ PYBINDINGS("core") {
13941481 " Pass True to turn on FLAC compression when serialized. "
13951482 " FLAC compression only works if the timestreams are in units of "
13961483 " counts." )
1484+ .def (" SetFLACDepth" , &G3TimestreamMap::SetFLACDepth,
1485+ " Change the bit depth for FLAC compression. "
1486+ " FLAC compression only works if the timestreams are in units of "
1487+ " counts." )
13971488 .add_property (" start" , &G3TimestreamMap::GetStartTime,
13981489 &G3TimestreamMap::SetStartTime,
13991490 " Time of the first sample in the time stream" )
@@ -1413,6 +1504,9 @@ PYBINDINGS("core") {
14131504 &G3TimestreamMap::SetFLACCompression,
14141505 " Level of FLAC compression used for this timestream map. "
14151506 " This can only be non-zero if the timestream is in units of counts." )
1507+ .add_property (" bit_depth" , &G3TimestreamMap::GetBitDepth,
1508+ &G3TimestreamMap::SetFLACDepth,
1509+ " Bit depth of FLAC compression used for this timestream map." )
14161510 ;
14171511 register_pointer_conversions<G3TimestreamMap>();
14181512
0 commit comments