windows 使用libsndfile库读取和保存音频文件(格式转换)

libsndfile的windows编译请参考官方文档<Using Vcpkg package manager>章节:libsndfile: A C library for reading and writing sound files containing sampled audio data. 音频文件基本处理操作库

windows 使用libsndfile库读取和保存音频文件(格式转换)

#include <sndfile.hh>
#pragma comment(lib, "sndfile.lib")
#pragma comment(lib, "vorbisenc.lib")
#pragma comment(lib, "vorbis.lib")
#pragma comment(lib, "FLAC.lib")
#pragma comment(lib, "ogg.lib")
#pragma comment(lib, "opus.lib")
#pragma comment(lib, "mpg123.lib")
#pragma comment(lib, "libmp3lame-static.lib")
#pragma comment(lib, "libmpghip-static.lib")
#pragma comment(lib, "shlwapi.lib")
//#pragma comment(lib, "advapi32.blib")

#include <fstream>
#include <iostream>
#include <vector>

int main()
{
	SndfileHandle sndfile("biden-48k.ogg");

	// 创建重采样对象
	int sr = sndfile.samplerate();
	int64_t framecount = sndfile.frames();

	SndfileHandle dstsndfile("biden-16k.mp3", SFM_WRITE, 
		SF_FORMAT_MPEG | SF_FORMAT_MPEG_LAYER_III, 1, 48000);
	std::cout << "dst snd file:" << dstsndfile.strError() << std::endl;

	std::fstream ofs_mp3("biden-48k.mp3", std::ios::binary);
	SF_VIRTUAL_IO vir{ 
		[](void* user_data) -> sf_count_t {
			std::fstream* ifs = ((std::fstream*)user_data);
			ifs->seekp(0, std::ios::end);
			auto size = ifs->tellp();
			ifs->seekp(0, std::ios::cur);
			return size;
		},
		[](sf_count_t offset, int whence, void* user_data) -> sf_count_t {
			std::fstream* ifs = ((std::fstream*)user_data);
			ifs->seekg(offset, whence);
			ifs->seekp(offset, whence);
			return 0;
		},
		[](void* ptr, sf_count_t count, void* user_data) -> sf_count_t {
			std::fstream* ifs = ((std::fstream*)user_data);
			ifs->read((char*)ptr, count);
			return ifs->gcount();
		},
		[](const void* ptr, sf_count_t count, void* user_data) ->sf_count_t {
			((std::fstream*)user_data)->write((const char*)ptr, count);
			return count;
		}, 
		[](void* user_data) -> sf_count_t {
			return ((std::fstream*)user_data)->tellp();
		} };
	SndfileHandle dstsndfile1(vir, &ofs_mp3, SFM_WRITE,
		SF_FORMAT_MPEG | SF_FORMAT_MPEG_LAYER_III, 1, 48000);
	std::cout << "dst snd file:" << dstsndfile.strError() << std::endl;

	std::ofstream ofs("biden-48k.pcm", std::ios::binary);

	int blocksize = sr / 100 * 2;
	std::vector<int16_t> data(blocksize);
	while (framecount - blocksize >= 0) {
		sndfile.read(data.data(), data.size());
		framecount -= blocksize;

		// 重采样
		ofs.write((const char*)data.data(), data.size() * sizeof(int16_t));

		int ret = dstsndfile.writef(data.data(), data.size());
		if (ret <= 0) {
			std::cout << "write error: " << dstsndfile.strError() << std::endl;
		}
		dstsndfile1.write(data.data(), data.size());
	}

	return 0;
}

首先,需要使用一个音频读取音频文件,常用的有libsndfile和portaudio等。这里以libsndfile为例,需要在代码中添加文件的链接。 接下来,需要使用FFT(快速傅里叶变换)算法对音频数据进行频谱分析,得到音频的频域数据。常用的FFT有FFTW和KISSFFT等,这里以KISSFFT为例。同样需要在代码中添加文件的链接。 最后,使用绘图(如Qt自带的QPainter)将频域数据绘制成图像。具体代码如下: ```c++ #include <QtCore/QCoreApplication> #include <QtGui/QPainter> #include <QDebug> #include <kiss_fft.h> #include <sndfile.h> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 打开音频文件 SF_INFO sfinfo; SNDFILE* file = sf_open("test.wav", SFM_READ, &sfinfo); if (!file) { qDebug() << "Failed to open audio file!"; return -1; } // 读取音频数据 const int N = 1024; float buf[N]; kiss_fft_cpx fft_in[N], fft_out[N]; kiss_fft_cfg cfg = kiss_fft_alloc(N, false, 0, 0); QPainter painter; QImage image(N, N, QImage::Format_RGB32); painter.begin(&image); int i, j, n; while ((n = sf_read_float(file, buf, N)) > 0) { // 对音频数据进行FFT变换 for (i = 0; i < N; i++) { fft_in[i].r = buf[i]; fft_in[i].i = 0; } kiss_fft(cfg, fft_in, fft_out); // 绘制频谱图 painter.fillRect(0, 0, N, N, Qt::black); for (i = 0; i < N / 2; i++) { float mag = sqrt(fft_out[i].r * fft_out[i].r + fft_out[i].i * fft_out[i].i); mag = log(mag + 1); mag = mag * 255 / 10; QColor color(mag, mag, mag); painter.setPen(color); painter.drawLine(i, N, i, N - mag); } painter.end(); image.save(QString("test_%1.png").arg(j++)); } // 释放资源 kiss_fft_free(cfg); sf_close(file); return a.exec(); } ``` 这段代码使用了1024个采样点进行FFT变换,绘制了一个1024x1024的频谱图,并将每一帧保存为一张PNG图片。可以通过调整N的大小来改变频谱图的分辨率。同时,也可以在绘图过程中加入更多的细节,如绘制坐标轴、标注等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值