@@ -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 );
@@ -764,12 +823,32 @@ uint8_t G3TimestreamMap::GetCompressionLevel() const
764823 return begin ()->second ->use_flac_ ;
765824}
766825
826+ uint8_t G3TimestreamMap::GetBitDepth () const
827+ {
828+ if (begin () == end ())
829+ return 0 ;
830+
831+ return begin ()->second ->flac_depth_ ;
832+ }
833+
767834void G3TimestreamMap::SetFLACCompression (int compression_level)
768835{
836+ // Check for errors
837+ begin ()->second ->SetFLACCompression (compression_level);
838+
769839 for (auto & ts : *this )
770840 ts.second ->use_flac_ = compression_level;
771841}
772842
843+ void G3TimestreamMap::SetFLACDepth (int bit_depth)
844+ {
845+ // Check for errors
846+ begin ()->second ->SetFLACDepth (bit_depth);
847+
848+ for (auto & ts : *this )
849+ ts.second ->flac_depth_ = bit_depth;
850+ }
851+
773852void G3TimestreamMap::Compactify ()
774853{
775854 // Check if already compacted
@@ -1069,7 +1148,8 @@ struct PyBufferOwner {
10691148static G3TimestreamMapPtr
10701149G3TimestreamMap_from_numpy (std::vector<std::string> keys,
10711150 boost::python::object data, G3Time start, G3Time stop,
1072- G3Timestream::TimestreamUnits units, int compression_level, bool copy_data)
1151+ G3Timestream::TimestreamUnits units, int compression_level,
1152+ bool copy_data, int bit_depth)
10731153{
10741154 G3TimestreamMapPtr x (new G3TimestreamMap);
10751155
@@ -1116,6 +1196,7 @@ G3TimestreamMap_from_numpy(std::vector<std::string> keys,
11161196 templ.start = start;
11171197 templ.stop = stop;
11181198 templ.SetFLACCompression (compression_level);
1199+ templ.SetFLACDepth (bit_depth);
11191200 if (strcmp (v->v .format , " d" ) == 0 ) {
11201201 templ.data_type_ = G3Timestream::TS_DOUBLE;
11211202 } else if (strcmp (v->v .format , " f" ) == 0 ) {
@@ -1347,6 +1428,10 @@ PYBINDINGS("core") {
13471428 " Pass True to turn on FLAC compression when serialized. "
13481429 " FLAC compression only works if the timestream is in units of "
13491430 " counts." )
1431+ .def (" SetFLACDepth" , &G3Timestream::SetFLACDepth,
1432+ " Change the bit depth for FLAC compression. "
1433+ " FLAC compression only works if the timestream is in units of "
1434+ " counts." )
13501435 .def_readwrite (" units" , &G3Timestream::units,
13511436 " Units of the data in the timestream, stored as one of the "
13521437 " members of core.G3TimestreamUnits." )
@@ -1361,6 +1446,8 @@ PYBINDINGS("core") {
13611446 .add_property (" compression_level" , &G3Timestream::GetCompressionLevel,
13621447 &G3Timestream::SetFLACCompression, " Level of FLAC compression used for this timestream. "
13631448 " This can only be non-zero if the timestream is in units of counts." )
1449+ .add_property (" bit_depth" , &G3Timestream::GetBitDepth,
1450+ &G3Timestream::SetFLACDepth, " Bit depth of FLAC compression used for this timestream." )
13641451 .def (" _assert_congruence" , &G3Timestream::G3TimestreamPythonHelpers::G3Timestream_assert_congruence,
13651452 " log_fatal() if units, length, start, or stop times do not match" )
13661453 .def (" _cxxslice" , &G3Timestream::G3TimestreamPythonHelpers::G3Timestream_getslice, " Slice-only __getitem__" )
@@ -1382,7 +1469,7 @@ PYBINDINGS("core") {
13821469 bp::default_call_policies (),
13831470 (bp::arg (" keys" ), bp::arg (" data" ), bp::arg (" start" )=G3Time (0 ),
13841471 bp::arg (" stop" )=G3Time (0 ), bp::arg (" units" ) = G3Timestream::TimestreamUnits::None,
1385- bp::arg (" compression_level" ) = 0 , bp::arg (" copy_data" ) = true )),
1472+ bp::arg (" compression_level" ) = 0 , bp::arg (" copy_data" ) = true , bp::arg ( " bit_depth " ) = 24 )),
13861473 " Create a timestream map from a numpy array or other numeric python iterable. "
13871474 " Each row of the 2D input array will correspond to a single timestream, with "
13881475 " the key set to the correspondingly-indexed entry of <keys>. If <copy_data> "
@@ -1398,6 +1485,10 @@ PYBINDINGS("core") {
13981485 " Pass True to turn on FLAC compression when serialized. "
13991486 " FLAC compression only works if the timestreams are in units of "
14001487 " counts." )
1488+ .def (" SetFLACDepth" , &G3TimestreamMap::SetFLACDepth,
1489+ " Change the bit depth for FLAC compression. "
1490+ " FLAC compression only works if the timestreams are in units of "
1491+ " counts." )
14011492 .add_property (" start" , &G3TimestreamMap::GetStartTime,
14021493 &G3TimestreamMap::SetStartTime,
14031494 " Time of the first sample in the time stream" )
@@ -1417,6 +1508,9 @@ PYBINDINGS("core") {
14171508 &G3TimestreamMap::SetFLACCompression,
14181509 " Level of FLAC compression used for this timestream map. "
14191510 " This can only be non-zero if the timestream is in units of counts." )
1511+ .add_property (" bit_depth" , &G3TimestreamMap::GetBitDepth,
1512+ &G3TimestreamMap::SetFLACDepth,
1513+ " Bit depth of FLAC compression used for this timestream map." )
14201514 ;
14211515 register_pointer_conversions<G3TimestreamMap>();
14221516
0 commit comments