Merge branch 'master' into cairocanvas
[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         // GDither is broken with GDither32bit if the dither depth is bigger than 24
58         if(throw_level (ThrowObject) && data_width > 24) {
59                 throw Exception (*this, "Trying to use SampleFormatConverter<int32_t> a data width > 24");
60         }
61         init_common (max_frames);
62         dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width);
63 }
64
65 template <>
66 void
67 SampleFormatConverter<int16_t>::init (framecnt_t max_frames, int type, int data_width)
68 {
69         if (throw_level (ThrowObject) && data_width > 16) {
70                 throw Exception (*this, boost::str(boost::format
71                     ("Data width (%1) too large for int16_t")
72                     % data_width));
73         }
74         init_common (max_frames);
75         dither = gdither_new ((GDitherType) type, channels, GDither16bit, data_width);
76 }
77
78 template <>
79 void
80 SampleFormatConverter<uint8_t>::init (framecnt_t max_frames, int type, int data_width)
81 {
82         if (throw_level (ThrowObject) && data_width > 8) {
83                 throw Exception (*this, boost::str(boost::format
84                     ("Data width (%1) too large for uint8_t")
85                     % data_width));
86         }
87         init_common (max_frames);
88         dither = gdither_new ((GDitherType) type, channels, GDither8bit, data_width);
89 }
90
91 template <typename TOut>
92 void
93 SampleFormatConverter<TOut>::init_common (framecnt_t max_frames)
94 {
95         reset();
96         if (max_frames  > data_out_size) {
97
98                 delete[] data_out;
99
100                 data_out = new TOut[max_frames];
101                 data_out_size = max_frames;
102         }
103 }
104
105 template <typename TOut>
106 SampleFormatConverter<TOut>::~SampleFormatConverter ()
107 {
108         reset();
109 }
110
111 template <typename TOut>
112 void
113 SampleFormatConverter<TOut>::reset()
114 {
115         if (dither) {
116                 gdither_free (dither);
117                 dither = 0;
118         }
119         
120         delete[] data_out;
121         data_out_size = 0;
122         data_out = 0;
123         
124         clip_floats = false;
125 }
126
127 /* Basic const version of process() */
128 template <typename TOut>
129 void
130 SampleFormatConverter<TOut>::process (ProcessContext<float> const & c_in)
131 {
132         float const * const data = c_in.data();
133         
134         check_frame_and_channel_count (c_in.frames (), c_in.channels ());
135
136         /* Do conversion */
137
138         for (uint32_t chn = 0; chn < c_in.channels(); ++chn) {
139                 gdither_runf (dither, chn, c_in.frames_per_channel (), data, data_out);
140         }
141
142         /* Write forward */
143
144         ProcessContext<TOut> c_out(c_in, data_out);
145         this->output (c_out);
146 }
147
148 /* Basic non-const version of process(), calls the const one */
149 template<typename TOut>
150 void
151 SampleFormatConverter<TOut>::process (ProcessContext<float> & c_in)
152 {
153         process (static_cast<ProcessContext<float> const &> (c_in));
154 }
155
156 /* template specialization for float, in-place processing (non-const) */
157 template<>
158 void
159 SampleFormatConverter<float>::process (ProcessContext<float> & c_in)
160 {
161         framecnt_t frames = c_in.frames();
162         float * data = c_in.data();
163         
164         if (clip_floats) {
165                 for (framecnt_t x = 0; x < frames; ++x) {
166                         if (data[x] > 1.0f) {
167                                 data[x] = 1.0f;
168                         } else if (data[x] < -1.0f) {
169                                 data[x] = -1.0f;
170                         }
171                 }
172         }
173
174         output (c_in);
175 }
176
177 /* template specialized const version, copies the data, and calls the non-const version */
178 template<>
179 void
180 SampleFormatConverter<float>::process (ProcessContext<float> const & c_in)
181 {
182         // Make copy of data and pass it to non-const version
183         check_frame_and_channel_count (c_in.frames(), c_in.channels());
184         TypeUtils<float>::copy (c_in.data(), data_out, c_in.frames());
185         
186         ProcessContext<float> c (c_in, data_out);
187         process (c);
188 }
189
190 template<typename TOut>
191 void
192 SampleFormatConverter<TOut>::check_frame_and_channel_count (framecnt_t frames, ChannelCount channels_)
193 {
194         if (throw_level (ThrowStrict) && channels_ != channels) {
195                 throw Exception (*this, boost::str (boost::format
196                         ("Wrong channel count given to process(), %1% instead of %2%")
197                         % channels_ % channels));
198         }
199         
200         if (throw_level (ThrowProcess) && frames  > data_out_size) {
201                 throw Exception (*this, boost::str (boost::format
202                         ("Too many frames given to process(), %1% instad of %2%")
203                         % frames % data_out_size));
204         }
205 }
206
207 template class SampleFormatConverter<uint8_t>;
208 template class SampleFormatConverter<int16_t>;
209 template class SampleFormatConverter<int32_t>;
210 template class SampleFormatConverter<float>;
211
212 } // namespace