OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 197|回复: 6

世嘉SMS模拟器移植到103,声音不正常

[复制链接]

13

主题

155

帖子

0

精华

高级会员

Rank: 4

积分
753
金钱
753
注册时间
2018-12-19
在线时间
157 小时
发表于 2024-4-23 10:59:51 | 显示全部楼层 |阅读模式
1金钱
这几天把429综合例程的SMS模拟器移植到103,游戏画面还是很流畅的,就是声音不正常,不知道SMS的声音是单声道还是双声道,采样深度是8位还是16位,试了几种都不太理想。
下面是移植好的程序
战舰V3 STM32F103 实验54 综合测试实验 2024-04-22.rar (4.44 MB, 下载次数: 1)

最佳答案

查看完整内容[请看2#楼]

NES游戏模拟器输出的声音数据是wav信号,用VS1053播放wav数据需要一个wav头,wav头就是wav声音数据文件的描述,用来告诉VS1053这些wav声音的属性。 下面是正点原子例程提供的wav头 这些数据一看就头大,后来发现里面有两个0x11,0x2B,由于是小端模式,还原成正常的数据就是0x2B11,也就是11025,说明这里是采样率,我就在sms模拟器里面把这个采样率换了好几种,单声道,双声道,8位采样,16位采样,无论怎么组合声音效果始终是不 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

13

主题

155

帖子

0

精华

高级会员

Rank: 4

积分
753
金钱
753
注册时间
2018-12-19
在线时间
157 小时
 楼主| 发表于 2024-4-23 10:59:52 | 显示全部楼层
本帖最后由 854278507 于 2024-4-25 10:00 编辑

NES游戏模拟器输出的声音数据是wav信号,用VS1053播放wav数据需要一个wav头,wav头就是wav声音数据文件的描述,用来告诉VS1053这些wav声音的属性。
下面是正点原子例程提供的wav头

  1. /* NES模拟器声音从VS1053输出,模拟WAV解码的wav头数据 */
  2. const uint8_t nes_wav_head[] =
  3. {
  4.     0X52, 0X49, 0X46, 0X46, 0XFF, 0XFF, 0XFF, 0XFF, 0X57, 0X41, 0X56, 0X45, 0X66, 0X6D, 0X74, 0X20,
  5.     0X10, 0X00, 0X00, 0X00, 0X01, 0X00, 0X01, 0X00, 0X11, 0X2B, 0X00, 0X00, 0X11, 0X2B, 0X00, 0X00,
  6.     0X01, 0X00, 0X08, 0X00, 0X64, 0X61, 0X74, 0X61, 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00,
  7.     0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  8. };
复制代码
这些数据一看就头大,后来发现里面有两个0x11,0x2B,由于是小端模式,还原成正常的数据就是0x2B11,也就是11025,说明这里是采样率,我就在sms模拟器里面把这个采样率换了好几种,单声道,双声道,8位采样,16位采样,无论怎么组合声音效果始终是不好,很大杂音。
昨天准备把SMS模拟器输出的声音数据保存到sd卡上,然后用别的播放器播放一下,看这个SMS模拟器输出的到底是什么格式的wav文件。
就在添加写文件功能的时候发现了问题所在。
下面是正点原子的打开声音输出的函数
  1. /**
  2. * @brief       NES打开音频输出
  3. * [url=home.php?mod=space&uid=271674]@param[/url]       samples_per_sync: 样本同步(未用到)
  4. * @param       sample_rate     : 音频采样率
  5. * @retval      0, 成功;
  6. *              其他, 失败;
  7. */
  8. int nes_sound_open(int samples_per_sync, int sample_rate)
  9. {
  10.     uint8_t *p;
  11.     uint8_t i;
  12.     p = mymalloc(SRAMIN, 100);  /* 申请100字节内存 */

  13.     if (p == NULL)return 1;     /* 内存申请失败,直接退出 */

  14.     printf("sound open:%d\r\n", sample_rate);

  15.     for (i = 0; i < sizeof(nes_wav_head); i++)  /* 复制nes_wav_head内容 */
  16.     {
  17.         p[i] = nes_wav_head[i];
  18.     }

  19.     if (lcddev.width == 480)    /* 是480*480屏幕 */
  20.     {
  21.         sample_rate = 8000;     /* 设置8Khz,约原来速度的0.75倍 */
  22.     }

  23.     p[24] = sample_rate & 0XFF; /* 设置采样率 */
  24.     p[25] = (sample_rate >> 8) & 0XFF;
  25.     p[28] = sample_rate & 0XFF; /* 设置字节速率(8位模式,等于采样率) */
  26.     p[29] = (sample_rate >> 8) & 0XFF;
  27.     nesplaybuf = 0;
  28.     nessavebuf = 0;

  29.     vs10xx_reset();             /* 硬复位 */
  30.     vs10xx_soft_reset();        /* 软复位 */
  31.     vs10xx_set_all();           /* 设置音量等参数 */
  32.     vs10xx_reset_decode_time(); /* 复位解码时间 */

  33.     while (vs10xx_send_music_data(p));      /* 发送wav head */

  34.     while (vs10xx_send_music_data(p + 32)); /* 发送wav head */

  35.     tim6_int_init(100 - 1, 1280 - 1);       /* 1ms中断一次 */

  36.     myfree(SRAMIN, p);          /* 释放内存 */
  37.     return 0;
  38. }
复制代码
在打开声音的函数里面定义了一个指针p,并给这个指针p申请了内存,然后把wav头的数组内容复制到了指针p所指向的地址,然后又根据采样率修改了指针p所指针地址的数据,难怪我无论怎么修改那个wav头都不行,原来并不是直接wav头直接送到VS1053的,在发送到VS1053之前做了修改。
于是我就把打开声音的函数做了修改,下面是我修改后的代码
  1. /**
  2. * @brief       SMS打开音频输出
  3. * @param       sample_rate     : 音频采样率
  4. * @retval      无
  5. */
  6. void sms_sound_open(int sample_rate)
  7. {
  8.     uint8_t *p;
  9.     uint8_t i;
  10.     p = mymalloc(SRAMIN, 64); // 申请100字节内存
  11.     if (p == NULL)            // 内存申请失败,直接退出
  12.     {
  13.         return;
  14.     }
  15.     printf("\r\n[%s][%s][%04d]sound open:%d", __FILE__, __func__, __LINE__, sample_rate);

  16.     memset(p, 0x00, 64);

  17.     if (lcddev.width == 480) // 是480*480屏幕
  18.     {
  19.         sample_rate = 8000; // 设置8Khz,约原来速度的0.75倍
  20.     }

  21.     /* 下面是wav头 */
  22.     p[0] = 'R'; /* "RIFF" */
  23.     p[1] = 'I';
  24.     p[2] = 'F';
  25.     p[3] = 'F';
  26.    
  27.     *(uint32_t *)&p[4] = 0xFFFFFFFF; /* 文件大小 */
  28.    
  29.     p[8]  = 'W'; /* "WAVE" */
  30.     p[9]  = 'A';
  31.     p[10] = 'V';
  32.     p[11] = 'E';
  33.    
  34.     p[12] = 'f'; /* "fmt " */
  35.     p[13] = 'm';
  36.     p[14] = 't';
  37.     p[15] = ' ';
  38.    
  39.     *(uint32_t *)&p[16] = 16;          /* 后面还有16个字节的数据 */
  40.     *(uint32_t *)&p[20] = 1;           /* 文件格式:wav为1 */
  41.     *(uint16_t *)&p[22] = 2;           /* 双声道 */
  42.     *(uint16_t *)&p[34] = 16;          /* 采样深度:16位 */
  43.     *(uint32_t *)&p[24] = sample_rate; /* 采样率 */

  44.     *(uint16_t *)&p[32] = *(uint16_t *)&p[22] * (*(uint16_t *)&p[34] / 8); /* 字节对齐 */
  45.     *(uint32_t *)&p[28] = sample_rate * *(uint16_t *)&p[32];               /* 比特率 */
  46.    
  47.     p[36] = 'd'; /* "data" */
  48.     p[37] = 'a';
  49.     p[38] = 't';
  50.     p[39] = 'a';
  51.    
  52.     *(uint32_t *)&p[40] = 0xFFFFFFFF; /* 数据大小 */

  53.     printf("\r\n\r\n");
  54.     printf("\r\n[%s][%s][%04u]WAV文件头", __FILE__, __func__, __LINE__);
  55.     printf("\r\n");
  56.     uint8_t k;
  57.     k = 0;
  58.     for (i = 0; i < 64; i++)
  59.     {
  60.         printf("0x%02X ", p[i]);
  61.         k++;
  62.         if (k >= 4)
  63.         {
  64.             k = 0;
  65.             printf("\r\n");
  66.         }
  67.     }

  68.     printf("\r\n\r\n");

  69.     smsbufpos = 0;
  70.     smsplaybuf = 0; // 即将播放的音频帧缓冲编号
  71.     smssavebuf = 0; // 当前保存到的音频缓冲编号

  72. #ifdef __VS10xX_H__

  73.     VS_HD_Reset();         // 硬复位
  74.     VS_Soft_Reset();       // 软复位
  75.     VS_Set_All();          // 设置音量等参数
  76.     VS_Reset_DecodeTime(); // 复位解码时间
  77.     while (VS_Send_MusicData(&p[0]))
  78.         ; // 发送wav head
  79.     while (VS_Send_MusicData(&p[32]))
  80.         ;                            // 发送wav head
  81.     TIM6_Int_Init(250 - 1, 128 - 1); // 0.25ms中断一次

  82. #endif /* __VS10xX_H__ */

  83.     myfree(SRAMIN, p); // 释放内存
  84. }
复制代码

从上面的函数可以看出不再需要wav头那个数组了,我定义了一个指针p,然后给这个指针p申请内存,然后直接修改指针p指向地址的内容,并写了注释。
上面的函数里面修改指针p所指向的数据最好是用结构体的方式,会比较直观一些,由于偷懒和为了让大家看明白这个wav头内容的修改就没有写成结构体的方式。
经过测试SMS模拟器输出的是双声道,16位采样,NES模拟器是单声道,8位采样。
SMS模拟器输出的声音数据量是NES模拟器输出声音数据量的4位。
准确来说SMS模拟器输出的wav字节率是NES模拟器输出的wav字节率的4倍。
wav字节率 = 声音通道数 * 采样率 * (采率深度 / 8)
SMS模拟器的wav字节率 = 2 * 11025 * (16 / 8)
NES模拟器的wav字节率 = 1 * 11025 * (8 / 8)
NES模拟器是用定时器6每1ms给VS1053发一次声音数据,SMS模拟器的声音数据量比较大,就要把定时的时间改小,字节率是4倍,定时时间就要是1/4了,就把定时器6的定时时间改成0.25ms,这样SMS模拟器输出的声音就正常了。

最后再补充一下SMS模拟器wav头数组的注释,虽然现在不需要这个数组了,但还是要了解一下这个数组的内容是什么意思。
  1. // SMS模拟器声音从VS1053输出,模拟WAV解码的wav头数据.
  2. const uint8_t sms_wav_head[] =
  3.     {
  4.         0x52, 0x49, 0x46, 0x46, /*  0 "RIFF" */
  5.         0xFF, 0xFF, 0xFF, 0xFF, /*  4 文件大小 */
  6.         0x57, 0x41, 0x56, 0x45, /*  8 "WAVE" */
  7.         0x66, 0x6D, 0x74, 0x20, /* 12 "fmt " */
  8.         0x10, 0x00, 0x00, 0x00, /* 16 后面的数据长度:0x10,后面还有16个字节的数据 */
  9.         0x01, 0x00,             /* 20 声音格式:wav为0x01 */
  10.         0x02, 0x00,             /* 22 声音通道数:
  11.                                  *    单声道:0x01
  12.                                  *    双声道:0x02
  13.                                  */
  14.         0x11, 0x2B, 0x00, 0x00, /* 24 采样率:每秒中的采样次数,可选以下三种
  15.                                  *    低质量:11025(0x2B11)
  16.                                  *    中质量:22050(0x5622)
  17.                                  *    高质量:44100(0xAC44)
  18.                                  */
  19.         0x44, 0xAC, 0x00, 0x00, /* 28 比特率:每秒中的数据字节数,采样率 * 单次采样所占用的字节数
  20.                                  *    单声道 8位采样时为采样率的1倍
  21.                                  *    单声道16位采样时为采样率的2倍
  22.                                  *    双声道 8位采样时为采样率的2倍
  23.                                  *    双声道16位采样时为采样率的4倍
  24.                                  */
  25.         0x04, 0x00,             /* 32 对齐方式
  26.                                  *    单声道 8位采样时:1字节对齐
  27.                                  *    单声道16位采样时:2字节对齐
  28.                                  *    双声道 8位采样时:2字节对齐
  29.                                  *    双声道16位采样时:4字节对齐
  30.                                  */
  31.         0x10, 0x00,             /* 34 采样深度:
  32.                                  *     8位采样:0x08
  33.                                  *    16位采样:0x10
  34.                                  */
  35.         0x64, 0x61, 0x74, 0x61, /* 36 "data" */
  36.         0xFF, 0xFF, 0xFF, 0xFF, /* 40 数据大小 */
  37.         0x00, 0x00, 0x00, 0x00, /* 44 */
  38.         0x00, 0x00, 0x00, 0x00, /* 48 */
  39.         0x00, 0x00, 0x00, 0x00, /* 52 */
  40.         0x00, 0x00, 0x00, 0x00, /* 56 */
  41.         0x00, 0x00, 0x00, 0x00, /* 60 */
  42. };
复制代码
下面是NES模拟器wav头数组的注释
  1. // NES模拟器声音从VS1053输出,模拟WAV解码的wav头数据.
  2. const uint8_t nes_wav_head[] =
  3.     {
  4.         0x52, 0x49, 0x46, 0x46, /*  0 "RIFF" */
  5.         0xFF, 0xFF, 0xFF, 0xFF, /*  4 文件大小 */
  6.         0x57, 0x41, 0x56, 0x45, /*  8 "WAVE" */
  7.         0x66, 0x6D, 0x74, 0x20, /* 12 "fmt " */
  8.         0x10, 0x00, 0x00, 0x00, /* 16 后面的数据长度:0x10,后面还有16个字节的数据 */
  9.         0x01, 0x00,             /* 20 声音格式:wav为0x01 */
  10.         0x01, 0x00,             /* 22 声音通道数:
  11.                                  *    单声道:0x01
  12.                                  *    双声道:0x02
  13.                                  */
  14.         0x11, 0x2B, 0x00, 0x00, /* 24 采样率:每秒中的采样次数,可选以下三种
  15.                                  *    低质量:11025(0x2B11)
  16.                                  *    中质量:22050(0x5622)
  17.                                  *    高质量:44100(0xAC44)
  18.                                  */
  19.         0x11, 0x2B, 0x00, 0x00, /* 28 比特率:每秒中的数据字节数,采样率 * 单次采样所占用的字节数
  20.                                  *    单声道 8位采样时为采样率的1倍
  21.                                  *    单声道16位采样时为采样率的2倍
  22.                                  *    双声道 8位采样时为采样率的2倍
  23.                                  *    双声道16位采样时为采样率的4倍
  24.                                  */
  25.         0x01, 0x00,             /* 32 对齐方式
  26.                                  *    单声道 8位采样时:1字节对齐
  27.                                  *    单声道16位采样时:2字节对齐
  28.                                  *    双声道 8位采样时:2字节对齐
  29.                                  *    双声道16位采样时:4字节对齐
  30.                                  */
  31.         0x08, 0x00,              /* 34 采样深度:
  32.                                  *     8位采样:0x08
  33.                                  *    16位采样:0x10
  34.                                  */
  35.         0x64, 0x61, 0x74, 0x61, /* 36 "data" */
  36.         0xFF, 0xFF, 0xFF, 0xFF, /* 40 数据大小 */
  37.         0x00, 0x00, 0x00, 0x00, /* 44 */
  38.         0x00, 0x00, 0x00, 0x00, /* 48 */
  39.         0x00, 0x00, 0x00, 0x00, /* 52 */
  40.         0x00, 0x00, 0x00, 0x00, /* 56 */
  41.         0x00, 0x00, 0x00, 0x00, /* 60 */
  42. };
复制代码






回复

使用道具 举报

13

主题

155

帖子

0

精华

高级会员

Rank: 4

积分
753
金钱
753
注册时间
2018-12-19
在线时间
157 小时
 楼主| 发表于 2024-4-23 11:01:05 | 显示全部楼层
NES模拟器和SMS模拟器都改成全屏运行了,我手上是9341的屏,分辨率是240*320,可以全屏显示,如果是别的分辨率有可能显示有问题。
回复

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164897
金钱
164897
注册时间
2010-12-1
在线时间
2100 小时
发表于 2024-4-24 22:52:21 | 显示全部楼层
我们的战舰综合实验不就支持嘛...
回复

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164897
金钱
164897
注册时间
2010-12-1
在线时间
2100 小时
发表于 2024-4-24 22:52:27 | 显示全部楼层
不用从429移植啊
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

13

主题

155

帖子

0

精华

高级会员

Rank: 4

积分
753
金钱
753
注册时间
2018-12-19
在线时间
157 小时
 楼主| 发表于 2024-4-25 09:14:12 | 显示全部楼层
问题解决了
回复

使用道具 举报

13

主题

155

帖子

0

精华

高级会员

Rank: 4

积分
753
金钱
753
注册时间
2018-12-19
在线时间
157 小时
 楼主| 发表于 2024-4-25 10:03:30 | 显示全部楼层
正点原子 发表于 2024-4-24 22:52
我们的战舰综合实验不就支持嘛...

战舰V3和战舰V4综合例程都没有SMS模拟器
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-10 00:31

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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