制作录音文件转PCM格式单片机播放

准备相关工具软件

录音工具:Audacity
格式转换工具:ffmpeg
工具下载地址

制作录音文件

将WAV文件转换为PCM数据

ffmpeg -i xxx.wav -f s16le -ar 16000 -ac 1 -acodec pcm_s16le pcm16k.pcm

其中:

  • -f为存储类型
  • s16le指的是16位整形数据
  • le代表的是小端序,对应的是be大端序,一般默认是le小端序。如果搞错了,生成的pcm文件是一串噪音
  • -ar 是音频采样率,一般有8k,16k等各种不同的采样率
  • -ac: 通道数,1指单通道
  • -acodec:生成文件格式,pcm_s16le指的是pcm文件,s16le对应前面**-f**部分

用ffmpeg播放pcm文件:

ffplay -ar 16000 -channels 1 -f s16le -i output.pcm

制作录音数据播放

制作流程:

  1. 使用FFMPEG将WAV文件转为PCM文件
  2. 使用读取PCM文件导出16进制数组
  3. 在程序中定义这个常量数组
  4. 调用接口输出数据

PCM数据导出数组工具:

/** Includes -----------------------------------------------------------------*/
#include <stdint.h> /**< need definition of uint8_t */
#include <stddef.h> /**< need definition of NULL    */
#include <stdbool.h>/**< need definition of BOOL    */
#include <stdio.h>  /**< if need printf             */
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/** Private includes ---------------------------------------------------------*/
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
/** Private defines ----------------------------------------------------------*/
#ifndef OUT_FILE_NAME
	#define OUT_FILE_NAME "default.file"
#endif
/** Exported typedefines -----------------------------------------------------*/

/*文件打开权限*/
typedef enum
{
	READ_ONLY = 0,
	READ_WRITE_ONLY,
	WRITE_CREAT_CLEAR,
	READ_WRITE_CREAT_CLEAR,
	WRITE_APPEND_CREAT,
	READ_WRITE_APPEND_CREAT,
}FILE_OPEN_MODE;

/*文件分割方式*/
typedef enum
{
	SPACE_SPLIT = 0,
	COMMA_SPLIT,
}FILE_SPLIT_MODE;

/** Exported constants -------------------------------------------------------*/

/** Exported macros-----------------------------------------------------------*/
#define PRINT_ERRMSG(STR) fprintf(stderr,"line:%d,msg:%s,eMsg:%s\n", __LINE__, STR, strerror(errno))
/** Exported variables -------------------------------------------------------*/
/** Exported functions prototypes --------------------------------------------*/
/*返回指定文件是否存在*/
int file_is_exist(const char *fimename);

/*打开指定文件返回文件描述符*/
FILE *file_open(const char *filename ,FILE_OPEN_MODE mode);

/*读取指定打开的文件,返回总行数*/
int file_get_line_cnt(const char *filename);

/*获取文件大小*/
int get_file_size(const char *filename);

/*读取指定打开的文件指定行的内容到缓冲区*/
size_t file_read(const char *filename ,char *destbuf ,size_t size ,int linenum);

/*替换字符*/
size_t file_replace_ch(char *sourcebuf ,char sourcech,char destch);

/*清除字符串空格*/
char *strtriml(char *pstr);
char *strtrimr(char *pstr);
char *strtrim(char *pstr);

/*清除文本中空格,忽略注释标识行*/
bool file_strip_comments(char *string, char comment);
/**
  ******************************************************************
  * @brief   判断文件是否存在
  * @param   [in]fimename 文件名
  * @retval  返回0文件存在
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
int file_is_exist(const char *fimename)
{
	return access(fimename ,F_OK | W_OK | R_OK);
}

/**
  ******************************************************************
  * @brief   打开指定文件返回文件描述符,追加模式下fseek(fp, 0, SEEK_SET)无效
  * @param   [in]fimename 文件名
  * @param   [in]mode 打开文件的模式选择
  * @retval  返回0文件存在
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
FILE *file_open(const char *filename ,FILE_OPEN_MODE mode)
{
	FILE *fp = NULL;
	switch(mode)
	{
	case READ_ONLY:
		fp = fopen(filename ,"r");
		break;
	case READ_WRITE_ONLY:
		fp = fopen(filename ,"r+");
		break;
	case WRITE_CREAT_CLEAR:
		fp = fopen(filename ,"w");
		break;
	case READ_WRITE_CREAT_CLEAR:
		fp = fopen(filename ,"w+");
		break;
	case WRITE_APPEND_CREAT:
		fp = fopen(filename ,"a");
		break;
	case READ_WRITE_APPEND_CREAT:
		fp = fopen(filename ,"a+");/**< 首次读取时,从文件头部开始读*/
		break;
	}
	return fp;
}

/**
  ******************************************************************
  * @brief   打开读取指定打开的文件,返回总行数
  * @param   [in]fp 文件指针
  * @param   [in]filename 文件名称
  * @retval  返回-1读取失败
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
int file_get_line_cnt(const char *filename)
{
	int cnt = 0;
	char buf[256];
	FILE *fp = file_open(filename ,READ_ONLY);
	if(fp == NULL)
	{
		PRINT_ERRMSG("fopen");
		printf("read file name :%s error.\n" ,filename);
		return -1;
	}

	char *ret = NULL;
	/*读取文件流中的内容*/
	while((fgets(buf ,256 ,fp)) != NULL)
	{
		ret = strchr(buf ,'\n');
		if(ret != NULL)
		{
			cnt++;
		}
	}

	/*关闭文件*/
	fclose(fp);
	return cnt;
}

/**
  ******************************************************************
  * @brief   获取文件大小
  * @param   [in]fileName
  * @return  文件大小字节数
  * @author  aron566
  * @version V1.0
  * @date    2020-12-13
  ******************************************************************
  */
int get_file_size(const char *filename)
{ 
	struct stat st;
	stat(filename, &st);
	return st.st_size;
}


/**
  ******************************************************************
  * @brief   读取指定打开的文件指定行的内容到缓冲区
  * @param   [in]filename 文件名称
  * @param   [in]读取到的数据存储区
  * @param   [in]限制长度
  * @param 	 [in]需读取的行
  * @retval  执行结果,读取到字节数
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
size_t file_read(const char *filename ,char *destbuf ,size_t size ,int linenum)
{
	int cnt = 0;
	char buf[1024];

	/*初始化缓冲区*/
	if(strlen(destbuf) > 0)
	{
		destbuf[0] = '\0';
	}

	/*打开文件流*/
	FILE *fp = file_open(filename ,READ_ONLY);
	if(fp == NULL)
	{
		PRINT_ERRMSG("fopen");
		return -1;
	}

	/*读取文件流中的内容*/
	char *ret = NULL;
	size_t len = 0;
	while((fgets(buf ,1024 ,fp)) != NULL)
	{
		ret = strchr(buf ,'\n');
		if(ret != NULL)
		{
			cnt++;
			if(cnt == linenum)
			{
				if(len == 0)
				{
					strncpy(destbuf ,buf ,1024);
					if(size > 1024)
					{
						destbuf[1024] = '\0';
					}
					else
					{
						destbuf[1023] = '\0';
					}
				}
				else
				{
					/*追加字符串*/
					len += strlen(buf);
					if(len > size)
					{
						break;
					}
					else
					{
						strcat(destbuf ,buf);
					}
				}
				fclose(fp);
				return strlen(destbuf);
			}//cnt == linenum
		}
		else
		{
			/*判断是否超出缓冲区大小*/
			if(cnt == linenum-1)
			{
				/*目标缓冲区过小直接退出*/
				if(size <= 1024)
				{
					break;
				}
				len = strlen(destbuf);
				if(len == 0)
				{
					strncpy(destbuf ,buf ,1024);
					destbuf[1024] = '\0';
				}
				else
				{
					/*追加字符串*/
					len += strlen(buf);
					if(len > size)
					{
						break;
					}
					else
					{
						strcat(destbuf ,buf);
					}
				}
			}
		}
	}
	fclose(fp);
	return strlen(destbuf);
}

/**
  ******************************************************************
  * @brief   写入指定的内容到文件
  * @param   [in]filename 文件名称
  * @param   [in]buffer数据存储区
  * @param   [in]size写入的元素占总字节数
  * @param 	 [in]count写入元素数目
  * @param 	 [in]mode文件写入模式
  * @retval  执行结果,写入元素的总数
  * @author  aron566
  * @version V1.0
  * @date    2020-10-09
  ******************************************************************
  */
size_t file_write(const char *filename ,const void* buffer ,size_t size ,size_t count ,FILE_OPEN_MODE mode)
{
	/*打开文件流*/
	FILE *fp = file_open(filename ,mode);
	if(fp == NULL)
	{
		return 0;
	}
	size_t cnt = fwrite(buffer ,size ,count ,fp);
	/*同步到文件中*/
	fflush(fp);
	fclose(fp);
	return cnt;
}

/**
  ******************************************************************
  * @brief   替换目标字符串中字符
  * @param   [in]目标字符串
  * @param   [in]源字符
  * @param   [in]目标字符
  * @retval  执行结果,当前字符长度
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
size_t file_replace_ch(char *sourcebuf ,char sourcech,char destch)
{
    int i;
    i = strlen(sourcebuf) - 1;
    while (sourcebuf[i] == sourcech && (i >= 0))
    	sourcebuf[i--] = destch;
    return strlen(sourcebuf);
}

/**
  ******************************************************************
  * @brief   去除字符串右端空格
  * @param   [in]字符串指针
  * @retval  修剪后的字符串地址
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
char *strtrimr(char *pstr)
{
    int i;
    i = strlen(pstr) - 1;
    while (isspace(pstr[i]) && (i >= 0))
        pstr[i--] = '\0';
    return pstr;
}

/**
  ******************************************************************
  * @brief   去除字符串左端空格
  * @param   [in]字符串指针
  * @retval  修剪后的字符串地址
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
char *strtriml(char *pstr)
{
    int i = 0,j;
    j = strlen(pstr) - 1;
    while (isspace(pstr[i]) && (i <= j))
        i++;
    if (0<i)
        strcpy(pstr, &pstr[i]);
    return pstr;
}

/**
  ******************************************************************
  * @brief   去除字符串两端空格
  * @param   [in]字符串指针
  * @retval  修剪后的字符串地址
  * @author  aron566
  * @version V1.0
  * @date    2020-08-28
  ******************************************************************
  */
char *strtrim(char *pstr)
{
    char *p;
    p = strtrimr(pstr);
    return strtriml(p);
}

/**
  ******************************************************************
  * @brief   去掉字符串内所有空白,且忽略注释部分,最终得到没有空白的字符串
  * @param   [in]string:字符串
  * @param   [in]comment:注释标识
  * @retval  true表示数据可用
  * @author  aron566
  * @version V1.0
  * @date    2020-08-31
  ******************************************************************
  */
bool file_strip_comments(char *string, char comment)
{
  if (NULL == string || '\n' == *string || '\r' == *string) {
    return false; /* 第一个字符为回车或换行,表示空行 */
  }

  char *p, *q;
  /* 下面去掉字符串中所有空白字符 */
  for (p = q = string; *p != '\0' && *p != comment; p++) {
	  /* 不是空白字符则重写字符串 */
    if (0 == isspace(*p)) {
      *q++ = *p;
    }
  }
  *q = '\0';

  return 0 != strlen(string); /* 字符串长度不为0,表示数据可用 */
}

/**
 * @brief 16进制字符转为数值
 * 
 * @param ch 16进制字符
 * @return uint8_t 数值
 */
uint8_t hex_char_to_value(uint8_t ch)
{
	uint8_t result = 0;
	/*获取16进制的高字节位数据*/
	if (ch >= '0' && ch <= '9')
	{
		result = ch - '0';
	}
	else if (ch >= 'a' && ch <= 'z')
	{
		result = ch - 'a' + 10;
	}
	else if (ch >= 'A' && ch <= 'Z')
	{
		result = ch - 'A' + 10;
	}
	else
	{
		result = 0;
	}
	return result;
}

/**
 * @brief 将大写字母转换成小写字母
 * 
 * @param ch 大写字母
 * @return uint8_t 小写字母
 */
uint8_t ch_tolower(uint8_t ch)
{
    if(ch >= 'A' && ch <= 'Z')
    {
        return ch + 'a' - 'A';
    }
    else
    {
        return ch;
    }
}

/**
 * @brief 16进制的字符串转换成整数
 * 
 * @param s 16进制字符串
 * @return int 数值
 */
int hextoi(char s[])
{
    int i = 0;
    int ret = 0;
    if(s[0] == '0' && (s[1]=='x' || s[1]=='X'))
    {  
        i = 2;
    }
    else
    {
        i = 0;
    }
    for(;(s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z');++i)
    {
        if(ch_tolower(s[i]) > '9')
        {
            ret = 16 * ret + (10 + ch_tolower(s[i]) - 'a');
        }
        else
        {
            ret = 16 * ret + (ch_tolower(s[i]) - '0');
        }
    }
    return ret;
}

/**
 * @brief main入口
 * 
 * @param argc 参数个数
 * @return argv[] 参数
 */
int main(int argc ,char *argv[])
{
#if defined (ENABLE_PRINTF_HEX)
	if(argc < 2)
	{
		printf("Usage:%s filename\n" ,argv[0]);
		return -1;
	}
#else
	if(argc < 4)
	{
		printf("Usage:%s filename strlen split_num\n" ,argv[0]);
		return -1;
	}

	int buf_len = atoi(argv[2]);
	char *strbuf = (char*)malloc(sizeof(char)*buf_len);
	if(strbuf == NULL)
	{
		printf("malloc error!\n");
		return -1;
	}
	memset(strbuf ,0 ,buf_len);
#endif

	/*打开文件*/
	FILE *fp = file_open(argv[1] ,READ_ONLY);
	if(fp == NULL)
	{
		printf("can't open file.\n");
		return -1;
	}
#if defined (ENABLE_PRINTF_HEX)
	/*获取文件字节数*/
	int file_size = get_file_size(argv[1]);
	uint8_t byte = 0;
	char hex_str[64];
	sprintf(hex_str, "static const uint8_t hex_data[%d] = \n{\n", file_size);
	file_write("hex_file.txt" ,hex_str ,1 ,strlen(hex_str) ,WRITE_APPEND_CREAT);
  for(int index = 0; index < file_size; index++)
	{
		fread(&byte, 1, 1, fp);
		if(index == file_size-1)
		{
			sprintf(hex_str, "0x%02X", byte);
		}
		else
		{
			sprintf(hex_str, "0x%02X, ", byte);
		}
		file_write("hex_file.txt" ,hex_str ,1 ,strlen(hex_str) ,WRITE_APPEND_CREAT);
	}
	sprintf(hex_str, "\n};\n");
	file_write("hex_file.txt" ,hex_str ,1 ,strlen(hex_str) ,WRITE_APPEND_CREAT);
	return 0;
#else
	/*读取文件内容*/
	char txt[64] = {0};
	while((fgets(txt ,64 ,fp)) != NULL)
   {
	    /* 去掉字符串所有空白,注释也忽略 */
	    if (file_strip_comments(txt ,' '))
	    {
	      strcat(strbuf ,txt);
	    }
   }
	
	int split_num = atoi(argv[3]);
	uint8_t temp = 0;
	for(int i = 0;i < buf_len;)
	{
#if defined (ENABLE_PRINTF_CH)
		if(i < split_num)
		{
			printf("%c%c" ,strbuf[i] ,strbuf[i+1]);
		}
		else
		{
			printf(" ""%c%c" ,strbuf[i] ,strbuf[i+1]);
		}
#endif
#if defined (ENABLE_OUT_FILE)
		temp = hex_char_to_value(strbuf[i]);
		temp <<= 4;
		temp |= hex_char_to_value(strbuf[i+1]);
		file_write(OUT_FILE_NAME ,&temp ,1 ,1 ,WRITE_APPEND_CREAT);
#endif
		i += split_num;
	}
#endif
	return 0;
}

以上代码保存为cov.c

linux平台下直接编译:

已编译好的工具位于:DebugTool/cov_tool

#编译
gcc split.c -DENABLE_PRINTF_HEX -o cov_tool
#添加执行权限
sudo chmod +x cov_tool

#执行转换
./cov_tool <pcm文件名>

#输出
hex_file.txt

复制hex_file.txt中文本,在程序中定义音频常量
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要使用Python录音并将其换为PCM格式,您可以使用PyAudio库进行录音,并使用wave库将其换为PCM格式。以下是一个简单的示例代码: ```python import pyaudio import wave # 录音参数 CHUNK = 1024 # 每次读取的音频帧数 FORMAT = pyaudio.paInt16 # 采样位数 CHANNELS = 1 # 声道数 RATE = 16000 # 采样率 RECORD_SECONDS = 5 # 录音时长 WAVE_OUTPUT_FILENAME = "output.wav" # 输出文件名 # 初始化PyAudio对象 audio = pyaudio.PyAudio() # 打开音频流 stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) print("开始录音...") frames = [] # 录音 for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): data = stream.read(CHUNK) frames.append(data) print("录音结束.") # 停止录音 stream.stop_stream() stream.close() audio.terminate() # 保存录音文件 wave_file = wave.open(WAVE_OUTPUT_FILENAME, 'wb') wave_file.setnchannels(CHANNELS) wave_file.setsampwidth(audio.get_sample_size(FORMAT)) wave_file.setframerate(RATE) wave_file.writeframes(b''.join(frames)) wave_file.close() print("已保存为PCM格式的音频文件:", WAVE_OUTPUT_FILENAME) ``` 上述代码使用了PyAudio库进行录音,将每次读取的音频数据存储到frames列表中。然后使用wave库创建一个Wave文件,设置相应的参数,并将frames中的数据写入文件中。 请注意,上述代码中设置了采样率为16000,采样位数为16位,声道数为1。您可以根据需要进行相应的修改。 希望对您有帮助!如果您还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aron566

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值