前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MP3 编码解码 附完整c代码

MP3 编码解码 附完整c代码

原创
作者头像
cpuimage
发布2018-08-05 22:14:28
2K0
发布2018-08-05 22:14:28
举报
文章被收录于专栏:算法+算法+算法+

近期一直不间断学习音频处理,一直也没想着要去碰音频编解码相关。

主要是觉得没什么实际的作用和意义。

不管视频编解码,图像编解码,音频编解码,都有很多组织基金在推动。

当然,在一些特定的情景下,需要用起来编解码库,

而一般这些库都会有编译困难,使用困难等等困难综合症。

图像方面,已经有stb_image,spot,freeimage等编解码库系列,做得特别赞。

https://github.com/nothings/stb/

https://github.com/r-lyeh-archived/spot

http://freeimage.sourceforge.net/index.html

当然有一段时间,jpeg的编码库也是个头疼的事情,直到tinyjpg的出现。

视频这块有libav,ffmpeg

https://libav.org/

https://ffmpeg.org/

而音频这块,就有点差强人意了。

当然dr_libs 也已经做了不少工作了。

https://github.com/mackron/dr_libs

可惜的是,他做了wav的编解码库,mp3的解码库,就是没有mp3的编码库。

而一般mp3 的编码库,大众使用最多的是lame

http://lame.sourceforge.net/

在一阵寻寻觅觅之后,俺找到了一个mp3的编码库。

其原官网已经成为历史资源了。

https://web.archive.org/web/20060102002813/http://www.everett9981.freeserve.co.uk/pete.htm

也是相当历史久远了。

也有人对其进行了回炉重造。

https://github.com/toots/shine

俺一直惦念着,找个时间,进行代码整合,blabla

秉承着简洁简单的态度,就这么新鲜出炉了。

在写示例代码的时候,踩了几个小坑。

贴上完整代码:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <time.h>
  5 #include "timing.h"
  6 #include "shine_mp3.h"
  7 
  8 #define DR_WAV_IMPLEMENTATION
  9 
 10 #include "dr_wav.h"
 11 
 12 #define DR_MP3_IMPLEMENTATION
 13 
 14 #include "dr_mp3.h"
 15 
 16 void error(char *s);
 17 
 18 
 19 int16_t *wavRead_int16(char *filename, uint32_t *sampleRate, uint32_t *channels, uint64_t *totalSampleCount) {
 20     int16_t *buffer = drwav_open_and_read_file_s16(filename, channels, sampleRate, totalSampleCount);
 21     if (buffer == NULL) {
 22         drmp3_config pConfig;
 23         float *mp3_buffer = drmp3_open_and_decode_file_f32(filename, &pConfig, totalSampleCount);
 24         if (mp3_buffer != NULL) {
 25             buffer = (int16_t *) calloc(*totalSampleCount, sizeof(int16_t));
 26             *channels = pConfig.outputChannels;
 27             *sampleRate = pConfig.outputSampleRate;
 28             if (buffer != NULL)
 29                 drwav_f32_to_s16(buffer, mp3_buffer, *totalSampleCount);
 30             free(mp3_buffer);
 31         } else {
 32             printf("read file [%s] error.\n", filename);
 33         }
 34     }
 35     return buffer;
 36 }
 37 
 38 
 39 /* Some global vars. */
 40 char *infname, *outfname;
 41 FILE *outfile;
 42 int quiet = 0;
 43 int stereo = STEREO;
 44 int force_mono = 0;
 45 
 46 /* Write out the MP3 file */
 47 int write_mp3(long bytes, void *buffer, void *config) {
 48     return fwrite(buffer, sizeof(unsigned char), bytes, outfile) / sizeof(unsigned char);
 49 }
 50 
 51 /* Output error message and exit */
 52 void error(char *s) {
 53     fprintf(stderr, "Error: %s\n", s);
 54     exit(1);
 55 }
 56 
 57 static void print_usage() {
 58     printf("Audio Processing\n");
 59     printf("mp3 encoder && decoder\n");
 60     printf("blog: http://cpuimage.cnblogs.com/\n");
 61     printf("Usage: mp3 encoder && decoder [options] <infile> <outfile>\n\n");
 62     printf("Use \"-\" for standard input or output.\n\n");
 63     printf("Options:\n");
 64     printf(" -h            this help message\n");
 65     printf(" -b <bitrate>  set the bitrate [8-320], default 64 kbit\n");
 66     printf(" -m            force encoder to operate in mono\n");
 67     printf(" -c            set copyright flag, default off\n");
 68     printf(" -j            encode in joint stereo (stereo data only)\n");
 69     printf(" -d            encode in dual-channel (stereo data only)\n");
 70     printf(" -q            quiet mode\n");
 71     printf(" -v            verbose mode\n");
 72 }
 73 
 74 /* Use these default settings, can be overridden */
 75 static void set_defaults(shine_config_t *config) {
 76     shine_set_config_mpeg_defaults(&config->mpeg);
 77 }
 78 
 79 /* Parse command line arguments */
 80 static int parse_command(int argc, char **argv, shine_config_t *config) {
 81     int i = 0;
 82 
 83     if (argc < 3) return 0;
 84 
 85     while (argv[++i][0] == '-' && argv[i][1] != '\000' && argv[i][1] != ' ')
 86         switch (argv[i][1]) {
 87             case 'b':
 88                 config->mpeg.bitr = atoi(argv[++i]);
 89                 break;
 90 
 91             case 'm':
 92                 force_mono = 1;
 93                 break;
 94 
 95             case 'j':
 96                 stereo = JOINT_STEREO;
 97                 break;
 98 
 99             case 'd':
100                 stereo = DUAL_CHANNEL;
101                 break;
102 
103             case 'c':
104                 config->mpeg.copyright = 1;
105                 break;
106 
107             case 'q':
108                 quiet = 1;
109                 break;
110 
111             case 'v':
112                 quiet = 0;
113                 break;
114 
115             case 'h':
116             default :
117                 return 0;
118         }
119 
120     if (argc - i != 2) return 0;
121     infname = argv[i++];
122     outfname = argv[i];
123     return 1;
124 }
125 
126 /* Print some info about what we're going to encode */
127 static void check_config(shine_config_t *config) {
128     static char *version_names[4] = {"2.5", "reserved", "II", "I"};
129     static char *mode_names[4] = {"stereo", "joint-stereo", "dual-channel", "mono"};
130     static char *demp_names[4] = {"none", "50/15us", "", "CITT"};
131 
132     printf("MPEG-%s layer III, %s  Psychoacoustic Model: Shine\n",
133            version_names[shine_check_config(config->wave.samplerate, config->mpeg.bitr)],
134            mode_names[config->mpeg.mode]);
135     printf("Bitrate: %d kbps  ", config->mpeg.bitr);
136     printf("De-emphasis: %s   %s %s\n",
137            demp_names[config->mpeg.emph],
138            ((config->mpeg.original) ? "Original" : ""),
139            ((config->mpeg.copyright) ? "(C)" : ""));
140     printf("Encoding \"%s\" to \"%s\"\n", infname, outfname);
141 }
142 
143 int main(int argc, char **argv) {
144     shine_config_t config;
145     shine_t s;
146     int written;
147     unsigned char *data;
148     /* Set the default MPEG encoding paramters - basically init the struct */
149     set_defaults(&config);
150 
151     if (!parse_command(argc, argv, &config)) {
152         print_usage();
153         exit(1);
154     }
155 
156     quiet = quiet || !strcmp(outfname, "-");
157 
158     if (!quiet) {
159         printf("Audio Processing\n");
160         printf("mp3 encoder && decoder\n");
161         printf("blog:http://cpuimage.cnblogs.com/\n");
162     }
163     uint32_t sampleRate = 0;
164     uint64_t totalSampleCount = 0;
165     uint32_t channels = 0;
166     int16_t *data_in = wavRead_int16(infname, &sampleRate, &channels, &totalSampleCount);
167     if (data_in == NULL)
168         return -1;
169     double startTime = now();
170     config.wave.samplerate = sampleRate;
171     config.wave.channels = channels;
172 
173     if (force_mono)
174         config.wave.channels = 1;
175 
176     /* See if samplerate and bitrate are valid */
177     if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0)
178         error("Unsupported samplerate/bitrate configuration.");
179 
180     /* open the output file */
181     if (!strcmp(outfname, "-"))
182         outfile = stdout;
183     else
184         outfile = fopen(outfname, "wb");
185     if (!outfile) {
186         fprintf(stderr, "Could not create \"%s\".\n", outfname);
187         exit(1);
188     }
189 
190     /* Set to stereo mode if wave data is stereo, mono otherwise. */
191     if (config.wave.channels > 1)
192         config.mpeg.mode = stereo;
193     else
194         config.mpeg.mode = MONO;
195 
196     /* Initiate encoder */
197     s = shine_initialise(&config);
198 
199     // assert(s != NULL);
200     /* Print some info about the file about to be created (optional) */
201     if (!quiet) check_config(&config);
202 
203     int samples_per_pass = shine_samples_per_pass(s) * channels;
204 
205     /* All the magic happens here */
206     size_t count = totalSampleCount / samples_per_pass;
207     int16_t *buffer = data_in;
208     for (int i = 0; i < count; i++) {
209         data = shine_encode_buffer_interleaved(s, buffer, &written);
210         if (write_mp3(written, data, &config) != written) {
211             fprintf(stderr, "mp3 encoder && decoder: write error\n");
212             return 1;
213         }
214         buffer += samples_per_pass;
215     }
216     size_t last = totalSampleCount % samples_per_pass;
217     if (last != 0) {
218         int16_t *cache = (int16_t *) calloc(samples_per_pass, sizeof(int16_t));
219         if (cache != NULL) {
220             memcpy(cache, buffer, last * sizeof(int16_t));
221             data = shine_encode_buffer_interleaved(s, cache, &written);
222             free(cache);
223             if (write_mp3(written, data, &config) != written) {
224                 fprintf(stderr, "mp3 encoder && decoder: write error\n");
225                 return 1;
226             }
227         }
228     }
229     /* Flush and write remaining data. */
230     data = shine_flush(s, &written);
231     write_mp3(written, data, &config);
232     /* Close encoder. */
233     shine_close(s);
234     /* Close the MP3 file */
235     fclose(outfile);
236     free(data_in);
237     double time_interval = calcElapsed(startTime, now());
238     if (!quiet)
239         printf("time interval: %d ms\n ", (int) (time_interval * 1000));
240 
241     return 0;
242 }

熟悉我的风格的朋友们,估计一看就清楚了。

也不多做解释,当然了,这份代码是学习mp3编解码的不二之选。

使用示例:

tinymp3 -b 64 input.mp3 ouput.mp3

tinymp3 -b 64 input.wav ouput.mp3

相关参数说明:

Usage: tinymp3  [options] <infile> <outfile>

Use "-" for standard input or output.

Options: -h this help message -b <bitrate> set the bitrate [8-320], default 64 kbit -m force encoder to operate in mono -c set copyright flag, default off -j encode in joint stereo (stereo data only) -d encode in dual-channel (stereo data only) -q quiet mode

不做解释,直接上取下项目,cmake一下,你懂的。

项目地址:

https://github.com/cpuimage/tinymp3

前面有不少朋友问到音频重采样库的问题,抽空整理了下speex的resampler。

我想重采样这方面也是够用的了。

项目地址:

https://github.com/cpuimage/speex_resampler

以上,权当抛砖引玉。

另外感谢 热心网友打赏两瓶可乐。

独乐乐,不如众乐乐。

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是:  gaozhihan@vip.qq.com

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档