C++生成指定頻率的正弦波wav音訊檔案以及生成播放資料
阿新 • • 發佈:2019-02-13
基於MTK平臺的工廠模式程式碼編寫的一個正弦波wav音訊檔案生成程式碼片
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的問題還是生成資料有問題,看圖形是負數被當成正數顯示在圖形上了。
16bit的看起來很正常
參考: