搜索
bottom↓
回复: 0

《DNESP32S3使用指南-IDF版_V1.6》第四十二章 录音机实验

[复制链接]

出0入234汤圆

发表于 2024-7-31 18:11:27 | 显示全部楼层 |阅读模式
2.jpg
1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729
1.png
3.png

第四十二章 录音机实验


       上一章,我们实现了一个简单的音乐播放器,本章我们将在上一章的基础上,继续用ES8388实现一个简单的录音机,录制WAV格式的录音。
       本章分为如下几个小节:
       42.1 ES8388录音简介
       42.2 硬件设计
       42.3 程序设计
       42.4 下载验证

       42.1 ES8388录音简介
       本章涉及的知识点基本上在上一章都有介绍。本章要实现WAV录音,还是和上一章一样,要了解:WAV文件格式、ES8388和I²S。WAV文件格式,我们在上一章已经做了详细介绍了,这里就不作介绍了。
       正点原子DNESP32S3开发板将板载的一个MIC分别接入到了ES8388的2个差分输入通道(LIP/LIN和RIP/RIN,原理图见:图42.2.3)。代码上,我们采用立体声WAV录音,不过,左右声道的音源都是一样的,录音出来的WAV文件,听起来就是个单声道效果。
       关于ES8388的驱动与上一章是一样的,区别在于ES8388的工作状态不一样,在本章录音实验中ES8388设置为开启ADC,上一章节则是设置为开启DAC,读者想了解可以参考第42章的介绍,或者参考本例程源代码和ES8388的pdf数据手册理解。

       42.2 硬件设计

       42.2.1 例程功能
       本章实验功能简介:开机后,先初始化各外设,然后检测字库是否存在,如果检测无问题,则开始循环播放SD卡MUSIC文件夹里面的歌曲(必须在SD卡根目录建立一个MUSIC文件夹,并存放歌曲在里面),在SPILCD上显示歌曲名字、播放时间、歌曲总时间、歌曲总数目、当前歌曲的编号等信息。KEY0用于选择下一曲,KEY2用于选择上一曲,KEY3用来控制暂停/继续播放。LED闪烁,提示程序运行状态。

       42.2.2 硬件资源
       本实验,大家需要准备1个microSD/SD卡(在里面新建一个MUSIC文件夹,并存放一些歌曲在MUSIC文件夹下)和一个耳机(非必备),分别插入SD卡接口和耳机接口,然后下载本实验就可以实现录音机的效果。实验用到的硬件资源如下:

       1. LED灯
              LED -IO0

       2.独立按键
              KEY0(XL9555) - IO1_7
              KEY1(XL9555) - IO1_6
              KEY2(XL9555) - IO1_5
              KEY3(XL9555) - IO1_4

       3. XL9555
              IIC_SDA-IO41
              IIC_SCL-IO42

       4. SPILCD
              CS-IO21
              SCK-IO12
              SDA-IO11
              DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
              PWR- IO1_3(XL9555)
              RST- IO1_2(XL9555)

       5. SD
              CS-IO2
              SCK-IO12
              MOSI-IO11
              MISO-IO13

       6. ES8388音频CODEC芯片(IIC端口0)
              IIC_SDA-IO41
              IIC_SCL-IO42
              I2S_BCK_IO-IO46
              I2S_WS_IO-IO9
              I2S_DO_IO-IO10
              I2S_DI_IO-IO14
              IS2_MCLK_IO-IO3

       7. 开发板板载的咪头或自备麦克风输入

       8. 喇叭或耳机

       录音机实验与上一章(音乐播放器实验)用到的硬件资源基本一样,我们这里就不重复介绍原理图了,有差异的是这次我们用到板载的咪头用于信号输入,也可以通过3.5mm的音频接口通过LINE_IN接入麦克风输入录音音源。

       42.2.3 原理图
       本实验相关的原理图同上一章节。

       42.3 程序设计

       42.3.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

第四十二章 录音机实验1549.png
图42.3.1.1 录音实验程序流程图

       42.3.2 录音实验函数解析
       本章实验所使用ESP32-S3的API函数在上一章节已经讲述过了,在此不再赘述。

       42.3.3 录音实验驱动解析
       在IDF版的31_recoding例程中,作者在31_recoding \components\BSP路径下新增了一个I2S文件夹和一个ES8388文件夹,分别用于存放i2s.c、i2s.h和es8388.c以及es8388.h这四个文件。其中,i2s.h和es8388.h文件负责声明I2S以及ES8388相关的函数和变量,而i2s.c和es8388.c文件则实现了I2S以及ES8388的驱动代码。下面,我们将详细解析这四个文件的实现内容。

       1,recorder驱动
       录这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码,RECORDER的驱动主要包括两个文件:recorder.c和recorder.h。
       音乐播放器实验中我们已经学过配置ES8388的方法,我们在recoder.c编写函数配置ES8388工作在PCM录音模式,我们编写代码如下:
  1. /**
  2. * @brief       进入PCM 录音模式
  3. * @param       无
  4. * @retval      无
  5. */
  6. void recoder_enter_rec_mode(void)
  7. {
  8.     es8388_adda_cfg(0, 1);                  /* 开启ADC */
  9.     es8388_input_cfg(0);                    /* 开启输入通道(通道1,MIC所在通道) */
  10.     es8388_mic_gain(8);                     /* MIC增益设置为最大 */
  11.     es8388_alc_ctrl(3, 4, 4);               /* 开启立体声ALC控制,以提高录音音量 */
  12.     es8388_output_cfg(0, 0);                /* 关闭通道1和2的输出 */
  13.     es8388_spkvol_set(0);                   /* 关闭喇叭. */
  14.     es8388_sai_cfg(0, 3);                   /* 飞利浦标准,16位数据长度 */
  15.    
  16.     /* 初始化I2S */
  17.     i2s_set_samplerate_bits_sample(SAMPLE_RATE,
  18.                                                I2S_BITS_PER_SAMPLE_16BIT);
  19.     i2s_trx_start();                        /* 开启I2S */
  20.     recoder_remindmsg_show(0);
  21. }
复制代码
       该函数就是用我们前面介绍的方法,激活ES8388的PCM模式,本章,我们使用的是44.1Khz采样率,16位单声道线性PCM模式。
       由于最后要把录音写入到文件,这里需要准备wav的文件头,为方便,我们定义了一个__WaveHeader结构体来定义文件头的数据字节,这个结构体包含了前面提到的wav文件的数据结构块:
  1. Typedef struct
  2. {
  3.     ChunkRIFF riff;                   /* riff块 */
  4.     ChunkFMT fmt;                       /* fmt块 */
  5.     //ChunkFACT fact;                   /* fact块 线性PCM,没有这个结构体 */
  6.     ChunkDATA data;                     /* data块 */
  7. } __WaveHeader;
复制代码
       我们定义一个recoder_wav_init()函数方便初始化文件信息,代码如下:
  1. void recoder_wav_init(__WaveHeader *wavhead)
  2. {
  3.     wavhead->riff.ChunkID = 0x46464952;           /* RIFF" */
  4.     wavhead->riff.ChunkSize = 0;                  /* 还未确定,最后需要计算 */
  5.     wavhead->riff.Format = 0x45564157;            /* "WAVE" */
  6.     wavhead->fmt.ChunkID = 0x20746D66;            /* "fmt " */
  7. wavhead->fmt.ChunkSize = 16;                  /* 大小为16个字节 */

  8. /* 0x01,表示PCM; 0x00,表示IMA ADPCM */
  9.     wavhead->fmt.AudioFormat = 0x01;
  10.     wavhead->fmt.NumOfChannels = 2;               /* 双声道 */
  11.     wavhead->fmt.SampleRate = SAMPLE_RATE;        /* 采样速率 */
  12. wavhead->fmt.ByteRate = wavhead->fmt.SampleRate * 4;

  13. /* 字节速率=采样率*通道数*(ADC位数/8) */
  14.     wavhead->fmt.BlockAlign = 4;                    /* 块大小=通道数*(ADC位数/8) */
  15.     wavhead->fmt.BitsPerSample = 16;                /* 16位PCM */
  16.     wavhead->data.ChunkID = 0x61746164;             /* "data" */
  17.     wavhead->data.ChunkSize = 0;                    /* 数据大小,还需要计算 */
  18. }
复制代码
       录音完成我们还要重新计算录音文件的大小写入文件头,以保证音频文件能正常被解析。我们把这些数据直接按顺序写入文件即可完成录音操作,结合文件操作和按键功能定义,我们用wav_recoder()函数实现录音过程,代码如下:
  1. /**
  2. * @brief       WAV录音
  3. * @param       无
  4. * @retval      无
  5. */
  6. void wav_recorder(void)
  7. {
  8.     uint8_t res;
  9.     uint8_t key;
  10.     uint8_t rval = 0;
  11.     uint32_t bw;
  12.    
  13.     __WaveHeader *wavhead = 0;
  14.    
  15.     /* 目录 */
  16.     FF_DIR recdir;
  17.    
  18.     /* 录音文件 */
  19.     FIL *f_rec;
  20.    
  21.     /* 数据缓存指针 */
  22.     uint8_t *pdatabuf;
  23.    
  24.     /* 文件名称 */
  25.     uint8_t *pname = 0;
  26.    
  27.     /* 录音时间 */
  28.     uint32_t recsec = 0;
  29.    
  30.     /* 计时器 */
  31.     uint8_t timecnt = 0;
  32.     uint16_t bytes_read = 0;

  33.     /* 打开录音文件夹 */
  34.     while (f_opendir(&recdir, "0:/RECORDER"))
  35.     {
  36.         lcd_show_string(30, 230, 240, 16, 16, "RECORDER folder error!", RED);
  37.         vTaskDelay(200);
  38.         
  39.         /* 清除显示 */
  40.         lcd_fill(30, 230, 240, 246, WHITE);
  41.         vTaskDelay(200);
  42.         
  43.         /* 创建该目录 */
  44.         f_mkdir("0:/RECORDER");
  45.     }

  46.     /* 录音存储区 */
  47.     pdatabuf = malloc(1024 * 10);
  48.    
  49.     /* 开辟FIL字节的内存区域 */
  50.     f_rec = (FIL*)malloc(sizeof(FIL));
  51.    
  52.     /* 开辟__WaveHeader字节的内存区域 */
  53.     wavhead = (__WaveHeader *)malloc(sizeof(__WaveHeader));
  54.    
  55.     /* 申请30个字节内存,文件名类似"0:RECORDER/REC00001.wav" */
  56.     pname = malloc(30);

  57.     if (!f_rec || !wavhead || !pname || !pdatabuf)
  58.     {
  59.         /* 任意一项失败, 则失败 */
  60.         rval = 1;
  61.     }

  62.     if (rval == 0)
  63.     {
  64.         /* 进入录音模式,此时耳机可以听到咪头采集到的音频 */
  65.         recoder_enter_rec_mode();
  66.         
  67.         /* pname没有任何文件名 */
  68.         pname[0] = 0;

  69.         while (rval == 0)
  70.         {
  71.             key = xl9555_key_scan(0);

  72.             switch (key)
  73.             {
  74.                 /* STOP&SAVE */
  75.                 case KEY2_PRES:
  76.                
  77.                     /* 有录音 */
  78.                     if (g_rec_sta & 0x80)
  79.                     {
  80.                         /* 关闭录音 */
  81.                         g_rec_sta = 0;
  82.                         
  83.                         /* 整个文件的大小-8; */
  84.                         wavhead->riff.ChunkSize = g_wav_size + 36;
  85.                         
  86.                         /* 数据大小 */
  87.                         wavhead->data.ChunkSize = g_wav_size;
  88.                         
  89.                         /* 偏移到文件头. */
  90.                         f_lseek(f_rec, 0);
  91.                         
  92.                         /* 写入头数据 */
  93.                         f_write(f_rec,
  94.                                  (const void *)wavhead,
  95.                                   sizeof(__WaveHeader),
  96.                                   &bw);
  97.                                 
  98.                         f_close(f_rec);
  99.                         g_wav_size = 0;
  100.                     }

  101.                     g_rec_sta = 0;
  102.                     recsec = 0;
  103.                     
  104.                     /* 关闭DS0 */
  105.                     LED(1);
  106.                     
  107.                     /* 清除显示,清除之前显示的录音文件名 */
  108.                     lcd_fill(30,
  109.                                190,
  110.                                lcd_self.width,
  111.                                lcd_self.height,
  112.                                WHITE);
  113.                     break;

  114.                 /* REC/PAUSE */
  115.                 case KEY0_PRES:
  116.                
  117.                     /* 如果是暂停,继续录音 */
  118.                     if (g_rec_sta & 0x01)
  119.                     {
  120.                         /* 取消暂停 */
  121.                         g_rec_sta &= 0xFE;
  122.                     }
  123.                     
  124.                     /* 已经在录音了,暂停 */
  125.                     else if (g_rec_sta & 0x80)
  126.                     {
  127.                         /* 暂停 */
  128.                         g_rec_sta |= 0x01;
  129.                     }
  130.                     
  131.                     /* 还没开始录音 */
  132.                     else
  133.                     {
  134.                         recsec = 0;
  135.                         
  136.                         /* 得到新的名字 */
  137.                         recoder_new_pathname(pname);
  138.                         text_show_string(30,
  139.                                             190,
  140.                                             lcd_self.width,
  141.                                             16,
  142.                                             "录制:",
  143.                                             16,
  144.                                             0,
  145.                                             RED);
  146.                         
  147.                         /* 显示当前录音文件名字 */
  148.                         text_show_string(30 + 40,
  149.                                             190,
  150.                                             lcd_self.width,
  151.                                             16,
  152.                                             (char *)pname + 11,
  153.                                             16,
  154.                                             0,
  155.                                             RED);
  156.                         
  157.                         /* 初始化wav数据 */
  158.                         recoder_wav_init(wavhead);
  159.                         
  160.                         /* 打开文件 */
  161.                         res = f_open(f_rec,
  162.                                        (const TCHAR*)pname,
  163.                                         FA_CREATE_ALWAYS |
  164.                                         FA_WRITE);
  165.                         
  166.                         /* 文件创建失败 */
  167.                         if (res)
  168.                         {
  169.                             /* 创建文件失败,不能录音 */
  170.                             g_rec_sta = 0;
  171.                            
  172.                             /* 提示是否存在SD卡 */
  173.                             rval = 0xFE;
  174.                         }
  175.                         else
  176.                         {
  177.                             /* 写入头数据 */
  178.                             res = f_write(f_rec,
  179.                                             (const void *)wavhead,
  180.                                              sizeof(__WaveHeader),
  181.                                             (UINT*)&bw);
  182.                                          
  183.                             recoder_msg_show(0, 0);
  184.                            
  185.                             /* 开始录音 */
  186.                             g_rec_sta |= 0x80;
  187.                         }
  188.                     }

  189.                     if (g_rec_sta & 0x01)
  190.                     {
  191.                         /* 提示正在暂停 */
  192.                         LED(0);
  193.                     }
  194.                     else
  195.                     {
  196.                         LED(1);
  197.                     }
  198.                     break;

  199.                 /* 播放最近一段录音 */
  200.                 case KEY3_PRES:
  201.                
  202.                     /* 没有在录音 */
  203.                     if (g_rec_sta != 0x80)
  204.                     {
  205.                         /* 如果按键被按下,且pname不为空 */
  206.                         if (pname[0])
  207.                         {
  208.                             text_show_string(30,
  209.                             190,
  210.                             lcd_self.width, 16, "播放:", 16, 0, RED);
  211.                            
  212.                             /* 显示当播放的文件名字 */
  213.                             text_show_string(30 + 40,
  214.                                                 190,
  215.                                                 lcd_self.width,
  216.                                                 16,
  217.                                                 (char *)pname + 11,
  218.                                                 16,
  219.                                                 0,
  220.                                                 RED);
  221.                            
  222.                             /* 进入播放模式 */
  223.                             recoder_enter_play_mode();
  224.                            
  225.                             /* 播放pname */
  226.                             audio_play_song(pname);
  227.                            
  228.                             /* 清除显示,清除之前显示的录音文件名 */
  229.                             lcd_fill(30,
  230.                                        190,
  231.                                        lcd_self.width,
  232.                                        lcd_self.height,
  233.                                        WHITE);
  234.                            
  235.                             /* 重新进入录音模式 */
  236.                             recoder_enter_rec_mode();
  237.                         }
  238.                     }
  239.                     break;
  240.             }

  241.             if ((g_rec_sta & 0x80) == 0x80)
  242.             {
  243.                 if ((g_rec_sta & 0x01) == 0x00)
  244.                 {
  245.                     bytes_read = i2s_rx_read((uint8_t *)pdatabuf, 1024 * 10);
  246.                     
  247.                     /* 写入文件 */
  248.                     res = f_write(f_rec, pdatabuf, bytes_read, (UINT*)&bw);

  249.                     if (res)
  250.                     {
  251.                         printf("write error:%d\r\n", res);
  252.                     }

  253.                     /* WAV数据大小增加 */
  254.                     g_wav_size += bytes_read;
  255.                 }
  256.             }
  257.             else
  258.             {
  259.                 vTaskDelay(1);
  260.             }

  261.             timecnt++;

  262.             if ((timecnt % 20) == 0)
  263.             {
  264.                 /* LED闪烁 */
  265.                 LED_TOGGLE();
  266.             }


  267.             /* 录音时间显示 */
  268.             if (recsec != (g_wav_size / wavhead->fmt.ByteRate))
  269.             {
  270.                 /* 录音时间 */
  271.                 recsec = g_wav_size / wavhead->fmt.ByteRate;
  272.                
  273.                 /* 显示码率 */
  274.                 recoder_msg_show(recsec,
  275.                                     wavhead->fmt.SampleRate *
  276.                                     wavhead->fmt.NumOfChannels *
  277.                                     wavhead->fmt.BitsPerSample);
  278.             }
  279.         }
  280.     }

  281.     /* 释放内存 */
  282.     free(pdatabuf);
  283.    
  284.     /* 释放内存 */
  285.     free(f_rec);
  286.    
  287.     /* 释放内存 */
  288.     free(wavhead);
  289.    
  290.     /* 释放内存 */
  291. free(pname);
复制代码

       42.3.4 CMakeLists.txt文件
       打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:

  1. set(src_dirs
  2.             IIC
  3.             LCD
  4.             LED
  5.             SDIO
  6.             SPI
  7.             XL9555
  8.             ES8388
  9.             I2S)

  10. set(include_dirs
  11.             IIC
  12.             LCD
  13.             LED
  14.             SDIO
  15.             SPI
  16.             XL9555
  17.             ES8388
  18.             I2S)

  19. set(requires
  20.             driver
  21.             fatfs)

  22. idf_component_register(SRC_DIRS ${src_dirs}
  23. INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

  24. component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
复制代码
       上述的红色I2C、ES8388驱动需要由开发者自行添加,以确保录音驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了录音驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
       打开本实验main文件下的CMakeLists.txt文件,其内容如下所示:
  1. idf_component_register(
  2.     SRC_DIRS
  3.         "."
  4.         "app"
  5.     INCLUDE_DIRS
  6.         "."
  7.         "app")
复制代码
       上述的红色app驱动需要由开发者自行添加,在此便不做赘述了。

       42.3.5 实验应用代码
       打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
  1. i2c_obj_t i2c0_master;

  2. /**
  3. * @brief       程序入口
  4. * @param       无
  5. * @retval      无
  6. */
  7. void app_main(void)
  8. {
  9.     esp_err_t ret;
  10.     uint8_t key = 0;

  11.     /* 初始化NVS */
  12.     ret = nvs_flash_init();

  13. if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
  14.                 ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  15.     {
  16.         ESP_ERROR_CHECK(nvs_flash_erase());
  17.         ret = nvs_flash_init();
  18.     }

  19.     /* 初始化LED */
  20.     led_init();
  21.    
  22.     /* 初始化IIC0 */
  23.     i2c0_master = iic_init(I2C_NUM_0);
  24.    
  25.     /* 初始化SPI */
  26.     spi2_init();
  27.    
  28.     /* 初始化IO扩展芯片 */  
  29.     xl9555_init(i2c0_master);
  30.    
  31.     /* 初始化LCD */
  32.     lcd_init();

  33.     /* ES8388初始化 */
  34.     es8388_init(i2c0_master);
  35.    
  36.     /* 设置耳机音量 */
  37.     es8388_hpvol_set(25);
  38.    
  39.     /* 设置喇叭音量 */
  40.     es8388_spkvol_set(25);
  41.    
  42.     /* 打开喇叭 */
  43.     xl9555_pin_write(SPK_EN_IO,0);
  44.    
  45.     /* I2S初始化 */
  46.     i2s_init();

  47.     /* 检测不到SD卡 */
  48.     while (sd_spi_init())
  49.     {
  50.         lcd_show_string(30, 110, 200, 16, 16, "SD Card Error!", RED);
  51.         vTaskDelay(500);
  52.         lcd_show_string(30, 130, 200, 16, 16, "Please Check! ", RED);
  53.         vTaskDelay(500);
  54.     }

  55.     /* 检查字库 */
  56.     while (fonts_init())
  57.     {
  58.         /* 清屏 */
  59.         lcd_clear(WHITE);
  60.         lcd_show_string(30, 30, 200, 16, 16, "ESP32-S3", RED);
  61.         
  62.         /* 更新字库 */
  63.         key = fonts_update_font(30, 50, 16, (uint8_t *)"0:", RED);

  64.         /* 更新失败 */
  65.         while (key)
  66.         {
  67.             lcd_show_string(30, 50, 200, 16, 16, "Font Update Failed!", RED);
  68.             vTaskDelay(200);
  69.             lcd_fill(20, 50, 200 + 20, 90 + 16, WHITE);
  70.             vTaskDelay(200);
  71.         }

  72.         lcd_show_string(30, 50, 200, 16, 16, "Font Update Success!   ", RED);
  73.         vTaskDelay(1500);
  74.         
  75.         /* 清屏 */
  76.         lcd_clear(WHITE);
  77.     }
  78.    
  79.     /* 为fatfs相关变量申请内存 */
  80.     ret = exfuns_init();
  81.    
  82.     /* 实验信息显示延时 */
  83.     vTaskDelay(500);

  84.     text_show_string(30, 50, 200, 16, "正点原子ESP32开发板", 16, 0, RED);
  85.     text_show_string(30, 70, 200, 16, "WAV 录音机 实验", 16, 0, RED);
  86.     text_show_string(30, 90, 200, 16, "正点原子@ALIENTEK", 16, 0, RED);

  87.     while (1)
  88.     {
  89.         /* 录音 */
  90.         wav_recorder();
  91.     }
  92. }
复制代码
       可以看到main函数与音乐播放器实验十分类似,封装好了APP,main函数会精简很多。

       42.4 下载验证
       在代码编译成功之后,我们下载代码到正点原子DNESP32S3开发板上,先初始化各外设,然后检测字库是否存在,如果检测无问题,再检测SD卡根目录是否存在RECORDER文件夹,如果不存在则创建,如果创建失败,则报错。在找到SD卡的RECORDER文件夹后,即进入录音模式(包括配置ES8388和I²S等),此时可以在耳机(或喇叭)听到采集到的音频。KEY0用于开始/暂停录音,KEY2用于保存并停止录音,KEY3用于播放最近一次的录音。

第四十二章 录音机实验16845.png
图42.4.1 录音机实验界面

       此时,我们按下KEY0就开始录音了,此时看到屏幕显示录音文件的名字以及录音时长,如图42.4.2所示:

第四十二章 录音机实验16915.png
图42.4.2 录音进行中

       在录音的时候按下KEY0则执行暂停/继续录音的切换,通过LED指示录音暂停。通过按下KEY2,可以停止当前录音,并保存录音文件。在完成一次录音文件保存之后,我们可以通过按KEY3按键,来实现播放这个录音文件(即播放最近一次的录音文件),实现试听。
       我们可以把录音完成的wav文件放到电脑上,可以通过一些播放软件播放并查看详细的音频编码信息,本例程使用的是KMPlayer播放,查看到的信息如图42.4.3所示:

第四十二章 录音机实验17137.png
图42.4.3 录音文件属性

       这和我们程序设计时的效果一样,通过电脑端的播放器可以直接播放我们所录的音频。经实测,效果还是非常不错的。

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-25 06:15

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表