1. 程式人生 > >C++生成指定頻率的正弦波wav音訊檔案以及生成播放資料

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的看起來很正常

正弦波

正弦波放大96點

參考: