C++生成指定频率的正弦波wav音频文件以及生成播放数据

基于MTK平台的工厂模式代码编写的一个正弦波wav音频文件生成代码片

y=k sinx

y=sin x

struct WavHead{
	char RIFF[4];    //头部分那个RIFF
	int size0;//存的是后面所有文件大小
	char WAVE[4];
	char FMT[4];
	int size1;//存的是fmt保存的大小,包含这之后,data前面几个,共16个
	short fmttag;
	short channel;
	int samplespersec;//每秒采样数
	int bytepersec;
	short blockalign;
	short bitpersamples;
	char DATA[4];
	int size2;//剩下文件大小
};

int MakeWaveData2(int freq, float amp, int time_ms, unsigned char* p, int len, int sampleRate = 44100, int channels = 2, int BitsPerSample = 16)
{
	int ret = 0;
	ALOGD(TAG"%s Enter\n", __FUNCTION__);
	int SAMPLE_NUM = sampleRate * time_ms / 1000; //采集样本总数
	int AUDIO_CYCLE = sampleRate / freq; //一个正弦波采集样本个数
	int ACCURACY = (BitsPerSample == 16) ? INT16_MAX : INT8_MAX;  //精度
	//int BUFF_SIZE = sampleRate * durations * channels * BitsPerSample/8;
	ALOGD(TAG"amp=%0.2f SAMPLE_NUM=%d AUDIO_CYCLE=%d ACCURACY=%d len=%d\n", amp, SAMPLE_NUM, AUDIO_CYCLE, ACCURACY, len);
	if(amp > 1.0){
		ALOGW(TAG"amp>1.0 set to 1.0\n");
		amp = 1.0;
	} else if (amp <= 0.01){
	       ALOGW(TAG"amp < 0.01 set to 0.01\n");
		amp = 0.01;
	}
	
	for (int i = 0; i < SAMPLE_NUM; i++){ //实际上只要采集AUDIO_CYCLE 个点就可以得到一个完整正弦波
	       int16_t v = (int16_t)( amp * ACCURACY * sin( 2 * MATH_PI * (i % AUDIO_CYCLE * 1.0)/AUDIO_CYCLE )); //正弦函数y = k sin x
		if (BitsPerSample == 16) {//16位
			if (channels == 1){//16位单通道
				p[i*2] = (v & 0xFF);           //低字节在前 16bits量化,低字节8位
				p[1+i*2] = ((v >>8) & 0xFF); //高字节在后,高字节8位
			}else{//16位双通道
				p[i*4] = (v & 0xFF);           //低字节在前 16bits量化,低字节8位
				p[1+i*4] = ((v >>8) & 0xFF); //高字节在后,高字节8位
				p[2+i*4] = (v & 0xFF);           //低字节在前 16bits量化,低字节8位
				p[3+i*4] = ((v >>8) & 0xFF); //高字节在后,高字节8位
			}

			if (i < AUDIO_CYCLE){
				if (channels == 1){
					ALOGD(TAG"v:%d p[0]:%d(0x%02X) p[1]:%d(0x%02X)", v, p[i*2], p[i*2], p[1+i*2], p[1+i*2]);
				} else {
					ALOGD(TAG"v:%d p[0]:%d(0x%02X) p[1]:%d(0x%02X)", v, p[i*4], p[i*4], p[1+i*4], p[1+i*4]);
				}
	              }
		}else{
			if (channels == 1){//8位单通道
				p[i] = (v & 0xFF);
			}else{//8位双通道
				p[i*2] = (v & 0xFF);
				p[1+i*2] = (v & 0xFF);
			}

			if (i < AUDIO_CYCLE){
		   	       ALOGD(TAG"v:%d p:%d", v, p[i*2]);
	              }
		}
	}
	
	return ret;
}

int MakeWaveFile(const char * filename, int freq, float amp, int time_ms, int sampleRate = 44100, int channels = 2, int BitsPerSample = 16)
{
	int ret = 0;
	short blockalign = (short)channels*BitsPerSample/8;
	WavHead wavHead = {{'R','I','F','F'},
		sampleRate * time_ms/1000*channels*BitsPerSample/8+36, //文件长度=该字段+ 8
		{'W','A','V','E'},
		{'f','m','t',' '},
		16, //格式长度,在此之前成员的大小
		1,   //编码格式为PCM
		(short)channels, //声道
		sampleRate, //采样频率
		channels*sampleRate*BitsPerSample/8,// 数据传输速率
		blockalign, //数据块对齐单位(一个sample所占字节数)
		(short)BitsPerSample,//采样位数(数度)
		{'d','a','t','a'},
		sampleRate * time_ms/1000*channels*BitsPerSample/8};//采样数据的大小
		
	ALOGD(TAG"%s Enter\n", __FUNCTION__);
	ALOGD(TAG"filename=%s freq=%d amp=%0.2f time_ms=%d sampleRate=%d channels=%d BitsPerSample=%d size0=%d size1=%d size2=%d bytepersec=%d blockalign=%d\n", 
		__FUNCTION__, filename, freq, amp, time_ms, sampleRate, channels, BitsPerSample,
		wavHead.size0, wavHead.size1, wavHead.size2, wavHead.bytepersec, wavHead.blockalign);

	FILE * fd= fopen(filename, "wb");
	if(NULL == fd){
		ALOGE(TAG"fopen %s failed %s %d\n", filename, strerror(errno), errno);
		return -1;
	}

	int write = fwrite(&wavHead, sizeof(wavHead), 1, fd);
	ALOGD(TAG"fwrite sizeof(wavHead)=%d writed=%d\n", sizeof(wavHead), write);
	unsigned char * data = new unsigned char[wavHead.size2];
	MakeWaveData2(freq, amp, time_ms, data, wavHead.size2, sampleRate, channels, BitsPerSample);
	write = fwrite(data, wavHead.size2, 1, fd);
	ALOGD(TAG"fwrite sizeof(wavHead)=%d writed=%d\n", wavHead.size2, write);
	
	delete [] data;
	fclose(fd);
	return ret;
}

测试代码:

 int freq = 5000;
	 float amp = 0.8;
        int BitsPerSample = 16;
	 int durations = 1000;
        int totalLen = (sampleRate * 2 * BitsPerSample / 8) * durations /1000;
        /*
        07-24 14:51:19.160: W/AudioALSAPlaybackHandlerBase(1204): doDcRemoval(), inBytes 960000 > mDcRemoveBufferSize 131072
        07-24 14:51:19.160: E/AudioALSAPlaybackHandlerBase(1204): AUD_ASSERT(0) fail: "vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/AudioALSAPlaybackHandlerBase.cpp", 521L
        */
	 unsigned char * audiodata = new unsigned char[totalLen]; //doDcRemoval(), inBytes 960000 > mDcRemoveBufferSize 131072
        //MakeWaveData(freq, amp, durations, inBuffer, ReadBlockLen);
	 //PCM_decode_data(&pWavePlaydata->mWaveHeader, inBuffer, ReadBlockLen, outBuffer, &out_size);
	 MakeWaveFile("/data/liu.wav", freq, amp, durations, sampleRate, 2, BitsPerSample);
	 MakeWaveData2(freq, amp, durations, audiodata, totalLen, sampleRate);
	 int outlen = 0;
	 int writelen = 0;
	 for (int i = 0; i< 1; i ++){
	 	writelen  = streamOutput->write(audiodata, totalLen);
	      //writelen  = streamOutput->write(inBuffer, ReadBlockLen);
             //writelen  = streamOutput->write(outBuffer, out_size);
	      ALOGD(TAG"Audio_Wave_Playabck_routine make_wave_data write to hardware... i=%d totalLen = %d writelen = %d outlen = %d\n", i, totalLen, writelen, outlen);
	 }
	 delete [] audiodata;
	 audiodata = NULL;

8bit单声道波形在Cool Edit Pro中查看会变形,负数显示成正数了。不知道是Cool Edit Pro的问题还是生成数据有问题,看图形是负数被当成正数显示在图形上了。

8bit单声道波形会变形

8bit单声道波形会变形数据

16bit的看起来很正常

扫描二维码关注公众号,回复: 2429160 查看本文章

正弦波

正弦波放大96点

参考:

《WAV 文件格式分析》 https://blog.csdn.net/zgyggy/article/details/70050280

《手工制作Wav文件以及生成播放数据》 https://blog.csdn.net/zouli415/article/details/80177280

《C++生成简单WAV文件(一)》https://www.cnblogs.com/shibuliao/p/3815210.html

《wav文件格式分析与详解》 https://www.cnblogs.com/ranson7zop/p/7657874.html

猜你喜欢

转载自blog.csdn.net/liudongming_1985/article/details/81206072