Skip to content

Commit 3a55668

Browse files
committed
Merge branch 'mp3-support'
* mp3-support: MACOS: update audio libs to support mp3 files MISC: new spectmorph-mxe with libsndfile-1.2.2 with mp3 support for windows STATIC: build-deps.sh: update to libsndfile-1.2.2 with opus and mp3 support LIB: WavData: don't fail if input sample is shorted than predicted This can happen for mp3 files, which sometimes have inaccurate size information. TESTS: testwavdata: add more test code GLUI: use supported audio file extensions from WavData LIB: WavData: implement function which returns supported file extensions Signed-off-by: Stefan Westerfeld <stefan@space.twc.de>
2 parents d196894 + d1aaf72 commit 3a55668

File tree

7 files changed

+157
-45
lines changed

7 files changed

+157
-45
lines changed

glui/sminsteditwindow.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ void
737737
InstEditWindow::on_add_sample_clicked()
738738
{
739739
FileDialogFormats formats;
740-
formats.add ("Supported Audio Files", { "wav", "flac", "ogg", "aiff" });
740+
formats.add ("Supported Audio Files", WavData::supported_extensions());
741741
formats.add ("All Files", { "*" });
742742
open_file_dialog ("Select Sample to load", formats, [=](string filename) {
743743
load_sample (filename);

lib/smwavdata.cc

Lines changed: 84 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
#include "smwavdata.hh"
44
#include "smmath.hh"
55

6+
#include <set>
7+
68
#include <sndfile.h>
79
#include <assert.h>
810

911
using namespace SpectMorph;
1012

1113
using std::string;
1214
using std::vector;
15+
using std::set;
1316

1417
static string
1518
strip_dot (string s)
@@ -62,50 +65,58 @@ WavData::load (std::function<SNDFILE* (SF_INFO *)> open_func)
6265
return false;
6366
}
6467

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)
7569
{
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);
9172
}
9273

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;
9875

99-
return false;
100-
}
76+
sf_count_t count;
77+
sf_count_t buffer_size = 4096;
10178

102-
if (count != sfinfo.frames)
79+
vector<float> fbuffer;
80+
vector<int> ibuffer (buffer_size * sfinfo.channels);
81+
do
10382
{
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());
106109

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+
}
108118
}
119+
while (count > 0);
109120

110121
m_mix_freq = sfinfo.samplerate;
111122
m_n_channels = sfinfo.channels;
@@ -438,3 +449,40 @@ WavData::error_blurb() const
438449
{
439450
return m_error_blurb.c_str();
440451
}
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+
}

lib/smwavdata.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public:
3232
WavData();
3333
WavData (const std::vector<float>& samples, int n_channels, float mix_freq, int bit_depth);
3434

35+
static std::vector<std::string> supported_extensions();
36+
3537
bool load (const std::vector<unsigned char>& in);
3638
bool load (const std::string& filename);
3739
bool load_mono (const std::string& filename);

macos/build-deps.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,13 @@ autoconfbuild --disable-shared
131131
src opus-1.3.1 tar.gz https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz
132132
autoconfbuild --disable-shared
133133

134-
src libsndfile-1.2.0 tar.xz https://github.com/libsndfile/libsndfile/releases/download/1.2.0/libsndfile-1.2.0.tar.xz
134+
src mpg123-1.33.3 tar.bz2 https://sourceforge.net/projects/mpg123/files/mpg123/1.33.3/mpg123-1.33.3.tar.bz2
135+
autoconfbuild --disable-shared
136+
137+
src lame-3.100 tar.gz https://sourceforge.net/projects/lame/files/lame/3.100/lame-3.100.tar.gz
138+
autoconfbuild --disable-shared
139+
140+
src libsndfile-1.2.2 tar.xz https://github.com/libsndfile/libsndfile/releases/download/1.2.2/libsndfile-1.2.2.tar.xz
135141
autoconfbuild --disable-shared
136142

137143
src lv2-1.18.10 tar.gz https://github.com/lv2/lv2/archive/refs/tags/v1.18.10.tar.gz

misc/Dockerfile-win

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ RUN apt-get -y install wget build-essential autoconf automake \
55
libtool-bin pkg-config gettext autoconf-archive \
66
xz-utils nsis meson wine
77

8-
RUN wget -q https://github.com/swesterfeld/spectmorph-mxe/releases/download/2024-06-16/spectmorph-mxe-2024-06-16.tar.xz
9-
RUN tar xf spectmorph-mxe-2024-06-16.tar.xz
8+
RUN wget -q https://github.com/swesterfeld/spectmorph-mxe/releases/download/2025-10-16/spectmorph-mxe-2025-10-16.tar.xz
9+
RUN tar xf spectmorph-mxe-2025-10-16.tar.xz
1010

1111
ADD . /spectmorph
1212
WORKDIR /spectmorph

static/build-deps.sh

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ function autoconfbuild {
1717
make install
1818
}
1919

20+
function download {
21+
echo "--- Downloading.. ${SRCDIR}/$1 $2"
22+
test -f ${SRCDIR}/$1 && return
23+
curl -L -o ${SRCDIR}/$1.tmp $2
24+
mv ${SRCDIR}/$1.tmp ${SRCDIR}/$1
25+
}
26+
2027
SRCDIR=$PWD/src
2128
BUILDD=$PWD/build
2229

@@ -33,7 +40,6 @@ cd $SRCDIR
3340
apt-get source libogg
3441
apt-get source libvorbis
3542
apt-get source flac
36-
apt-get source libsndfile
3743
apt-get source zlib1g
3844
apt-get source libffi
3945
apt-get source glib2.0
@@ -53,7 +59,28 @@ cd $SRCDIR/flac-1.3.3
5359
./autogen.sh
5460
autoconfbuild --disable-shared
5561

56-
cd $SRCDIR/libsndfile-1.0.28
62+
download opus-1.3.1.tar.gz https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz
63+
cd $SRCDIR
64+
tar xf opus-1.3.1.tar.gz
65+
cd opus-1.3.1
66+
autoconfbuild --disable-shared
67+
68+
download mpg123-1.33.3.tar.bz2 https://sourceforge.net/projects/mpg123/files/mpg123/1.33.3/mpg123-1.33.3.tar.bz2
69+
cd $SRCDIR
70+
tar xf mpg123-1.33.3.tar.bz2
71+
cd $SRCDIR/mpg123-1.33.3
72+
autoconfbuild --disable-shared
73+
74+
download lame-3.100.tar.gz https://sourceforge.net/projects/lame/files/lame/3.100/lame-3.100.tar.gz
75+
cd $SRCDIR
76+
tar xf lame-3.100.tar.gz
77+
cd $SRCDIR/lame-3.100
78+
autoconfbuild --disable-shared
79+
80+
download libsndfile-1.2.2.tar.xz https://github.com/libsndfile/libsndfile/releases/download/1.2.2/libsndfile-1.2.2.tar.xz
81+
cd $SRCDIR
82+
tar xf libsndfile-1.2.2.tar.xz
83+
cd libsndfile-1.2.2
5784
autoconfbuild --disable-shared
5885

5986
cd $SRCDIR/zlib-1.2.11.dfsg

tests/testwavdata.cc

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <assert.h>
66
#include <math.h>
7+
#include <cstring>
78

89
using namespace SpectMorph;
910

@@ -129,8 +130,36 @@ run_tests (WavData::OutFormat format, string ext)
129130
}
130131

131132
int
132-
main()
133+
main (int argc, char **argv)
133134
{
134-
run_tests (WavData::OutFormat::WAV, "wav");
135-
run_tests (WavData::OutFormat::FLAC, "flac");
135+
if (argc == 2 && strcmp (argv[1], "extensions") == 0)
136+
{
137+
for (auto ext : WavData::supported_extensions())
138+
printf ("%s\n", ext.c_str());
139+
}
140+
else if (argc == 3 && strcmp (argv[1], "load") == 0)
141+
{
142+
WavData wd;
143+
double start_time = get_time();
144+
bool ok = wd.load (argv[2]);
145+
double end_time = get_time();
146+
if (!ok)
147+
{
148+
printf ("ERROR: %s\n", wd.error_blurb());
149+
return 1;
150+
}
151+
else
152+
{
153+
assert (wd.n_values() % wd.n_channels() == 0);
154+
printf ("OK: n_frames: %zd\n", wd.n_values() / wd.n_channels());
155+
printf ("hash %s\n", sha1_hash ((const unsigned char *) wd.samples().data(), sizeof (float) * wd.samples().size()).c_str());
156+
printf ("%f Mvalues/s\n", wd.n_values() / 1000 / 1000 / (end_time - start_time));
157+
}
158+
}
159+
else
160+
{
161+
assert (argc == 1);
162+
run_tests (WavData::OutFormat::WAV, "wav");
163+
run_tests (WavData::OutFormat::FLAC, "flac");
164+
}
136165
}

0 commit comments

Comments
 (0)