|
3 | 3 | #include "smwavdata.hh" |
4 | 4 | #include "smmath.hh" |
5 | 5 |
|
| 6 | +#include <set> |
| 7 | + |
6 | 8 | #include <sndfile.h> |
7 | 9 | #include <assert.h> |
8 | 10 |
|
9 | 11 | using namespace SpectMorph; |
10 | 12 |
|
11 | 13 | using std::string; |
12 | 14 | using std::vector; |
| 15 | +using std::set; |
13 | 16 |
|
14 | 17 | static string |
15 | 18 | strip_dot (string s) |
@@ -62,50 +65,58 @@ WavData::load (std::function<SNDFILE* (SF_INFO *)> open_func) |
62 | 65 | return false; |
63 | 66 | } |
64 | 67 |
|
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.data(), sfinfo.frames); |
73 | | - } |
74 | | - else |
| 68 | + if (sfinfo.frames != SF_COUNT_MAX) |
75 | 69 | { |
76 | | - // for non-floating point wav files, we convert |
77 | | - vector<int> isamples (sfinfo.frames * sfinfo.channels); |
78 | | - count = sf_readf_int (sndfile, isamples.data(), 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; |
| 70 | + /* reserve space only if we know how long the input sample is */ |
| 71 | + m_samples.reserve (sfinfo.frames * sfinfo.channels); |
91 | 72 | } |
92 | 73 |
|
93 | | - error = sf_error (sndfile); |
94 | | - if (error) |
95 | | - { |
96 | | - m_error_blurb = strip_dot (sf_strerror (sndfile)); |
97 | | - sf_close (sndfile); |
| 74 | + int mask_format = sfinfo.format & SF_FORMAT_SUBMASK; |
98 | 75 |
|
99 | | - return false; |
100 | | - } |
| 76 | + sf_count_t count; |
| 77 | + sf_count_t buffer_size = 4096; |
101 | 78 |
|
102 | | - if (count != sfinfo.frames) |
| 79 | + vector<float> fbuffer; |
| 80 | + vector<int> ibuffer (buffer_size * sfinfo.channels); |
| 81 | + do |
103 | 82 | { |
104 | | - m_error_blurb = "Reading sample data failed: short read"; |
105 | | - sf_close (sndfile); |
| 83 | + if (mask_format == SF_FORMAT_FLOAT || mask_format == SF_FORMAT_DOUBLE) |
| 84 | + { |
| 85 | + // for floating point wav files, we use the float data as provided by libsndfile |
| 86 | + fbuffer.resize (buffer_size * sfinfo.channels); |
| 87 | + count = sf_readf_float (sndfile, fbuffer.data(), buffer_size); |
| 88 | + fbuffer.resize (count * sfinfo.channels); |
| 89 | + } |
| 90 | + else |
| 91 | + { |
| 92 | + // for non-floating point wav files, we convert |
| 93 | + count = sf_readf_int (sndfile, ibuffer.data(), buffer_size); |
| 94 | + fbuffer.resize (count * sfinfo.channels); |
| 95 | + |
| 96 | + /* reading a wav file and saving it again with the libsndfile float API will |
| 97 | + * change some values due to normalization issues: |
| 98 | + * http://www.mega-nerd.com/libsndfile/FAQ.html#Q010 |
| 99 | + * |
| 100 | + * to avoid the problem, we use the int API and do the conversion beween int |
| 101 | + * and float manually - the important part is that the normalization factors |
| 102 | + * used during read and write are identical |
| 103 | + */ |
| 104 | + const double norm = 1.0 / 0x80000000LL; |
| 105 | + for (size_t i = 0; i < fbuffer.size(); i++) |
| 106 | + fbuffer[i] = ibuffer[i] * norm; |
| 107 | + } |
| 108 | + m_samples.insert (m_samples.end(), fbuffer.begin(), fbuffer.end()); |
106 | 109 |
|
107 | | - return false; |
| 110 | + error = sf_error (sndfile); |
| 111 | + if (error) |
| 112 | + { |
| 113 | + m_error_blurb = strip_dot (sf_strerror (sndfile)); |
| 114 | + sf_close (sndfile); |
| 115 | + |
| 116 | + return false; |
| 117 | + } |
108 | 118 | } |
| 119 | + while (count > 0); |
109 | 120 |
|
110 | 121 | m_mix_freq = sfinfo.samplerate; |
111 | 122 | m_n_channels = sfinfo.channels; |
@@ -438,3 +449,40 @@ WavData::error_blurb() const |
438 | 449 | { |
439 | 450 | return m_error_blurb.c_str(); |
440 | 451 | } |
| 452 | + |
| 453 | +vector<string> |
| 454 | +WavData::supported_extensions() |
| 455 | +{ |
| 456 | + set<string> unique_extensions; |
| 457 | + |
| 458 | + int count = 0; |
| 459 | + if (sf_command (nullptr, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) != 0) |
| 460 | + { |
| 461 | + sm_debug ("error: failed to query libsndfile format count.\n"); |
| 462 | + return {}; |
| 463 | + } |
| 464 | + |
| 465 | + for (int i = 0; i < count; i++) |
| 466 | + { |
| 467 | + SF_FORMAT_INFO info; |
| 468 | + info.format = i; |
| 469 | + |
| 470 | + if (sf_command (nullptr, SFC_GET_SIMPLE_FORMAT, &info, sizeof(info)) == 0) |
| 471 | + { |
| 472 | + string ext = info.extension; |
| 473 | + if (ext == "oga") /* older libsndfile versions return oga for ogg/vorbis format */ |
| 474 | + { |
| 475 | + unique_extensions.insert ("ogg"); |
| 476 | + } |
| 477 | + else if (ext == "aiff") |
| 478 | + { |
| 479 | + unique_extensions.insert ("aif"); |
| 480 | + unique_extensions.insert ("aiff"); |
| 481 | + } |
| 482 | + else if (!ext.empty()) |
| 483 | + unique_extensions.insert (ext); |
| 484 | + } |
| 485 | + } |
| 486 | + |
| 487 | + return vector<string> (unique_extensions.begin(), unique_extensions.end()); |
| 488 | +} |
0 commit comments