Work around a bug in gdither with 32-bit integer export, fixes 32-bit export.
[ardour.git] / libs / audiographer / src / general / sample_format_converter.cc
1 /*
2     Copyright (C) 2012 Paul Davis 
3     Author: Sakari Bergen
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include "audiographer/general/sample_format_converter.h"
22
23 #include "audiographer/exception.h"
24 #include "audiographer/type_utils.h"
25 #include "private/gdither/gdither.h"
26
27 #include <boost/format.hpp>
28
29 namespace AudioGrapher
30 {
31
32 template <typename TOut>
33 SampleFormatConverter<TOut>::SampleFormatConverter (ChannelCount channels) :
34   channels (channels),
35   dither (0),
36   data_out_size (0),
37   data_out (0),
38   clip_floats (false)
39 {
40 }
41
42 template <>
43 void
44 SampleFormatConverter<float>::init (framecnt_t max_frames, int /* type */, int data_width)
45 {
46         if (throw_level (ThrowObject) && data_width != 32) {
47                 throw Exception (*this, "Unsupported data width");
48         }
49         init_common (max_frames);
50         dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
51 }
52
53 template <>
54 void
55 SampleFormatConverter<int32_t>::init (framecnt_t max_frames, int type, int data_width)
56 {
57         if(throw_level (ThrowObject) && data_width < 24) {
58                 throw Exception (*this, "Trying to use SampleFormatConverter<int32_t> for data widths < 24");
59         }
60
61         init_common (max_frames);
62
63         // GDither is broken with GDither32bit if the dither depth
64         // is bigger than 24, so lets just use that...
65         dither = gdither_new (GDitherNone, channels, GDither32bit, 24);
66 }
67
68 template <>
69 void
70 SampleFormatConverter<int16_t>::init (framecnt_t max_frames, int type, int data_width)
71 {
72         if (throw_level (ThrowObject) && data_width != 16) {
73                 throw Exception (*this, "Unsupported data width");
74         }
75         init_common (max_frames);
76         dither = gdither_new ((GDitherType) type, channels, GDither16bit, data_width);
77 }
78
79 template <>
80 void
81 SampleFormatConverter<uint8_t>::init (framecnt_t max_frames, int type, int data_width)
82 {
83         if (throw_level (ThrowObject) && data_width != 8) {
84                 throw Exception (*this, "Unsupported data width");
85         }
86         init_common (max_frames);
87         dither = gdither_new ((GDitherType) type, channels, GDither8bit, data_width);
88 }
89
90 template <typename TOut>
91 void
92 SampleFormatConverter<TOut>::init_common (framecnt_t max_frames)
93 {
94         reset();
95         if (max_frames  > data_out_size) {
96
97                 delete[] data_out;
98
99                 data_out = new TOut[max_frames];
100                 data_out_size = max_frames;
101         }
102 }
103
104 template <typename TOut>
105 SampleFormatConverter<TOut>::~SampleFormatConverter ()
106 {
107         reset();
108 }
109
110 template <typename TOut>
111 void
112 SampleFormatConverter<TOut>::reset()
113 {
114         if (dither) {
115                 gdither_free (dither);
116                 dither = 0;
117         }
118         
119         delete[] data_out;
120         data_out_size = 0;
121         data_out = 0;
122         
123         clip_floats = false;
124 }
125
126 /* Basic const version of process() */
127 template <typename TOut>
128 void
129 SampleFormatConverter<TOut>::process (ProcessContext<float> const & c_in)
130 {
131         float const * const data = c_in.data();
132         
133         check_frame_and_channel_count (c_in.frames (), c_in.channels ());
134
135         /* Do conversion */
136
137         for (uint32_t chn = 0; chn < c_in.channels(); ++chn) {
138                 gdither_runf (dither, chn, c_in.frames_per_channel (), data, data_out);
139         }
140
141         /* Write forward */
142
143         ProcessContext<TOut> c_out(c_in, data_out);
144         this->output (c_out);
145 }
146
147 /* Basic non-const version of process(), calls the const one */
148 template<typename TOut>
149 void
150 SampleFormatConverter<TOut>::process (ProcessContext<float> & c_in)
151 {
152         process (static_cast<ProcessContext<float> const &> (c_in));
153 }
154
155 /* template specialization for float, in-place processing (non-const) */
156 template<>
157 void
158 SampleFormatConverter<float>::process (ProcessContext<float> & c_in)
159 {
160         framecnt_t frames = c_in.frames();
161         float * data = c_in.data();
162         
163         if (clip_floats) {
164                 for (framecnt_t x = 0; x < frames; ++x) {
165                         if (data[x] > 1.0f) {
166                                 data[x] = 1.0f;
167                         } else if (data[x] < -1.0f) {
168                                 data[x] = -1.0f;
169                         }
170                 }
171         }
172
173         output (c_in);
174 }
175
176 /* template specialized const version, copies the data, and calls the non-const version */
177 template<>
178 void
179 SampleFormatConverter<float>::process (ProcessContext<float> const & c_in)
180 {
181         // Make copy of data and pass it to non-const version
182         check_frame_and_channel_count (c_in.frames(), c_in.channels());
183         TypeUtils<float>::copy (c_in.data(), data_out, c_in.frames());
184         
185         ProcessContext<float> c (c_in, data_out);
186         process (c);
187 }
188
189 template<typename TOut>
190 void
191 SampleFormatConverter<TOut>::check_frame_and_channel_count (framecnt_t frames, ChannelCount channels_)
192 {
193         if (throw_level (ThrowStrict) && channels_ != channels) {
194                 throw Exception (*this, boost::str (boost::format
195                         ("Wrong channel count given to process(), %1% instead of %2%")
196                         % channels_ % channels));
197         }
198         
199         if (throw_level (ThrowProcess) && frames  > data_out_size) {
200                 throw Exception (*this, boost::str (boost::format
201                         ("Too many frames given to process(), %1% instad of %2%")
202                         % frames % data_out_size));
203         }
204 }
205
206 template class SampleFormatConverter<uint8_t>;
207 template class SampleFormatConverter<int16_t>;
208 template class SampleFormatConverter<int32_t>;
209 template class SampleFormatConverter<float>;
210
211 } // namespace