1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2 
3 #include "smwavdata.hh"
4 #include "smmath.hh"
5 
6 #include <sndfile.h>
7 #include <assert.h>
8 
9 using namespace SpectMorph;
10 
11 using std::string;
12 using std::vector;
13 
14 static string
strip_dot(string s)15 strip_dot (string s)
16 {
17   if (!s.empty() && s[s.size() - 1] == '.')
18     s.erase (s.size() - 1);
19   return s;
20 }
21 
WavData()22 WavData::WavData()
23 {
24   clear();
25 }
26 
27 void
clear()28 WavData::clear()
29 {
30   m_samples.clear();
31 
32   m_n_channels  = 0;
33   m_bit_depth   = 0;
34   m_mix_freq    = 0;
35   m_error_blurb = "";
36 }
37 
38 bool
load(const string & filename)39 WavData::load (const string& filename)
40 {
41   return load ([&] (SF_INFO *sfinfo) {
42     return sf_open (filename.c_str(), SFM_READ, sfinfo);
43   });
44 }
45 
46 bool
load(std::function<SNDFILE * (SF_INFO *)> open_func)47 WavData::load (std::function<SNDFILE* (SF_INFO *)> open_func)
48 {
49   clear(); // get rid of old contents
50 
51   SF_INFO sfinfo = { 0, };
52 
53   SNDFILE *sndfile = open_func (&sfinfo);
54 
55   int error = sf_error (sndfile);
56   if (error)
57     {
58       m_error_blurb = strip_dot (sf_strerror (sndfile));
59       if (sndfile)
60         sf_close (sndfile);
61 
62       return false;
63     }
64 
65   int mask_format = sfinfo.format & SF_FORMAT_SUBMASK;
66   sf_count_t count;
67 
68   m_samples.resize (sfinfo.frames * sfinfo.channels);
69   if (mask_format == SF_FORMAT_FLOAT || mask_format == SF_FORMAT_DOUBLE)
70     {
71       // for floating point wav files, we use the float data as provided by libsndfile
72       count = sf_readf_float (sndfile, &m_samples[0], sfinfo.frames);
73     }
74   else
75     {
76       // for non-floating point wav files, we convert
77       vector<int> isamples (sfinfo.frames * sfinfo.channels);
78       count = sf_readf_int (sndfile, &isamples[0], sfinfo.frames);
79 
80       /* reading a wav file and saving it again with the libsndfile float API will
81        * change some values due to normalization issues:
82        *   http://www.mega-nerd.com/libsndfile/FAQ.html#Q010
83        *
84        * to avoid the problem, we use the int API and do the conversion beween int
85        * and float manually - the important part is that the normalization factors
86        * used during read and write are identical
87        */
88       const double norm = 1.0 / 0x80000000LL;
89       for (size_t i = 0; i < m_samples.size(); i++)
90         m_samples[i] = isamples[i] * norm;
91     }
92 
93   error = sf_error (sndfile);
94   if (error)
95     {
96       m_error_blurb = strip_dot (sf_strerror (sndfile));
97       sf_close (sndfile);
98 
99       return false;
100     }
101 
102   if (count != sfinfo.frames)
103     {
104       m_error_blurb = "Reading sample data failed: short read";
105       sf_close (sndfile);
106 
107       return false;
108     }
109 
110   m_mix_freq    = sfinfo.samplerate;
111   m_n_channels  = sfinfo.channels;
112 
113   switch (sfinfo.format & SF_FORMAT_SUBMASK)
114     {
115       case SF_FORMAT_PCM_U8:
116       case SF_FORMAT_PCM_S8:
117           m_bit_depth = 8;
118           break;
119 
120       case SF_FORMAT_PCM_16:
121           m_bit_depth = 16;
122           break;
123 
124       case SF_FORMAT_PCM_24:
125           m_bit_depth = 24;
126           break;
127 
128       case SF_FORMAT_FLOAT:
129       case SF_FORMAT_PCM_32:
130           m_bit_depth = 32;
131           break;
132 
133       case SF_FORMAT_DOUBLE:
134           m_bit_depth = 64;
135           break;
136 
137       default:
138           m_bit_depth = 32; /* unknown */
139     }
140 
141   error = sf_close (sndfile);
142   if (error)
143     {
144       m_error_blurb = strip_dot (sf_error_number (error));
145       return false;
146     }
147   return true;
148 }
149 
150 bool
load_mono(const string & filename)151 WavData::load_mono (const string& filename)
152 {
153   if (!load (filename))
154     return false;
155 
156   if (m_n_channels != 1)
157     {
158       m_error_blurb = "Only mono files supported";
159       return false;
160     }
161 
162   return true;
163 }
164 
165 bool
save(const string & filename,OutFormat out_format)166 WavData::save (const string& filename, OutFormat out_format)
167 {
168   return save ([&] (SF_INFO *sfinfo)
169     {
170       return sf_open (filename.c_str(), SFM_WRITE, sfinfo);
171     }, out_format);
172 }
173 
174 namespace {
175 struct VirtualData
176 {
177   vector<unsigned char> *mem    = nullptr;
178   sf_count_t             offset = 0;
179 };
180 }
181 
182 static sf_count_t
virtual_get_len(void * data)183 virtual_get_len (void *data)
184 {
185   VirtualData *vdata = static_cast<VirtualData *> (data);
186 
187   return vdata->mem->size();
188 }
189 
190 static sf_count_t
virtual_seek(sf_count_t offset,int whence,void * data)191 virtual_seek (sf_count_t offset, int whence, void *data)
192 {
193   VirtualData *vdata = static_cast<VirtualData *> (data);
194 
195   if (whence == SEEK_CUR)
196     {
197       vdata->offset = vdata->offset + offset;
198     }
199   else if (whence == SEEK_SET)
200     {
201       vdata->offset = offset;
202     }
203   else if (whence == SEEK_END)
204     {
205       vdata->offset = vdata->mem->size() + offset;
206     }
207 
208   /* can't seek beyond eof */
209   vdata->offset = sm_bound<sf_count_t> (0, vdata->offset, vdata->mem->size());
210   return vdata->offset;
211 }
212 
213 static sf_count_t
virtual_read(void * ptr,sf_count_t count,void * data)214 virtual_read (void *ptr, sf_count_t count, void *data)
215 {
216   /* FIXME: need to implement reading, too */
217   VirtualData *vdata = static_cast<VirtualData *> (data);
218 
219   int rcount = 0;
220   unsigned char *uptr = static_cast<unsigned char *> (ptr);
221   for (sf_count_t i = 0; i < count; i++)
222     {
223       size_t rpos = i + vdata->offset;
224       if (rpos < vdata->mem->size())
225         {
226           uptr[i] = (*vdata->mem)[rpos];
227           rcount++;
228         }
229     }
230   vdata->offset += rcount;
231   return rcount;
232 }
233 
234 static sf_count_t
virtual_write(const void * ptr,sf_count_t count,void * data)235 virtual_write (const void *ptr, sf_count_t count, void *data)
236 {
237   VirtualData *vdata = static_cast<VirtualData *> (data);
238 
239   const unsigned char *uptr = static_cast<const unsigned char *> (ptr);
240   for (sf_count_t i = 0; i < count; i++)
241     {
242       unsigned char ch = uptr[i];
243 
244       size_t wpos = i + vdata->offset;
245       if (wpos >= vdata->mem->size())
246         vdata->mem->resize (wpos + 1);
247       (*vdata->mem)[wpos] = ch;
248     }
249   vdata->offset += count;
250   return count;
251 }
252 
253 static sf_count_t
virtual_tell(void * data)254 virtual_tell (void *data)
255 {
256   VirtualData *vdata = static_cast<VirtualData *> (data);
257   return vdata->offset;
258 }
259 
260 bool
save(vector<unsigned char> & out,OutFormat out_format)261 WavData::save (vector<unsigned char>& out, OutFormat out_format)
262 {
263   VirtualData virtual_data;
264 
265   virtual_data.mem = &out;
266 
267   SF_VIRTUAL_IO sfvirtual = {
268     virtual_get_len,
269     virtual_seek,
270     virtual_read,
271     virtual_write,
272     virtual_tell
273   };
274   return save ([&] (SF_INFO *sfinfo)
275     {
276       return sf_open_virtual (&sfvirtual, SFM_WRITE, sfinfo, &virtual_data);
277     }, out_format);
278 }
279 
280 bool
load(const vector<unsigned char> & in)281 WavData::load (const vector<unsigned char>& in)
282 {
283   VirtualData virtual_data;
284 
285   /* to ensure that in really isn't modified */
286   vector<unsigned char> in_copy = in;
287   virtual_data.mem = &in_copy;
288 
289   SF_VIRTUAL_IO sfvirtual = {
290     virtual_get_len,
291     virtual_seek,
292     virtual_read,
293     virtual_write,
294     virtual_tell
295   };
296   return load ([&] (SF_INFO *sfinfo) {
297     return sf_open_virtual (&sfvirtual, SFM_READ, sfinfo, &virtual_data);
298   });
299 }
300 
301 bool
save(std::function<SNDFILE * (SF_INFO *)> open_func,OutFormat out_format)302 WavData::save (std::function<SNDFILE* (SF_INFO *)> open_func, OutFormat out_format)
303 {
304   SF_INFO sfinfo = {0,};
305 
306   sfinfo.samplerate = sm_round_positive (m_mix_freq);
307   sfinfo.channels   = m_n_channels;
308 
309   switch (out_format)
310   {
311     case OutFormat::WAV:  sfinfo.format = SF_FORMAT_WAV;
312                           break;
313     case OutFormat::FLAC: sfinfo.format = SF_FORMAT_FLAC;
314                           break;
315     default:              assert (false);
316   }
317   if (m_bit_depth > 16)
318     sfinfo.format |= SF_FORMAT_PCM_24;
319   else
320     sfinfo.format |= SF_FORMAT_PCM_16;
321 
322   SNDFILE *sndfile = open_func (&sfinfo);
323   int error = sf_error (sndfile);
324   if (error)
325     {
326       m_error_blurb = strip_dot (sf_strerror (sndfile));
327       if (sndfile)
328         sf_close (sndfile);
329 
330       return false;
331     }
332 
333   vector<int> isamples (m_samples.size());
334   for (size_t i = 0; i < m_samples.size(); i++)
335     {
336       const double norm      =  0x80000000LL;
337       const double min_value = -0x80000000LL;
338       const double max_value =  0x7FFFFFFF;
339 
340       isamples[i] = lrint (sm_bound<double> (min_value, m_samples[i] * norm, max_value));
341     }
342 
343   sf_count_t frames = m_samples.size() / m_n_channels;
344   sf_count_t count = sf_writef_int (sndfile, &isamples[0], frames);
345 
346   error = sf_error (sndfile);
347   if (error)
348     {
349       m_error_blurb = strip_dot (sf_strerror (sndfile));
350       sf_close (sndfile);
351 
352       return false;
353     }
354 
355   if (count != frames)
356     {
357       m_error_blurb = "Writing sample data failed: short write";
358       sf_close (sndfile);
359 
360       return false;
361     }
362 
363   error = sf_close (sndfile);
364   if (error)
365     {
366       m_error_blurb = strip_dot (sf_error_number (error));
367       return false;
368     }
369   return true;
370 }
371 
WavData(const vector<float> & samples,int n_channels,float mix_freq,int bit_depth)372 WavData::WavData (const vector<float>& samples, int n_channels, float mix_freq, int bit_depth)
373 {
374   m_samples     = samples;
375   m_n_channels  = n_channels;
376   m_mix_freq    = mix_freq;
377   m_bit_depth   = bit_depth;
378 }
379 
380 void
load(const vector<float> & samples,int n_channels,float mix_freq,int bit_depth)381 WavData::load (const vector<float>& samples, int n_channels, float mix_freq, int bit_depth)
382 {
383   // same function: WavData::WavData(...)
384 
385   m_samples     = samples;
386   m_n_channels  = n_channels;
387   m_mix_freq    = mix_freq;
388   m_bit_depth   = bit_depth;
389 }
390 
391 void
prepend(const vector<float> & samples)392 WavData::prepend (const vector<float>& samples)
393 {
394   assert (samples.size() % m_n_channels == 0);
395 
396   m_samples.insert (m_samples.begin(), samples.begin(), samples.end());
397 }
398 
399 float
operator [](size_t pos) const400 WavData::operator[] (size_t pos) const
401 {
402   assert (pos < m_samples.size());
403 
404   return m_samples[pos];
405 }
406 
407 float
mix_freq() const408 WavData::mix_freq() const
409 {
410   return m_mix_freq;
411 }
412 
413 int
n_channels() const414 WavData::n_channels() const
415 {
416   return m_n_channels;
417 }
418 
419 int
bit_depth() const420 WavData::bit_depth() const
421 {
422   return m_bit_depth;
423 }
424 
425 const vector<float>&
samples() const426 WavData::samples() const
427 {
428   return m_samples;
429 }
430 
431 size_t
n_values() const432 WavData::n_values() const
433 {
434   return m_samples.size();
435 }
436 
437 const char *
error_blurb() const438 WavData::error_blurb() const
439 {
440   return m_error_blurb.c_str();
441 }
442