转:https://www.cnblogs.com/cpuimage/p/9427457.html
WAV转MP3场景,已经验证,其他场景待验证
近期一直不间断学习音频处理,一直也没想着要去碰音频编解码相关。
主要是觉得没什么实际的作用和意义。
不管视频编解码,图像编解码,音频编解码,都有很多组织基金在推动。
当然,在一些特定的情景下,需要用起来编解码库,
而一般这些库都会有编译困难,使用困难等等困难综合症。
图像方面,已经有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
而音频这块,就有点差强人意了。
当然dr_libs 也已经做了不少工作了。
https://github.com/mackron/dr_libs
可惜的是,他做了wav的编解码库,mp3的解码库,就是没有mp3的编码库。
而一般mp3 的编码库,大众使用最多的是lame
在一阵寻寻觅觅之后,俺找到了一个mp3的编码库。
其原官网已经成为历史资源了。
https://web.archive.org/web/20060102002813/http://www.everett9981.freeserve.co.uk/pete.htm
也是相当历史久远了。
也有人对其进行了回炉重造。
https://github.com/toots/shine
俺一直惦念着,找个时间,进行代码整合,blabla
秉承着简洁简单的态度,就这么新鲜出炉了。
在写示例代码的时候,踩了几个小坑。
贴上完整代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "timing.h" #include "shine_mp3.h" #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #define DR_MP3_IMPLEMENTATION #include "dr_mp3.h" void error(char *s); int16_t *wavRead_int16(char *filename, uint32_t *sampleRate, uint32_t *channels, uint64_t *totalSampleCount) { int16_t *buffer = drwav_open_and_read_file_s16(filename, channels, sampleRate, totalSampleCount); if (buffer == NULL) { drmp3_config pConfig; float *mp3_buffer = drmp3_open_and_decode_file_f32(filename, &pConfig, totalSampleCount); if (mp3_buffer != NULL) { buffer = (int16_t *) calloc(*totalSampleCount, sizeof(int16_t)); *channels = pConfig.outputChannels; *sampleRate = pConfig.outputSampleRate; if (buffer != NULL) drwav_f32_to_s16(buffer, mp3_buffer, *totalSampleCount); free(mp3_buffer); } else { printf("read file [%s] error. ", filename); } } return buffer; } /* Some global vars. */ char *infname, *outfname; FILE *outfile; int quiet = 0; int stereo = STEREO; int force_mono = 0; /* Write out the MP3 file */ int write_mp3(long bytes, void *buffer, void *config) { return fwrite(buffer, sizeof(unsigned char), bytes, outfile) / sizeof(unsigned char); } /* Output error message and exit */ void error(char *s) { fprintf(stderr, "Error: %s ", s); exit(1); } static void print_usage() { printf("Audio Processing "); printf("mp3 encoder && decoder "); printf("blog: http://cpuimage.cnblogs.com/ "); printf("Usage: mp3 encoder && decoder [options] <infile> <outfile> "); printf("Use "-" for standard input or output. "); printf("Options: "); printf(" -h this help message "); printf(" -b <bitrate> set the bitrate [8-320], default 64 kbit "); printf(" -m force encoder to operate in mono "); printf(" -c set copyright flag, default off "); printf(" -j encode in joint stereo (stereo data only) "); printf(" -d encode in dual-channel (stereo data only) "); printf(" -q quiet mode "); printf(" -v verbose mode "); } /* Use these default settings, can be overridden */ static void set_defaults(shine_config_t *config) { shine_set_config_mpeg_defaults(&config->mpeg); } /* Parse command line arguments */ static int parse_command(int argc, char **argv, shine_config_t *config) { int i = 0; if (argc < 3) return 0; while (argv[++i][0] == '-' && argv[i][1] != ' 00' && argv[i][1] != ' ') switch (argv[i][1]) { case 'b': config->mpeg.bitr = atoi(argv[++i]); break; case 'm': force_mono = 1; break; case 'j': stereo = JOINT_STEREO; break; case 'd': stereo = DUAL_CHANNEL; break; case 'c': config->mpeg.copyright = 1; break; case 'q': quiet = 1; break; case 'v': quiet = 0; break; case 'h': default : return 0; } if (argc - i != 2) return 0; infname = argv[i++]; outfname = argv[i]; return 1; } /* Print some info about what we're going to encode */ static void check_config(shine_config_t *config) { static char *version_names[4] = {"2.5", "reserved", "II", "I"}; static char *mode_names[4] = {"stereo", "joint-stereo", "dual-channel", "mono"}; static char *demp_names[4] = {"none", "50/15us", "", "CITT"}; printf("MPEG-%s layer III, %s Psychoacoustic Model: Shine ", version_names[shine_check_config(config->wave.samplerate, config->mpeg.bitr)], mode_names[config->mpeg.mode]); printf("Bitrate: %d kbps ", config->mpeg.bitr); printf("De-emphasis: %s %s %s ", demp_names[config->mpeg.emph], ((config->mpeg.original) ? "Original" : ""), ((config->mpeg.copyright) ? "(C)" : "")); printf("Encoding "%s" to "%s" ", infname, outfname); } int main(int argc, char **argv) { shine_config_t config; shine_t s; int written; unsigned char *data; /* Set the default MPEG encoding paramters - basically init the struct */ set_defaults(&config); if (!parse_command(argc, argv, &config)) { print_usage(); exit(1); } quiet = quiet || !strcmp(outfname, "-"); if (!quiet) { printf("Audio Processing "); printf("mp3 encoder && decoder "); printf("blog:http://cpuimage.cnblogs.com/ "); } uint32_t sampleRate = 0; uint64_t totalSampleCount = 0; uint32_t channels = 0; int16_t *data_in = wavRead_int16(infname, &sampleRate, &channels, &totalSampleCount); if (data_in == NULL) return -1; double startTime = now(); config.wave.samplerate = sampleRate; config.wave.channels = channels; if (force_mono) config.wave.channels = 1; /* See if samplerate and bitrate are valid */ if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) error("Unsupported samplerate/bitrate configuration."); /* open the output file */ if (!strcmp(outfname, "-")) outfile = stdout; else outfile = fopen(outfname, "wb"); if (!outfile) { fprintf(stderr, "Could not create "%s". ", outfname); exit(1); } /* Set to stereo mode if wave data is stereo, mono otherwise. */ if (config.wave.channels > 1) config.mpeg.mode = stereo; else config.mpeg.mode = MONO; /* Initiate encoder */ s = shine_initialise(&config); // assert(s != NULL); /* Print some info about the file about to be created (optional) */ if (!quiet) check_config(&config); int samples_per_pass = shine_samples_per_pass(s) * channels; /* All the magic happens here */ size_t count = totalSampleCount / samples_per_pass; int16_t *buffer = data_in; for (int i = 0; i < count; i++) { data = shine_encode_buffer_interleaved(s, buffer, &written); if (write_mp3(written, data, &config) != written) { fprintf(stderr, "mp3 encoder && decoder: write error "); return 1; } buffer += samples_per_pass; } size_t last = totalSampleCount % samples_per_pass; if (last != 0) { int16_t *cache = (int16_t *) calloc(samples_per_pass, sizeof(int16_t)); if (cache != NULL) { memcpy(cache, buffer, last * sizeof(int16_t)); data = shine_encode_buffer_interleaved(s, cache, &written); free(cache); if (write_mp3(written, data, &config) != written) { fprintf(stderr, "mp3 encoder && decoder: write error "); return 1; } } } /* Flush and write remaining data. */ data = shine_flush(s, &written); write_mp3(written, data, &config); /* Close encoder. */ shine_close(s); /* Close the MP3 file */ fclose(outfile); free(data_in); double time_interval = calcElapsed(startTime, now()); if (!quiet) printf("time interval: %d ms ", (int) (time_interval * 1000)); return 0; }
注释:由于编译环境部不同,有基础枚举复制,可以需要强制转换
熟悉我的风格的朋友们,估计一看就清楚了。
也不多做解释,当然了,这份代码是学习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