Merge pull request #110 from tesselode/fixes
[wdl/wdl-ol.git] / WDL / lameencdec.cpp
blob743f77be1e61bdbad66ad09b5b555093c7bd65d2
1 /*
2 WDL - lameencdec.cpp
3 Copyright (C) 2005 and later Cockos Incorporated
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
27 #ifdef _WIN32
28 #include <windows.h>
29 #include <shlobj.h>
30 #else
31 #include <unistd.h>
32 #endif
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
38 #include "wdlcstring.h"
39 #include "lameencdec.h"
42 #ifdef __APPLE__
43 #include <Carbon/Carbon.h>
44 #endif
45 #ifndef _WIN32
46 #include <dlfcn.h>
47 #endif
49 typedef enum MPEG_mode_e {
50 STEREO = 0,
51 JOINT_STEREO,
52 DUAL_CHANNEL, /* LAME doesn't supports this! */
53 MONO,
54 NOT_SET,
55 MAX_INDICATOR /* Don't use this! It's used for sanity checks. */
56 } MPEG_mode;
58 typedef void *lame_t;
60 static struct {
61 int (*close)(lame_t);
62 lame_t (*init)();
63 int (*set_in_samplerate)(lame_t, int);
64 int (*set_num_channels)(lame_t,int);
65 int (*set_out_samplerate)(lame_t,int);
66 int (*set_quality)(lame_t,int);
67 int (*set_mode)(lame_t,MPEG_mode);
68 int (*set_brate)(lame_t, int);
69 int (*init_params)(lame_t);
70 int (*get_framesize)(lame_t);
72 int (*encode_buffer_float)(lame_t,
73 const float buffer_l [], /* PCM data for left channel */
74 const float buffer_r [], /* PCM data for right channel */
75 const int nsamples, /* number of samples per channel */
76 unsigned char* mp3buf, /* pointer to encoded MP3 stream */
77 const int mp3buf_size );
78 int (*encode_flush)(lame_t,unsigned char* mp3buf, int size);
80 // these are optional
81 int (*set_VBR)(lame_t, int);
82 int (*set_VBR_q)(lame_t, int);
83 int (*set_VBR_mean_bitrate_kbps)(lame_t, int);
84 int (*set_VBR_min_bitrate_kbps)(lame_t, int);
85 int (*set_VBR_max_bitrate_kbps)(lame_t, int);
86 size_t (*get_lametag_frame)(lame_t, unsigned char *, size_t);
87 const char *(*get_lame_version)();
88 } lame;
90 #if 1
91 #define LAME_DEBUG_LOADING(x)
92 #else
93 #define LAME_DEBUG_LOADING(x) OutputDebugString(x)
94 #endif
96 static char s_last_dll_file[128];
98 static bool tryLoadDLL2(const char *name)
100 #ifdef _WIN32
101 HINSTANCE dll = LoadLibrary(name);
102 #else
103 void *dll=dlopen(name,RTLD_NOW|RTLD_LOCAL);
104 #endif
106 if (!dll) return false;
108 LAME_DEBUG_LOADING("trying to load");
109 LAME_DEBUG_LOADING(name);
110 int errcnt = 0;
111 #ifdef _WIN32
112 #define GETITEM(x) if (NULL == (*(void **)&lame.x = GetProcAddress((HINSTANCE)dll,"lame_" #x))) errcnt++;
113 #define GETITEM_NP(x) if (NULL == (*(void **)&lame.x = GetProcAddress((HINSTANCE)dll, #x))) errcnt++;
114 #else
115 #define GETITEM(x) if (NULL == (*(void **)&lame.x = dlsym(dll,"lame_" #x))) errcnt++;
116 #define GETITEM_NP(x) if (NULL == (*(void **)&lame.x = dlsym(dll,#x))) errcnt++;
117 #endif
118 GETITEM(close)
119 GETITEM(init)
120 GETITEM(set_in_samplerate)
121 GETITEM(set_num_channels)
122 GETITEM(set_out_samplerate)
123 GETITEM(set_quality)
124 GETITEM(set_mode)
125 GETITEM(set_brate)
126 GETITEM(init_params)
127 GETITEM(get_framesize)
128 GETITEM(encode_buffer_float)
129 GETITEM(encode_flush)
131 int errcnt2 = errcnt;
132 GETITEM(set_VBR)
133 GETITEM(set_VBR_q)
134 GETITEM(set_VBR_mean_bitrate_kbps)
135 GETITEM(set_VBR_min_bitrate_kbps)
136 GETITEM(set_VBR_max_bitrate_kbps)
137 GETITEM(get_lametag_frame)
138 GETITEM_NP(get_lame_version)
139 #undef GETITEM
140 #undef GETITEM_NP
141 if (errcnt2)
143 memset(&lame, 0, sizeof(lame));
145 #ifdef _WIN32
146 FreeLibrary(dll);
147 #else
148 dlclose(dll);
149 #endif
150 return false;
153 LAME_DEBUG_LOADING("loaded normal mode");
155 lstrcpyn_safe(s_last_dll_file, name, sizeof(s_last_dll_file));
156 #ifdef _WIN32
157 // if (!strstr(name,"\\"))
158 GetModuleFileName(dll,s_last_dll_file, (DWORD)sizeof(s_last_dll_file));
159 #else
160 // if (!strstr(name,"/"))
162 Dl_info inf={0,};
163 dladdr((void*)lame.init,&inf);
164 if (inf.dli_fname)
165 lstrcpyn_safe(s_last_dll_file,inf.dli_fname,sizeof(s_last_dll_file));
167 #endif
169 return true;
174 void LameEncoder::InitDLL(const char *extrapath, bool forceRetry)
176 static int a;
177 if (a<0) return;
179 if (forceRetry) a=0;
180 else if (a > 30) return; // give up
182 a++;
184 char me[1024];
185 #ifdef _WIN32
186 const char *dll = "libmp3lame.dll";
187 #elif defined(__APPLE__)
188 const char *dll = "libmp3lame.dylib";
189 #else
190 const char *dll = "libmp3lame.so.0";
191 #endif
193 if (extrapath)
195 snprintf(me,sizeof(me),"%s%c%s",extrapath,WDL_DIRCHAR,dll);
196 if (tryLoadDLL2(me)) { a = -1; return; }
199 if (tryLoadDLL2(dll)) a=-1;
203 const char *LameEncoder::GetLibName() { return s_last_dll_file; }
205 const char *LameEncoder::GetInfo()
207 static char buf[128];
208 if (!CheckDLL()) return NULL;
209 const char *p = lame.get_lame_version ? lame.get_lame_version() : NULL;
210 if (p && *p)
212 snprintf(buf, sizeof(buf), "LAME %s", p);
213 return buf;
215 return "LAME ?.??";
218 int LameEncoder::CheckDLL() // returns 1 for lame API, 2 for Blade, 0 for none
220 InitDLL();
221 if (!lame.close||
222 !lame.init||
223 !lame.set_in_samplerate||
224 !lame.set_num_channels||
225 !lame.set_out_samplerate||
226 !lame.set_quality||
227 !lame.set_mode||
228 !lame.set_brate||
229 !lame.init_params||
230 !lame.get_framesize||
231 !lame.encode_buffer_float||
232 !lame.encode_flush)
234 return 0;
237 return 1;
241 LameEncoder::LameEncoder(int srate, int nch, int bitrate, int stereomode, int quality, int vbrmethod, int vbrquality, int vbrmax, int abr)
243 m_lamestate=0;
244 if (!CheckDLL())
246 errorstat=1;
247 return;
250 errorstat=0;
251 m_nch=nch;
252 m_encoder_nch = stereomode == 3 ? 1 : m_nch;
255 m_lamestate=lame.init();
256 if (!m_lamestate)
258 errorstat=1;
259 return;
262 lame.set_in_samplerate(m_lamestate, srate);
263 lame.set_num_channels(m_lamestate,m_encoder_nch);
264 int outrate=srate;
266 int maxbr = (vbrmethod != -1 ? vbrmax : bitrate);
267 if (outrate>=32000 && maxbr <= 32*m_encoder_nch) outrate/=2;
269 lame.set_out_samplerate(m_lamestate,outrate);
270 lame.set_quality(m_lamestate,(quality>9 ||quality<0) ? 0 : quality);
271 lame.set_mode(m_lamestate,(MPEG_mode) (m_encoder_nch==1?3 :stereomode ));
272 lame.set_brate(m_lamestate,bitrate);
274 //int vbrmethod (-1 no vbr), int vbrquality (nVBRQuality), int vbrmax, int abr
275 if (vbrmethod != -1 && lame.set_VBR)
277 int vm=4; // mtrh
278 if (vbrmethod == 4) vm = 3; //ABR
279 lame.set_VBR(m_lamestate,vm);
281 if (lame.set_VBR_q) lame.set_VBR_q(m_lamestate,vbrquality);
283 if (vbrmethod == 4&&lame.set_VBR_mean_bitrate_kbps)
285 lame.set_VBR_mean_bitrate_kbps(m_lamestate,abr);
287 if (lame.set_VBR_max_bitrate_kbps)
289 lame.set_VBR_max_bitrate_kbps(m_lamestate,vbrmax);
291 if (lame.set_VBR_min_bitrate_kbps)
293 lame.set_VBR_min_bitrate_kbps(m_lamestate,bitrate);
296 lame.init_params(m_lamestate);
297 in_size_samples=lame.get_framesize(m_lamestate);
299 outtmp.Resize(65536);
302 void LameEncoder::Encode(float *in, int in_spls, int spacing)
304 if (errorstat) return;
306 if (in_spls > 0)
308 if (m_nch > 1 && m_encoder_nch==1)
310 // downmix
311 int x;
312 int pos=0;
313 int adv=2*spacing;
314 for (x = 0; x < in_spls; x ++)
316 float f=in[pos]+in[pos+1];
317 f*=16383.5f;
318 spltmp[0].Add(&f,sizeof(float));
319 pos+=adv;
322 else if (m_encoder_nch > 1) // deinterleave
324 int x;
325 int pos=0;
326 int adv=2*spacing;
327 for (x = 0; x < in_spls; x ++)
329 float f=in[pos];
330 f*=32767.0f;
331 spltmp[0].Add(&f,sizeof(float));
333 f=in[pos+1];
334 f*=32767.0f;
335 spltmp[1].Add(&f,sizeof(float));
337 pos+=adv;
340 else
342 int x;
343 int pos=0;
344 for (x = 0; x < in_spls; x ++)
346 float f=in[pos];
347 f*=32767.0f;
348 spltmp[0].Add(&f,sizeof(float));
350 pos+=spacing;
354 for (;;)
356 int a = spltmp[0].Available()/sizeof(float);
357 if (a >= in_size_samples) a = in_size_samples;
358 else if (a<1 || in_spls>0) break; // not enough samples available, and not flushing
360 int dwo=lame.encode_buffer_float(m_lamestate,(float *)spltmp[0].Get(),(float*)spltmp[m_encoder_nch>1].Get(), a,(unsigned char *)outtmp.Get(),outtmp.GetSize());
361 outqueue.Add(outtmp.Get(),dwo);
362 spltmp[0].Advance(a*sizeof(float));
363 if (m_encoder_nch > 1) spltmp[1].Advance(a*sizeof(float));
366 if (in_spls<1)
368 int a=lame.encode_flush(m_lamestate,(unsigned char *)outtmp.Get(),outtmp.GetSize());
369 if (a>0) outqueue.Add(outtmp.Get(),a);
374 spltmp[0].Compact();
375 spltmp[1].Compact();
379 #ifdef _WIN32
381 static BOOL HasUTF8(const char *_str)
383 const unsigned char *str = (const unsigned char *)_str;
384 if (!str) return FALSE;
385 while (*str)
387 unsigned char c = *str;
388 if (c >= 0xC2) // discard overlongs
390 if (c <= 0xDF && str[1] >=0x80 && str[1] <= 0xBF) return TRUE;
391 else if (c <= 0xEF && str[1] >=0x80 && str[1] <= 0xBF && str[2] >=0x80 && str[2] <= 0xBF) return TRUE;
392 else if (c <= 0xF4 && str[1] >=0x80 && str[1] <= 0xBF && str[2] >=0x80 && str[2] <= 0xBF) return TRUE;
394 str++;
396 return FALSE;
398 #endif
400 LameEncoder::~LameEncoder()
402 if (m_lamestate)
404 if (m_vbrfile.Get()[0] && lame.get_lametag_frame)
406 unsigned char buf[16384];
407 size_t a=lame.get_lametag_frame(m_lamestate,buf,sizeof(buf));
408 if (a>0 && a<=sizeof(buf))
410 FILE *fp=NULL;
411 #ifdef _WIN32
412 if (HasUTF8(m_vbrfile.Get()) && GetVersion()<0x80000000)
414 WCHAR wf[2048];
415 if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,m_vbrfile.Get(),-1,wf,2048))
417 fp = _wfopen(wf,L"r+b");
420 #endif
421 if (!fp) fp = fopen(m_vbrfile.Get(),"r+b");
422 if (fp)
424 fseek(fp,0,SEEK_SET);
425 fwrite(buf,1,a,fp);
426 fclose(fp);
430 lame.close(m_lamestate);
431 m_lamestate=0;