ALSA subsystem (fifteen)------PCM to WAV audio format

Hello! Here is Kite's blog,

Welcome to communicate with me.


All WAVs have a file header, which records the encoding parameters of the audio stream. Data blocks are recorded in little-endian byte order.

The full name of the WAV format is WAVE. You only need to add a WAV file header in front of the PCM file to generate a WAV format file.
The WAVE file format is part of the Microsoft RIFF specification for storing multimedia files. A RIFF file begins with a file header, followed by a series of data blocks (Chunk). A WAVE file is usually just a RIFF file with a single "WAVE" chunk consisting of two sub-chunks - the "fmt" chunk specifies the data format, and the "data" chunk contains the actual sample data. Call this form the "canonical form".

wav
The WAV file header information consists of 44 bytes, so only need to add 44 bytes of WAV file header to the PCM file header to generate WAV format files.

  • ChunkID: The size is 4 bytes of data, the content is "RIFF", indicating the resource exchange file identifier
  • ChunkSize: The size is 4 bytes of data, and the content is an integer, indicating the total number of bytes from the next address to the end of the file
  • Format: The size is 4 bytes of data, and the content is "WAVE", indicating the WAV file identifier
  • Subchunkl ID: The size is 4 bytes of data, the content is "fmt", indicating the waveform format identifier (fmt), and the last bit is a space.
  • Subchunkl Size: The size is 4 bytes of data, and the content is an integer, indicating the length of PCMWAVEFORMAT.
  • AudioFormat: The size is 2 bytes of data, and the content is a short integer, indicating the type of format (when the value is 1, it means that the data is linear PCM encoding)
  • NumChannels: The size is 2 bytes of data, the content is a short integer, indicating the number of channels, 1 for mono, 2 for dual
  • SampleRate: The size is 4 bytes of data, and the content is an integer, indicating the sampling rate, such as 44100
  • ByteRate: The size is 4 bytes of data, the content is an integer, indicating the waveform data transmission rate (average number of bytes per second), and the size is sampling rate * number of channels * number of sampling bits
  • BlockAlign: The size is 2 bytes of data, the content is a short integer, indicating the length of the DATA data block, and the size is the number of channels * the number of sampling bits
  • BitsPerSample: The size is 2 bytes of data, and the content is a short integer, indicating the number of sampling bits, that is, the PCM bit width, usually 8 bits or 16 bits
  • Subchunk2ID: The size is 4 bytes of data, the content is "data", indicating the data marker
  • Subchunk2 Size: The size is 4 bytes of data, and the content is an integer, indicating the total size of the next sound data. The 44 bytes of the header need to be subtracted.
  • data: is the content of other encoded files

For example, the following are the first 72 bytes of a WAVE file, where the bytes are displayed as hexadecimal numbers:
wav1
code pcm2wav.c:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <getopt.h>
#include <sys/signal.h>

#pragma pack(push)
#pragma pack(1)     //1字节对齐

typedef struct {
    
    
	char		RIFFNAME[4];
	unsigned int	nRIFFSize;
	char		WAVNAME[4];
	char		FMTNAME[4];
	unsigned int	nFMTSize;
	unsigned short	nAudioFormat;
	unsigned short  nChannels;
	unsigned int	nSampleRate; /*采样频率*/
	unsigned int	nBytesPerSecond; /*每秒所需字节数*/
	unsigned short	nBlockAlign; /*数据块对齐单位,每个采样需要的字节数*/
	unsigned short	wBitsPerSample; /*每个采样需要的bit数*/
	char		DATANAME[4];
	unsigned int	nDataSize;
} WAVFLIEHEAD;

#pragma pack(pop) /* 恢复先前的pack设置 */

WAVFLIEHEAD FileHeader;

void wav_head_print(void)
{
    
    
	printf("RIFFNAME:%c%c%c%c\n", FileHeader.RIFFNAME[0], FileHeader.RIFFNAME[1], FileHeader.RIFFNAME[2], FileHeader.RIFFNAME[3]);
	printf("RIFFSize:%d\n", FileHeader.nRIFFSize);
	printf("WAVNAME:%c%c%c%c\n", FileHeader.WAVNAME[0], FileHeader.WAVNAME[1], FileHeader.WAVNAME[2], FileHeader.WAVNAME[3]);
	printf("FMTNAME:%c%c%c%c\n", FileHeader.FMTNAME[0], FileHeader.FMTNAME[1], FileHeader.FMTNAME[2], FileHeader.FMTNAME[3]);
	printf("FMTSize:%d\n", FileHeader.nFMTSize);
	printf("AudioFormat:%d\n", FileHeader.nAudioFormat);
	printf("Channels:%d\n", FileHeader.nChannels);
	printf("SampleRate:%d\n", FileHeader.nSampleRate);
	printf("BytesPerSecond:%d\n", FileHeader.nBytesPerSecond);
	printf("BlockAlign:%d\n", FileHeader.nBlockAlign);
	printf("BitsPerSample:%d\n", FileHeader.wBitsPerSample);
	printf("DATANAME:%c%c%c%c\n", FileHeader.DATANAME[0], FileHeader.DATANAME[1], FileHeader.DATANAME[2], FileHeader.DATANAME[3]);
	printf("DataSize:%d\n", FileHeader.nDataSize);
}

int main(int argc, char const *argv[])
{
    
    
	unsigned int rate = 48000;
	unsigned short channels = 2, bits = 16;
	int option_index, c;
	static const char short_options[] = "hs:d:r:v:c:b:p:";
	static const struct option long_options[] = {
    
    
		{
    
    "help",no_argument,0,'h'},
		{
    
    "source pcm file name",required_argument,0,'s'},
		{
    
    "destination wav file name",required_argument,0,'d'},
		{
    
    "rate",required_argument,0,'r'},
		{
    
    "channels",required_argument,0,'r'},
		{
    
    0, 0, 0, 0}
	};
	char file_name_wav[100]={
    
    0,};
	char file_name_pcm[100]={
    
    0,};

	while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1)  {
    
    
		switch(c) {
    
    
			case 'h':
				printf("help\n");
				return 0;
			case 'd':
				strcpy(file_name_wav,optarg);
				break;
			case 's':
				strcpy(file_name_pcm,optarg);
				break;
			case 'r':
				rate = strtol(optarg, NULL, 0);
				break;
			case 'c':
				channels = strtol(optarg, NULL, 0);
				break;
			case 'b':
				bits = strtol(optarg, NULL, 0);
				break;
			default:
				printf("unsupport cmd,try -h for help\n");
				return 1;
		}
	}

	FileHeader.RIFFNAME[0] = 'R';
	FileHeader.RIFFNAME[1] = 'I';
	FileHeader.RIFFNAME[2] = 'F';
	FileHeader.RIFFNAME[3] = 'F';

	FileHeader.WAVNAME[0] = 'W';
	FileHeader.WAVNAME[1] = 'A';
	FileHeader.WAVNAME[2] = 'V';
	FileHeader.WAVNAME[3] = 'E';

	FileHeader.FMTNAME[0] = 'f';
	FileHeader.FMTNAME[1] = 'm';
	FileHeader.FMTNAME[2] = 't';
	FileHeader.FMTNAME[3] = 0x20;
	FileHeader.nFMTSize =16;//SND_PCM_FORMAT_S16_LE
	FileHeader.nAudioFormat = 1;

	FileHeader.DATANAME[0] = 'd';
	FileHeader.DATANAME[1] = 'a';
	FileHeader.DATANAME[2] = 't';
	FileHeader.DATANAME[3] = 'a';
	FileHeader.wBitsPerSample = bits;
	FileHeader.nBlockAlign =2;
	FileHeader.nSampleRate =rate;
	FileHeader.nBytesPerSecond = rate * FileHeader.nBlockAlign;
	FileHeader.nChannels = channels;
	wav_head_print();

	int nFileLen = 0;
	int nSize = sizeof(FileHeader);

	FILE *fp_s = NULL;
	FILE *fp_d = NULL;

	fp_s = fopen(file_name_pcm, "rb");
	if (fp_s == NULL)
		return -1;

	fp_d = fopen(file_name_wav, "wb+");
	if (fp_d == NULL)
		return -2;


	int nWrite =fwrite(&FileHeader, 1, nSize, fp_d);
	if (nWrite != nSize)
	{
    
    
		fclose(fp_s);
		fclose(fp_d);
		return -3;
	}

	while( !feof(fp_s))
	{
    
    
		char readBuf[4096];
		int nRead = fread(readBuf, 1,4096, fp_s);
		if (nRead >0)
		{
    
    
			fwrite(readBuf,1, nRead, fp_d);
		}

		nFileLen += nRead;
	}
	fseek(fp_d, 0L, SEEK_SET);

	FileHeader.nRIFFSize = nFileLen - 8 +nSize;
	FileHeader.nDataSize = nFileLen;
	nWrite =fwrite(&FileHeader, 1, nSize, fp_d);
	if (nWrite != nSize)
	{
    
    
		fclose(fp_s);
		fclose(fp_d);
		return -4;
	}

	fclose(fp_s);
	fclose(fp_d);

	return nFileLen;
}

Compile: gcc pcm2wav.c -o pcm2wav
If you want to convert test.pcm to test.wav

./pcm2wav -s test.pcm -d test.wav -c 2 -r 48000 -b 16

In the follow-up, an algorithm for converting monophonic to multi-channel is provided:

void MonoToMulti(char *src_buf, char *dst_buf, int len, int  bits, int channels)
{
    
    
	int i, j;
	for (int i = 0; i < len / (bits/8); i++)
	{
    
    
		for (int j = 0; j < channels * bits/8; j++)
			dst_buf[i * channels * bits/8 + j] = src_buf[i * bits/8 + j%(bits/8)];
	}
}

Reference:
Introduction to Audio Formats and Converting PCM to WAV

Guess you like

Origin blog.csdn.net/Guet_Kite/article/details/114625653