Project

General

Profile

plugin.cc

New version source - Jim Turner, June 10, 2022 03:57

 
1
/*  Audacious - Cross-platform multimedia player
2
 *  Copyright (C) 2005-2011 Audacious development team
3
 *
4
 *  Based on the xmms_sndfile input plugin:
5
 *  Copyright (C) 2000, 2002 Erik de Castro Lopo
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, write to the Free Software
19
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
 */
21

    
22
#include <stdlib.h>
23
#include <sndfile.h>
24

    
25
#define WANT_VFS_STDIO_COMPAT
26
#include <libaudcore/plugin.h>
27
#include <libaudcore/i18n.h>
28
#include <libaudcore/audstrings.h>
29

    
30
class SndfilePlugin : public InputPlugin
31
{
32
public:
33
    static const char about[];
34
    static const char * const exts[];
35
    static const char * const mimes[];
36

    
37
    static constexpr PluginInfo info = {
38
        N_("Sndfile Plugin"),
39
        PACKAGE,
40
        about
41
    };
42

    
43
    constexpr SndfilePlugin () : InputPlugin (info, InputInfo ()
44
        .with_priority (9)  /* low priority fallback (but before ffaudio) */
45
        .with_exts (exts)
46
        .with_mimes (mimes)) {}
47

    
48
    bool is_our_file (const char * filename, VFSFile & file);
49
    bool read_tag (const char * filename, VFSFile & file, Tuple & tuple, Index<char> * image);
50
    bool play (const char * filename, VFSFile & file);
51
};
52

    
53
EXPORT SndfilePlugin aud_plugin_instance;
54

    
55
/* Virtual file access wrappers for libsndfile
56
 */
57
static sf_count_t
58
sf_get_filelen (void *user_data)
59
{
60
    int64_t size = ((VFSFile *) user_data)->fsize ();
61
    return (size < 0) ? SF_COUNT_MAX : size;
62
}
63

    
64
static sf_count_t
65
sf_vseek (sf_count_t offset, int whence, void *user_data)
66
{
67
    if (((VFSFile *) user_data)->fseek (offset, to_vfs_seek_type (whence)) != 0)
68
        return -1;
69

    
70
    return ((VFSFile *) user_data)->ftell ();
71
}
72

    
73
static sf_count_t
74
sf_vseek_dummy (sf_count_t offset, int whence, void *user_data)
75
{
76
    return -1;
77
}
78

    
79
static sf_count_t
80
sf_vread (void *ptr, sf_count_t count, void *user_data)
81
{
82
    return ((VFSFile *) user_data)->fread (ptr, 1, count);
83
}
84

    
85
static sf_count_t
86
sf_vwrite_dummy (const void *ptr, sf_count_t count, void *user_data)
87
{
88
    return 0;
89
}
90

    
91
static sf_count_t
92
sf_tell (void *user_data)
93
{
94
    return ((VFSFile *) user_data)->ftell ();
95
}
96

    
97
static SF_VIRTUAL_IO sf_virtual_io =
98
{
99
    sf_get_filelen,
100
    sf_vseek,
101
    sf_vread,
102
    sf_vwrite_dummy,
103
    sf_tell
104
};
105

    
106
static SF_VIRTUAL_IO sf_virtual_io_stream =
107
{
108
    sf_get_filelen,
109
    sf_vseek_dummy,
110
    sf_vread,
111
    sf_vwrite_dummy,
112
    sf_tell
113
};
114

    
115
static void copy_string (SNDFILE * sf, int sf_id, Tuple & tup, Tuple::Field field)
116
{
117
    const char * str = sf_get_string (sf, sf_id);
118
    if (str)
119
        tup.set_str (field, str);
120
}
121

    
122
static void copy_int (SNDFILE * sf, int sf_id, Tuple & tup, Tuple::Field field)
123
{
124
    const char * str = sf_get_string (sf, sf_id);
125
    if (str && atoi (str))
126
        tup.set_int (field, atoi (str));
127
}
128

    
129
bool SndfilePlugin::read_tag (const char * filename, VFSFile & file, Tuple & tuple,
130
 Index<char> * image)
131
{
132
    SF_INFO sfinfo {}; // must be zeroed before sf_open()
133
    const char *format, *subformat;
134

    
135
    bool stream = (file.fsize () < 0);
136
    SNDFILE * sndfile = sf_open_virtual (stream ? & sf_virtual_io_stream :
137
     & sf_virtual_io, SFM_READ, & sfinfo, & file);
138

    
139
    if (! sndfile)
140
        return false;
141

    
142
    copy_string (sndfile, SF_STR_TITLE, tuple, Tuple::Title);
143
    copy_string (sndfile, SF_STR_ARTIST, tuple, Tuple::Artist);
144
    copy_string (sndfile, SF_STR_ALBUM, tuple, Tuple::Album);
145
    copy_string (sndfile, SF_STR_COMMENT, tuple, Tuple::Comment);
146
    copy_string (sndfile, SF_STR_GENRE, tuple, Tuple::Genre);
147
    copy_int (sndfile, SF_STR_DATE, tuple, Tuple::Year);
148
    copy_int (sndfile, SF_STR_TRACKNUMBER, tuple, Tuple::Track);
149

    
150
    sf_close (sndfile);
151

    
152
    if (sfinfo.samplerate > 0)
153
    {
154
        int64_t length = aud::rescale<int64_t> (sfinfo.frames, sfinfo.samplerate, 1000);
155
        tuple.set_int (Tuple::Length, length);
156
    }
157

    
158
    switch (sfinfo.format & SF_FORMAT_TYPEMASK)
159
    {
160
        case SF_FORMAT_WAV:
161
        case SF_FORMAT_WAVEX:
162
            format = "Microsoft WAV";
163
            break;
164
        case SF_FORMAT_AIFF:
165
            format = "Apple/SGI AIFF";
166
            break;
167
        case SF_FORMAT_AU:
168
            format = "Sun/NeXT AU";
169
            break;
170
        case SF_FORMAT_RAW:
171
            format = "Raw PCM data";
172
            break;
173
        case SF_FORMAT_PAF:
174
            format = "Ensoniq PARIS";
175
            break;
176
        case SF_FORMAT_SVX:
177
            format = "Amiga IFF / SVX8 / SV16";
178
            break;
179
        case SF_FORMAT_NIST:
180
            format = "Sphere NIST";
181
            break;
182
        case SF_FORMAT_VOC:
183
            format = "Creative VOC";
184
            break;
185
        case SF_FORMAT_IRCAM:
186
            format = "Berkeley/IRCAM/CARL";
187
            break;
188
        case SF_FORMAT_W64:
189
            format = "Sonic Foundry's 64 bit RIFF/WAV";
190
            break;
191
        case SF_FORMAT_MAT4:
192
            format = "Matlab (tm) V4.2 / GNU Octave 2.0";
193
            break;
194
        case SF_FORMAT_MAT5:
195
            format = "Matlab (tm) V5.0 / GNU Octave 2.1";
196
            break;
197
        case SF_FORMAT_PVF:
198
            format = "Portable Voice Format";
199
            break;
200
        case SF_FORMAT_XI:
201
            format = "Fasttracker 2 Extended Instrument";
202
            break;
203
        case SF_FORMAT_HTK:
204
            format = "HMM Tool Kit";
205
            break;
206
        case SF_FORMAT_SDS:
207
            format = "Midi Sample Dump Standard";
208
            break;
209
        case SF_FORMAT_AVR:
210
            format = "Audio Visual Research";
211
            break;
212
        case SF_FORMAT_SD2:
213
            format = "Sound Designer 2";
214
            break;
215
        case SF_FORMAT_FLAC:
216
            format = "Free Lossless Audio Codec";
217
            break;
218
        case SF_FORMAT_CAF:
219
            format = "Core Audio File";
220
            break;
221
        default:
222
            format = "Unknown sndfile";
223
    }
224

    
225
    switch (sfinfo.format & SF_FORMAT_SUBMASK)
226
    {
227
        case SF_FORMAT_PCM_S8:
228
            subformat = "signed 8 bit";
229
            break;
230
        case SF_FORMAT_PCM_16:
231
            subformat = "signed 16 bit";
232
            break;
233
        case SF_FORMAT_PCM_24:
234
            subformat = "signed 24 bit";
235
            break;
236
        case SF_FORMAT_PCM_32:
237
            subformat = "signed 32 bit";
238
            break;
239
        case SF_FORMAT_PCM_U8:
240
            subformat = "unsigned 8 bit";
241
            break;
242
        case SF_FORMAT_FLOAT:
243
            subformat = "32 bit float";
244
            break;
245
        case SF_FORMAT_DOUBLE:
246
            subformat = "64 bit float";
247
            break;
248
        case SF_FORMAT_ULAW:
249
            subformat = "U-Law";
250
            break;
251
        case SF_FORMAT_ALAW:
252
            subformat = "A-Law";
253
            break;
254
        case SF_FORMAT_IMA_ADPCM:
255
            subformat = "IMA ADPCM";
256
            break;
257
        case SF_FORMAT_MS_ADPCM:
258
            subformat = "MS ADPCM";
259
            break;
260
        case SF_FORMAT_GSM610:
261
            subformat = "GSM 6.10";
262
            break;
263
        case SF_FORMAT_VOX_ADPCM:
264
            subformat = "Oki Dialogic ADPCM";
265
            break;
266
        case SF_FORMAT_G721_32:
267
            subformat = "32kbs G721 ADPCM";
268
            break;
269
        case SF_FORMAT_G723_24:
270
            subformat = "24kbs G723 ADPCM";
271
            break;
272
        case SF_FORMAT_G723_40:
273
            subformat = "40kbs G723 ADPCM";
274
            break;
275
        case SF_FORMAT_DWVW_12:
276
            subformat = "12 bit Delta Width Variable Word";
277
            break;
278
        case SF_FORMAT_DWVW_16:
279
            subformat = "16 bit Delta Width Variable Word";
280
            break;
281
        case SF_FORMAT_DWVW_24:
282
            subformat = "24 bit Delta Width Variable Word";
283
            break;
284
        case SF_FORMAT_DWVW_N:
285
            subformat = "N bit Delta Width Variable Word";
286
            break;
287
        case SF_FORMAT_DPCM_8:
288
            subformat = "8 bit differential PCM";
289
            break;
290
        case SF_FORMAT_DPCM_16:
291
            subformat = "16 bit differential PCM";
292
            break;
293
        default:
294
            subformat = nullptr;
295
    }
296

    
297
    if (subformat != nullptr)
298
        tuple.set_format (str_printf ("%s (%s)", format, subformat),
299
         sfinfo.channels, sfinfo.samplerate, 0);
300
    else
301
        tuple.set_format (format, sfinfo.channels, sfinfo.samplerate, 0);
302

    
303
    return true;
304
}
305

    
306
bool SndfilePlugin::play (const char * filename, VFSFile & file)
307
{
308
    SF_INFO sfinfo {}; // must be zeroed before sf_open()
309

    
310
    bool stream = (file.fsize () < 0);
311
    SNDFILE * sndfile = sf_open_virtual (stream ? & sf_virtual_io_stream :
312
     & sf_virtual_io, SFM_READ, & sfinfo, & file);
313

    
314
    if (sndfile == nullptr)
315
        return false;
316

    
317
    open_audio (FMT_FLOAT, sfinfo.samplerate, sfinfo.channels);
318

    
319
    Index<float> buffer;
320
    buffer.resize (sfinfo.channels * (sfinfo.samplerate / 50));
321

    
322
    while (! check_stop ())
323
    {
324
        int seek_value = check_seek ();
325
        if (seek_value != -1)
326
        {
327
            int64_t frames = aud::rescale<int64_t> (seek_value, 1000, sfinfo.samplerate);
328
            sf_seek (sndfile, aud::min (frames, (int64_t) sfinfo.frames), SEEK_SET);
329
        }
330

    
331
        int samples = sf_read_float (sndfile, buffer.begin (), buffer.len ());
332
        if (! samples)
333
            break;
334

    
335
        write_audio (buffer.begin (), sizeof (float) * samples);
336
    }
337

    
338
    sf_close (sndfile);
339

    
340
    return true;
341
}
342

    
343
/* Function to see if WAV file contains embedded DTS format (contains a DTS header):
344
   (Borrowed & modified from:  https://hydrogenaud.io/index.php?PHPSESSID=0r361d02rgjc1uo2evsr91acue&action=dlattach;topic=94988.0;attach=10766)
345
   (See Reply# 19 in:  https://hydrogenaud.io/index.php?topic=94988.0)
346
   Function returns FALSE if DTS header found (DTS-WAV file which libsndfile accepts but can't play).
347
*/
348
bool SearchForDTSHeader (VFSFile & file) {
349
    unsigned int i;
350
    unsigned char next6bytes[7];
351
    unsigned long ScanSize = file.fsize () - 12;
352

    
353
    if (ScanSize > 99999)
354
        ScanSize = 99999;  /* Limit search to first 99999 bytes, per original program. */
355

    
356
    for (i=0; i<ScanSize; i++) {
357
        if (file.fseek ((long)i, VFS_SEEK_SET) < 0)
358
            return true;  /* Couldn't seek, so assume NOT DTS. */
359

    
360
        if (file.fread (next6bytes, 6, 1))
361
        {
362
            if (next6bytes[0] == 0xFF && next6bytes[1] == 0x1F
363
                    && next6bytes[2] == 0x00
364
                    && next6bytes[3] == 0xE8
365
                    && (next6bytes[4] & 0xF0) == 0xF0
366
                    && next6bytes[5] == 0x07)
367
                return false;  /* Header found (we're DTS format embedded in a WAV file), let ffaudio handle! */
368
        }
369
        else
370
            return true;  /* Couldn't read enough, so assume NOT DTS. */
371
    }
372
    return true;  /* We're a standard WAV file, so handle ourselves. */
373
}
374

    
375
bool SndfilePlugin::is_our_file (const char * filename, VFSFile & file)
376
{
377
        bool ok = true;
378
    SF_INFO tmp_sfinfo {}; // must be zeroed before sf_open()
379

    
380
    /* Have to open the file virtually as a sndfile to see if libsndfile can handle it. */
381
    bool stream = (file.fsize () < 0);
382
    SNDFILE * tmp_sndfile = sf_open_virtual (stream ? & sf_virtual_io_stream :
383
     & sf_virtual_io, SFM_READ, & tmp_sfinfo, & file);
384

    
385
    if (! tmp_sndfile)
386
        return false;
387

    
388
    if (file.fsize () > 12)  /* We're not a stream, so check if DTS, if so, punt to ffaudio!: */
389
               ok = SearchForDTSHeader (file);
390

    
391
    /* It can so close file and return true (unless embedded DTS). */
392
    sf_close (tmp_sndfile);
393
    tmp_sndfile = nullptr;
394

    
395
    return ok;
396
}
397

    
398
const char SndfilePlugin::about[] =
399
 N_("Based on the xmms_sndfile plugin:\n"
400
    "Copyright (C) 2000, 2002 Erik de Castro Lopo\n\n"
401
    "Adapted for Audacious by Tony Vroon <chainsaw@gentoo.org>\n\n"
402
    "This program is free software; you can redistribute it and/or "
403
    "modify it under the terms of the GNU General Public License "
404
    "as published by the Free Software Foundation; either version 2 "
405
    "of the License, or (at your option) any later version.\n\n"
406
    "This program is distributed in the hope that it will be useful, "
407
    "but WITHOUT ANY WARRANTY; without even the implied warranty of "
408
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
409
    "GNU General Public License for more details.\n\n"
410
    "You should have received a copy of the GNU General Public License "
411
    "along with this program; if not, write to the Free Software "
412
    "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.");
413

    
414
const char * const SndfilePlugin::exts[] = { "aiff", "au", "raw", "wav", nullptr };
415
const char * const SndfilePlugin::mimes[] = { "audio/wav", "audio/x-wav", nullptr };