1 //  sndfile helper functions
2 //
3 //  Copyright (c) 2002 James McCartney. All rights reserved.
4 //  Copyright (C) 2012 Tim Blechmann
5 //  Copyright (C) 2017 Brian Heim
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; see the file COPYING.  If not, write to
19 //  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 //  Boston, MA 02111-1307, USA.
21 
22 #pragma once
23 
24 #include "SC_Errors.h"
25 
26 #ifndef NO_LIBSNDFILE
27 
28 // on Windows, enable Windows libsndfile prototypes in order to access sf_wchar_open.
29 // See sndfile.h, lines 739-752. Note that order matters: this has to be the first include of sndfile.h
30 #    ifdef _WIN32
31 #        include "SC_Codecvt.hpp" // utf8_cstr_to_utf16_wstring
32 #        include <windows.h>
33 #        define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1
34 #    endif // _WIN32
35 #    include <sndfile.h>
36 #    include <sndfile.hh>
37 
38 #    include "string.h"
39 
40 #    include <boost/algorithm/string/predicate.hpp> // iequals
41 
42 using boost::iequals;
43 
headerFormatFromString(const char * name)44 static inline int headerFormatFromString(const char* name) {
45     if (!name)
46         return SF_FORMAT_AIFF;
47     if (iequals(name, "AIFF"))
48         return SF_FORMAT_AIFF;
49     if (iequals(name, "AIFC"))
50         return SF_FORMAT_AIFF;
51     if (iequals(name, "RIFF"))
52         return SF_FORMAT_WAV;
53     if (iequals(name, "WAVEX"))
54         return SF_FORMAT_WAVEX;
55     if (iequals(name, "WAVE"))
56         return SF_FORMAT_WAV;
57     if (iequals(name, "WAV"))
58         return SF_FORMAT_WAV;
59     if (iequals(name, "Sun"))
60         return SF_FORMAT_AU;
61     if (iequals(name, "IRCAM"))
62         return SF_FORMAT_IRCAM;
63     if (iequals(name, "NeXT"))
64         return SF_FORMAT_AU;
65     if (iequals(name, "raw"))
66         return SF_FORMAT_RAW;
67     if (iequals(name, "MAT4"))
68         return SF_FORMAT_MAT4;
69     if (iequals(name, "MAT5"))
70         return SF_FORMAT_MAT5;
71     if (iequals(name, "PAF"))
72         return SF_FORMAT_PAF;
73     if (iequals(name, "SVX"))
74         return SF_FORMAT_SVX;
75     if (iequals(name, "NIST"))
76         return SF_FORMAT_NIST;
77     if (iequals(name, "VOC"))
78         return SF_FORMAT_VOC;
79     if (iequals(name, "W64"))
80         return SF_FORMAT_W64;
81     if (iequals(name, "PVF"))
82         return SF_FORMAT_PVF;
83     if (iequals(name, "XI"))
84         return SF_FORMAT_XI;
85     if (iequals(name, "HTK"))
86         return SF_FORMAT_HTK;
87     if (iequals(name, "SDS"))
88         return SF_FORMAT_SDS;
89     if (iequals(name, "AVR"))
90         return SF_FORMAT_AVR;
91     if (iequals(name, "SD2"))
92         return SF_FORMAT_SD2;
93     if (iequals(name, "FLAC"))
94         return SF_FORMAT_FLAC;
95 // TODO allow other platforms to know vorbis once libsndfile 1.0.18 is established
96 #    if defined(__APPLE__) || defined(_WIN32) || LIBSNDFILE_1018
97     if (iequals(name, "vorbis"))
98         return SF_FORMAT_VORBIS;
99 #    endif
100     if (iequals(name, "CAF"))
101         return SF_FORMAT_CAF;
102     if (iequals(name, "RF64"))
103         return SF_FORMAT_RF64;
104     return 0;
105 }
106 
sampleFormatFromString(const char * name)107 static inline int sampleFormatFromString(const char* name) {
108     if (!name)
109         return SF_FORMAT_PCM_16;
110 
111     size_t len = strlen(name);
112     if (len < 1)
113         return 0;
114 
115     if (name[0] == 'u') {
116         if (len < 5)
117             return 0;
118         if (name[4] == '8')
119             return SF_FORMAT_PCM_U8; // uint8
120         return 0;
121     } else if (name[0] == 'i') {
122         if (len < 4)
123             return 0;
124         if (name[3] == '8')
125             return SF_FORMAT_PCM_S8; // int8
126         else if (name[3] == '1')
127             return SF_FORMAT_PCM_16; // int16
128         else if (name[3] == '2')
129             return SF_FORMAT_PCM_24; // int24
130         else if (name[3] == '3')
131             return SF_FORMAT_PCM_32; // int32
132     } else if (name[0] == 'f') {
133         return SF_FORMAT_FLOAT; // float
134     } else if (name[0] == 'd') {
135         return SF_FORMAT_DOUBLE; // double
136     } else if (name[0] == 'm' || name[0] == 'u') {
137         return SF_FORMAT_ULAW; // mulaw ulaw
138     } else if (name[0] == 'a') {
139         return SF_FORMAT_ALAW; // alaw
140     }
141     return 0;
142 }
143 
sndfileFormatInfoFromStrings(struct SF_INFO * info,const char * headerFormatString,const char * sampleFormatString)144 static inline int sndfileFormatInfoFromStrings(struct SF_INFO* info, const char* headerFormatString,
145                                                const char* sampleFormatString) {
146     int headerFormat = headerFormatFromString(headerFormatString);
147     if (!headerFormat)
148         return kSCErr_Failed;
149 
150     int sampleFormat = sampleFormatFromString(sampleFormatString);
151     if (!sampleFormat)
152         return kSCErr_Failed;
153 
154     info->format = (unsigned int)(headerFormat | sampleFormat);
155     return kSCErr_None;
156 }
157 
158 // ------------------------------ platform-specific functions ------------------------------
159 #    ifdef _WIN32
160 
sndfileOpen(LPCWSTR wpath,int mode,SF_INFO * sfinfo)161 inline SNDFILE* sndfileOpen(LPCWSTR wpath, int mode, SF_INFO* sfinfo) { return sf_wchar_open(wpath, mode, sfinfo); }
162 
163 // This safely opens a sound file using a raw cstring on any platform
sndfileOpenFromCStr(const char * path,int mode,SF_INFO * sfinfo)164 inline SNDFILE* sndfileOpenFromCStr(const char* path, int mode, SF_INFO* sfinfo) {
165     // convert to wchar first
166     const std::wstring path_w = SC_Codecvt::utf8_cstr_to_utf16_wstring(path);
167     return sndfileOpen(path_w.c_str(), mode, sfinfo);
168 }
169 
170 // Safely creates a handle using a raw cstring on any platform
makeSndfileHandle(const char * path,int mode=SFM_READ,int format=0,int channels=0,int samplerate=0)171 inline SndfileHandle makeSndfileHandle(const char* path, int mode = SFM_READ, int format = 0, int channels = 0,
172                                        int samplerate = 0) {
173     const std::wstring path_w = SC_Codecvt::utf8_cstr_to_utf16_wstring(path);
174     return SndfileHandle(path_w.c_str(), mode, format, channels, samplerate);
175 }
176 
177 #    else // not _WIN32
178 
sndfileOpen(const char * path,int mode,SF_INFO * sfinfo)179 inline SNDFILE* sndfileOpen(const char* path, int mode, SF_INFO* sfinfo) { return sf_open(path, mode, sfinfo); }
180 
181 // simple forward
sndfileOpenFromCStr(const char * path,int mode,SF_INFO * sfinfo)182 inline SNDFILE* sndfileOpenFromCStr(const char* path, int mode, SF_INFO* sfinfo) {
183     return sndfileOpen(path, mode, sfinfo);
184 }
185 
186 // simple forward
makeSndfileHandle(const char * path,int mode=SFM_READ,int format=0,int channels=0,int samplerate=0)187 inline SndfileHandle makeSndfileHandle(const char* path, int mode = SFM_READ, int format = 0, int channels = 0,
188                                        int samplerate = 0) {
189     return SndfileHandle(path, mode, format, channels, samplerate);
190 }
191 
192 #    endif // _WIN32
193 
194 #else // not NO_LIBSNDFILE
195 
sndfileFormatInfoFromStrings(struct SF_INFO * info,const char * headerFormatString,const char * sampleFormatString)196 static inline int sndfileFormatInfoFromStrings(struct SF_INFO* info, const char* headerFormatString,
197                                                const char* sampleFormatString) {
198     return kSCErr_Failed;
199 }
200 
201 #endif /* NO_LIBSNDFILE */
202