1 // LAME test program
2 //
3 // Copyright (c) 2010 Robert Hegemann
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the
17 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 // Boston, MA 02111-1307, USA.
19 
20 #include <lame.h>
21 #include <wchar.h>
22 #include <stdlib.h>
23 
24 
25 class PcmGenerator
26 {
27   float* m_buffer_ch0;
28   float* m_buffer_ch1;
29   int m_size;
30   float m_a;
31   float m_b;
32 
random()33   static double random()
34   {
35     int const range_max = 32768;
36     int const range_min = -32767;
37     return (double)rand() / (RAND_MAX + 1) * (range_max - range_min) + range_min;
38   }
39 
40   PcmGenerator( PcmGenerator const& );
41   PcmGenerator& operator = ( PcmGenerator const& );
42 
43 public:
44 
PcmGenerator(int size)45   explicit PcmGenerator(int size)
46   {
47     m_size = size >= 0 ? size : 0;
48     m_buffer_ch0 = new float [m_size];
49     m_buffer_ch1 = new float [m_size];
50     m_a = 0;
51     m_b = 0;
52     advance(0);
53   }
54 
~PcmGenerator()55   ~PcmGenerator()
56   {
57     delete[] m_buffer_ch0;
58     delete[] m_buffer_ch1;
59   }
60 
ch0() const61   float const* ch0() const { return m_buffer_ch0; }
ch1() const62   float const* ch1() const { return m_buffer_ch1; }
63 
advance( int x )64   void advance( int x ) {
65     float a = m_a;
66     float b = m_b;
67     for (int i = 0; i < m_size; ++i) {
68       a += 10;
69       if (a > 32768) a = random();
70       b -= 10;
71       if (b < -32767) b = random();
72       m_buffer_ch0[i] = a;
73       m_buffer_ch1[i] = b;
74     }
75     m_a = a;
76     m_b = b;
77   }
78 };
79 
80 class OutFile
81 {
82   FILE* m_file_handle;
83 
84 public:
OutFile()85   OutFile()
86     : m_file_handle(0)
87   {}
88 
OutFile(wchar_t const* filename)89   explicit OutFile(wchar_t const* filename)
90     : m_file_handle(0)
91   {
92     m_file_handle = _wfopen(filename, L"wb");
93   }
94 
~OutFile()95   ~OutFile()
96   {
97     close();
98   }
99 
isOpen() const100   bool isOpen() const {
101     return 0 != m_file_handle;
102   }
103 
close()104   void close() {
105     if (isOpen()) {
106       fclose(m_file_handle);
107       m_file_handle = 0;
108     }
109   }
110 
write(unsigned char const* data, int n)111   void write(unsigned char const* data, int n) {
112     fwrite(data, 1, n, m_file_handle);
113   }
114 };
115 
116 class Lame
117 {
118   lame_t m_gf;
119   bool m_init_params_called;
120 
ensureInitialized()121   void ensureInitialized() {
122     if (isOpen()) {
123       if (!m_init_params_called) {
124         m_init_params_called = true;
125         lame_init_params(m_gf);
126       }
127     }
128   }
129 
130 public:
131 
Lame()132   Lame()
133     : m_gf( lame_init() )
134     , m_init_params_called( false )
135   {}
136 
~Lame()137   ~Lame()
138   {
139     close();
140   }
141 
close()142   void close() {
143     if (isOpen()) {
144       lame_close(m_gf);
145       m_gf = 0;
146     }
147   }
148 
isOpen() const149   bool isOpen() const {
150     return m_gf != 0;
151   }
152 
operator lame_t() const153   operator lame_t () const {
154     return m_gf;
155   }
156 
setInSamplerate( int rate )157   void setInSamplerate( int rate ) {
158     lame_set_in_samplerate(m_gf, rate);
159   }
160 
setOutSamplerate( int rate )161   void setOutSamplerate( int rate ) {
162     lame_set_out_samplerate(m_gf, rate);
163   }
164 
setNumChannels( int num_channel )165   void setNumChannels( int num_channel ) {
166     lame_set_num_channels(m_gf, num_channel);
167   }
168 
encode(float const* ch0, float const* ch1, int n_in, unsigned char* out_buffer, int m_out_free)169   int encode(float const* ch0, float const* ch1, int n_in, unsigned char* out_buffer, int m_out_free) {
170     ensureInitialized();
171     return lame_encode_buffer_float(m_gf, ch0, ch1, n_in, out_buffer, m_out_free);
172   }
173 
flush(unsigned char* out_buffer, int m_out_free)174   int flush(unsigned char* out_buffer, int m_out_free) {
175     ensureInitialized();
176     return lame_encode_flush(m_gf, out_buffer, m_out_free);
177   }
178 
getLameTag(unsigned char* out_buffer, int m_out_free)179   int getLameTag(unsigned char* out_buffer, int m_out_free) {
180     ensureInitialized();
181     return lame_get_lametag_frame(m_gf, out_buffer, m_out_free);
182   }
183 
184 };
185 
186 class OutBuffer
187 {
188   unsigned char* m_data;
189   int m_size;
190   int m_used;
191 
192   OutBuffer( OutBuffer const& );
193   OutBuffer& operator = ( OutBuffer const& );
194 
195 public:
196 
OutBuffer()197   OutBuffer()
198   {
199     m_size = 1000 * 1000;
200     m_data = new unsigned char[ m_size ];
201     m_used = 0;
202   }
203 
~OutBuffer()204   ~OutBuffer()
205   {
206     delete[] m_data;
207   }
208 
advance( int i )209   void advance( int i ) {
210     m_used += i;
211   }
212 
used() const213   int used() const {
214     return m_used;
215   }
216 
unused() const217   int unused() const {
218     return m_size - m_used;
219   }
220 
current()221   unsigned char* current() { return m_data + m_used; }
begin()222   unsigned char* begin()   { return m_data; }
223 };
224 
generateFile(wchar_t const* filename, size_t n)225 void generateFile(wchar_t const* filename, size_t n)
226 {
227   int const chunk = 1152;
228   PcmGenerator src(chunk);
229 
230   OutFile mp3_stream( filename );
231   if (!mp3_stream.isOpen()) return;
232 
233   Lame lame;
234   if (!lame.isOpen()) return;
235 
236   OutBuffer mp3_stream_buffer;
237   int rc = 0;
238 
239   lame.setInSamplerate(44100);
240   lame.setOutSamplerate(44100);
241   lame.setNumChannels(2);
242 
243   while (n > 0) {
244     int const m = n < chunk ? n : chunk;
245     if ( n < chunk ) n = 0; else n -= chunk;
246     rc = lame.encode(src.ch0(), src.ch1(), m, mp3_stream_buffer.current(), mp3_stream_buffer.unused());
247     wprintf(L"rc=%d %d %d\n",rc,mp3_stream_buffer.used(),mp3_stream_buffer.unused());
248     if (rc < 0) return;
249     mp3_stream_buffer.advance( rc );
250     src.advance(m);
251   }
252 
253   rc = lame.flush(mp3_stream_buffer.current(), mp3_stream_buffer.unused());
254   wprintf(L"flush rc=%d\n",rc);
255   if (rc < 0) return;
256 
257   mp3_stream_buffer.advance( rc );
258 
259   int lametag_size = lame.getLameTag(0,0);
260   wprintf(L"lametag_size=%d\n",lametag_size);
261 
262   rc = lame.getLameTag(mp3_stream_buffer.begin(), lametag_size);
263   wprintf(L"rc=%d\n",rc);
264   if (rc < 0) return;
265 
266   mp3_stream.write(mp3_stream_buffer.begin(), mp3_stream_buffer.used());
267 
268   lame.close();
269 }
270 
271 int wmain(int argc, wchar_t** argv)
272 {
273   if (argc != 3) {
274     wprintf(L"usage: %ws <filename> <number pcm samples>\n", argv[0]);
275     return -1;
276   }
277   wprintf(L"open file %ws\n", argv[1]);
278   int n = _wtoi(argv[2]);
279   wprintf(L"synthesize %d samples long mp3 file\n",n);
280   generateFile(argv[1], n);
281   return 0;
282 }
283