Re-integrate export-optimization branch.
[ardour.git] / libs / audiographer / src / sample_format_converter.cc
1 #include "audiographer/sample_format_converter.h"
2
3 #include "gdither/gdither.h"
4 #include "audiographer/exception.h"
5
6 #include <boost/format.hpp>
7
8 #include <cstring>
9
10 namespace AudioGrapher
11 {
12
13 template <typename TOut>
14 SampleFormatConverter<TOut>::SampleFormatConverter (uint32_t channels) :
15   channels (channels),
16   dither (0),
17   data_out_size (0),
18   data_out (0),
19   clip_floats (false)
20 {
21 }
22
23 template <>
24 void
25 SampleFormatConverter<float>::init (nframes_t max_frames, int type, int data_width)
26 {
27         if (data_width != 32) { throw Exception (*this, "Unsupported data width"); }
28         init_common (max_frames);
29         dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
30 }
31
32 template <>
33 void
34 SampleFormatConverter<int32_t>::init (nframes_t max_frames, int type, int data_width)
35 {
36         if(data_width < 24) { throw Exception (*this, "Use SampleFormatConverter<int16_t> for data widths < 24"); }
37         
38         init_common (max_frames);
39         
40         if (data_width == 24) {
41                 dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width);
42         } else if (data_width == 32) {
43                 dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
44         } else {
45                 throw Exception (*this, "Unsupported data width");
46         }
47 }
48
49 template <>
50 void
51 SampleFormatConverter<int16_t>::init (nframes_t max_frames, int type, int data_width)
52 {
53         if (data_width != 16) { throw Exception (*this, "Unsupported data width"); }
54         init_common (max_frames);
55         dither = gdither_new ((GDitherType) type, channels, GDither16bit, data_width);
56 }
57
58 template <>
59 void
60 SampleFormatConverter<uint8_t>::init (nframes_t max_frames, int type, int data_width)
61 {
62         if (data_width != 8) { throw Exception (*this, "Unsupported data width"); }
63         init_common (max_frames);
64         dither = gdither_new ((GDitherType) type, channels, GDither8bit, data_width);
65 }
66
67 template <typename TOut>
68 void
69 SampleFormatConverter<TOut>::init_common (nframes_t max_frames )
70 {
71         reset();
72         if (max_frames  > data_out_size) {
73
74                 delete[] data_out;
75
76                 data_out = new TOut[max_frames];
77                 data_out_size = max_frames;
78         }
79 }
80
81 template <typename TOut>
82 SampleFormatConverter<TOut>::~SampleFormatConverter ()
83 {
84         reset();
85 }
86
87 template <typename TOut>
88 void
89 SampleFormatConverter<TOut>::reset()
90 {
91         if (dither) {
92                 gdither_free (dither);
93                 dither = 0;
94         }
95         
96         delete[] data_out;
97         data_out_size = 0;
98         data_out = 0;
99         
100         clip_floats = false;
101 }
102
103 /* Basic const version of process() */
104 template <typename TOut>
105 void
106 SampleFormatConverter<TOut>::process (ProcessContext<float> const & c_in)
107 {
108         float const * const data = c_in.data();
109         nframes_t const frames = c_in.frames();
110         
111         check_frame_count (frames);
112
113         /* Do conversion */
114
115         for (uint32_t chn = 0; chn < channels; ++chn) {
116                 gdither_runf (dither, chn, frames / channels, data, data_out);
117         }
118
119         /* Write forward */
120
121         ProcessContext<TOut> c_out(c_in, data_out);
122         output (c_out);
123 }
124
125 /* Basic non-const version of process(), calls the const one */
126 template<typename TOut>
127 void
128 SampleFormatConverter<TOut>::process (ProcessContext<float> & c_in)
129 {
130         process (static_cast<ProcessContext<float> const &> (c_in));
131 }
132
133 /* template specialization for float, in-place processing (non-const) */
134 template<>
135 void
136 SampleFormatConverter<float>::process (ProcessContext<float> & c_in)
137 {
138         nframes_t frames = c_in.frames();
139         float * data = c_in.data();
140         
141         if (clip_floats) {
142                 for (nframes_t x = 0; x < frames; ++x) {
143                         if (data[x] > 1.0f) {
144                                 data[x] = 1.0f;
145                         } else if (data[x] < -1.0f) {
146                                 data[x] = -1.0f;
147                         }
148                 }
149         }
150
151         output (c_in);
152 }
153
154 /* template specialized const version, copies the data, and calls the non-const version */
155 template<>
156 void
157 SampleFormatConverter<float>::process (ProcessContext<float> const & c_in)
158 {
159         // Make copy of data and pass it to non-const version
160         nframes_t frames = c_in.frames();
161         check_frame_count (frames);
162         memcpy (data_out, c_in.data(), frames * sizeof(float));
163         
164         ProcessContext<float> c (c_in, data_out);
165         process (c);
166 }
167
168 template<typename TOut>
169 void
170 SampleFormatConverter<TOut>::check_frame_count(nframes_t frames)
171 {
172         if (frames % channels != 0) {
173                 throw Exception (*this, boost::str (boost::format (
174                         "Number of frames given to process() was not a multiple of channels: %1% frames with %2% channels")
175                         % frames % channels));
176         }
177         
178         if (frames  > data_out_size) {
179                 throw Exception (*this, boost::str (boost::format (
180                         "Too many frames given to process(), %1% instad of %2%")
181                         % frames % data_out_size));
182         }
183 }
184
185 template class SampleFormatConverter<uint8_t>;
186 template class SampleFormatConverter<int16_t>;
187 template class SampleFormatConverter<int32_t>;
188 template class SampleFormatConverter<float>;
189
190 } // namespace