JavaScript 实现页面播放特定音符(如:C4)
Posted NemoHi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript 实现页面播放特定音符(如:C4)相关的知识,希望对你有一定的参考价值。
使用 MIDI.js 实现
https://github.com/mudcube/MIDI.js
便于我们学习,首先对项目进行克隆,打开VsCode,新建一个文件夹,终端 cd命令 进入该目录,输入命令:
git clone https://github.com/mudcube/MIDI.js.git
我们使用VsCode的Live Server插件打开下图路径下的网页
我们打开该页面可知,Basic 页面就是一个发出特定音符的案例 ,我们对其源码进行分析即可!(当然,也可以阅读其文档)
找到页面的源代码
不难看出,关键代码即
window.onload = function ()
MIDI.loadPlugin(
soundfontUrl: "./soundfont/",
instrument: "acoustic_grand_piano",
onprogress: function(state, progress)
console.log(state, progress);
,
onsuccess: function()
var delay = 0; // play one note every quarter second
var note = 50; // the MIDI note
var velocity = 127; // how hard the note hits
// play the note
MIDI.setVolume(0, 127);
MIDI.noteOn(0, note, velocity, delay);
MIDI.noteOff(0, note, delay + 0.75);
);
;
我们可以稍微修改这个页面 DOM,添加一个按钮,让按钮点击事件触发此函数。
<body>
<button onclick="playNote()">点击播放</button>
<script type="text/javascript">
function playNote()
MIDI.loadPlugin(
soundfontUrl: "./soundfont/",
instrument: "acoustic_grand_piano",
onprogress: function (state, progress)
console.log(state, progress);
,
onsuccess: function ()
var delay = 0; // play one note every quarter second
var note = 50; // the MIDI note
var velocity = 127; // how hard the note hits
// play the note
MIDI.setVolume(0, 127);
MIDI.noteOn(0, note, velocity, delay);
MIDI.noteOff(0, note, delay + 0.75);
);
;
</script>
</body>
这时,我们点击按钮就完成了播放一个音符。
如何指定播放特定音符?
首先我们要知道少许的 MIDI 原理
将变量 note 值查表改为对应的值即可发出指定的音符
例如:我想发出 so (5),对应是 G,因此我们输入列名为 G 下的任意一个数字(7/19/31...)即可(都是 so 只是音高不同)
同样,更改 delay 变量的值和 velocity 的值可以调整播放的参数。
如何通过数字得到音符的具体字符串?
const noteMap =
0: 'C',
1: 'C#',
2: 'D',
3: 'D#',
4: 'E',
5: 'F',
6: 'F#',
7: 'G',
8: 'G#',
9: 'A',
10: 'A#',
11: 'B'
window.onload = function ()
//页面加载时生成一个随机数代表音符(超高难度 0-127)
noteCode = parseInt(Math.random() * 127);
const tone = noteMap[(noteCode % 12)];
const groupNum = parseInt(noteCode / 12);
ans = tone + groupNum;
console.log('ans:', ans, 'code', noteCode);
CH582M,PWM模拟DAC实现WAV播放,FATFS文件
一、
利用PWM、RC电路、TCB8002D音频功率放大器。通过调制PWM的占空比输出wav音频。
不是通过调整(一开始方向搞错)
- 频率(调整音调)
- 通过delay_ms延时函数来实现四分之一音符、二分之一音符、全音符。
- 占空比调整音量
二、
首先配置输出PWM输出频率,源文件为44.1KHz的采样频率,所以PWM配置输出为不小于44KHz(影响播放速度,小了播放慢、大了播放快)。
SetSysClock(CLK_SOURCE_PLL_60MHz); //系统时钟
GPIOB_ModeCfg(GPIO_Pin_5, GPIO_ModeOut_PP_5mA); //使能TCB8002D音频功率放大器
GPIOB_SetBits(GPIO_Pin_5);
GPIOB_ModeCfg(GPIO_Pin_0, GPIO_ModeOut_PP_5mA); // 配置PWM输出
PWMX_CLKCfg(5); // cycle = 4/Fsys 分频
PWMX_CycleCfg(PWMX_Cycle_256); // 周期 = 64*cycle 重复计数值
我们可以通过GoldWave将源音频文件转换为44.1KHz8Bit的wav,再理由WinHex获取C数组形式的数据。
#define maxSize sizeof(wavdat)
const unsigned wavdat[] =
0x75, 0x74, 0x73, 0x72, 0x71, 0x70, 0x6F, 0x6F, 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x79, 0x79,
0x7A, 0x7A, 0x7A, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7C, 0x7D, 0x7F, 0x80, 0x81,
0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x83, 0x84, 0x84, 0x84, 0x84, 0x83, 0x82, 0x81,
0x80, 0x80, 0x81, 0x83, 0x85, 0x87, 0x89, 0x8A, 0x8C, 0x8D, 0x8F, 0x8F, 0x8F, 0x8E, 0x8D, 0x8B,
0x8A, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x87, 0x85, 0x83, 0x81, 0x80, 0x7E, 0x7D, 0x7D, 0x7C,
0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, 0x7D, 0x7D, 0x7F, 0x80, 0x80, 0x81, 0x81,
0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7D, 0x7E,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x81, 0x82, 0x84, 0x86, 0x87, 0x89, 0x8B, 0x8C,
0x8C, 0x8D, 0x8D, 0x8C, 0x8C, 0x8D, 0x8D, 0x8D, 0x8D, 0x8C, 0x8C, 0x8A, 0x89, 0x87, 0x87, 0x86,
0x86, 0x85, 0x85, 0x84, 0x83, 0x83, 0x82, 0x82, 0x82, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x7F,
0x7F, 0x7E, 0x7D, 0x7B, 0x7A, 0x79, 0x77, 0x76, 0x75, 0x75, 0x76, 0x77, 0x79, 0x7B, 0x7C, 0x7E,
0x7E, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x81, 0x81, 0x82, 0x83, 0x85, 0x86, 0x87,
...........
读取数据信息
while(1)
PWMX_ACTOUT(CH_PWM6,(wavdat[wavIndex]), Low_Level, ENABLE); // 25% 占空比
if(++wavIndex>=maxSize)wavIndex=0;
参考文章:
使用PWM实现语音播放_嵌入式Linux,的博客-CSDN博客
STM32F103使用TIM DMA DAC实现播放WAV音乐_liqiang420795936的博客-CSDN博客_stm32驱动喇叭播放音乐c语言 输出音频 单片机,单片机播放WAV格式音频的理解_张溪梦 Simon的博客-CSDN博客TTS(1)单片机播放WAV语音,有原理,有代码_乘简的博客-CSDN博客_wav播放原理基于PWM控制的声音播放的实现_天上人间555的博客-CSDN博客_pwm 语音
三、 读取TF卡内WAV音频文件播放音乐
前面需要注意的是不能只单一个PWM直接扔数据給他,音频文件为44.1KHz所以要固定频率更新数据,利用定时器每隔 (44.1 kHz)产生一次中断来更新数据,而不是直接扔数据主频8Mhz直接扔数据一下会扔很多数据。
1:FATFS基础知识(23.2.1):文件系统FATFS的移植教程_sunshine_SGP的博客-CSDN博客_fatfs移植
2:SD卡的读取
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "tftlcd.h"
#include "key.h"
#include "malloc.h"
#include "sd.h"
#include "flash.h"
#include "ff.h"
#include "fatfs_app.h"
#include "diskio.h"
#include "integer.h"
#include "string.h"
FATFS fsobject;
FIL SDFile ;
uint8_t readBuf[512];
uint8_t writeBuf[512];
BYTE work[_MAX_SS];
char *fileName = "test.txt";
uint32_t writeLen;
uint32_t readLen;
int main(void)
FRESULT retSD ;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
USART1_Init(115200);
TFTLCD_Init(); //LCD初始化
// 挂载
retSD = f_mount(&fsobject, "0:", 1);
switch(retSD)
case FR_NO_FILESYSTEM: // FM_FAT32
retSD = f_mkfs("0:",0,sizeof(work));
if(retSD!=FR_OK)
printf("格式化失败\\r\\n");
while(1);
break;
case FR_OK:
printf("挂载成功\\r\\n");
break;
default:
while(1);
// 写
retSD = f_open(&SDFile,fileName, FA_CREATE_ALWAYS | FA_WRITE);
if(retSD!=FR_OK)
printf("打开失败\\r\\n");
while(1);
printf("打开成功\\r\\n");
sprintf((char *)writeBuf,"%s","abc123今天天气还可以---FATFStest\\r\\n");
retSD = f_write(&SDFile,writeBuf, strlen((const char *)writeBuf), &writeLen);
if(retSD!=FR_OK)
printf("写失败\\r\\n");
while(1);
printf("写成功\\r\\n");
f_close(&SDFile);
// 读
retSD = f_open(&SDFile,fileName, FA_OPEN_EXISTING | FA_READ);
if(retSD!=FR_OK)
while(1);
printf("打开成功\\r\\n");
retSD = f_read(&SDFile,readBuf, 50, &readLen);
if(retSD!=FR_OK)
while(1);
printf("读到的数据为%s\\r\\n",readBuf);
LCD_ShowString(10,180,tftlcd_data.width,tftlcd_data.height,16,readBuf);
f_close(&SDFile);
// 卸载
f_mount(NULL, "0:", 1);
while (1)
3.1挂载FATFS文件管理系统
通过文件管理系统读取我们需要的wav文件里的数据。d移植自:
【CH579开发板】+分享TF卡的FATFS例子 - - 21ic电子技术开发论坛
/******************SD卡初始化***********************/
if(MSD_Init())
printf("error!\\n");
else
MSD_GetCardInfo(&cardinfo);
printf("OK\\n");
if(MSD_ReadSingleBlock(0,buf) == 0) //读取MBR扇数据打
printf("TF Card Sector Data:\\n");
p = buf;
for(i=0; i!=512/16; i++)
for(j=0; j!=16; j++)
printf("%02X ",*p++);
printf("\\n");
/******************FATFFS***********************/
f_mount(&fs,"0:",1); //挂载SD卡
3.2配置TIM定时器
配置定时器,在定时器中断设置更新数据的标志位,定时器到了就更新PWM占空数据。
/******************定时器初始化***********************/
TMR0_TimerInit(FREQ_SYS / 44100); // 设置定时时间
TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END); // 开启中断
PFIC_EnableIRQ(TMR0_IRQn);
/******************定时中断***********************/
void TMR0_IRQHandler(void) // TMR0 定时中断
if(TMR0_GetITFlag(TMR0_3_IT_CYC_END))
TMR0_ClearITFlag(TMR0_3_IT_CYC_END); // 清除中断标志
GPIOB_InverseBits(GPIO_Pin_1);
capFlag = 1;
3.3读取数据播放音乐
一次读写512个字节数或512倍的数据到缓冲区,tf卡读写操作时每次是按块来读写,最小块是512个字节,文件系统读取TF卡数据是一次读一个块到文件系统的缓存区。
注意点:WAV格式是有符号的整型,所以要将源数据进行转换成无符号整型加上0x80后转换为u8类型。
int readdata(void )
FATFS fs; //逻辑磁盘工作区
FIL file; //文件
FRESULT res;//状态变量
UINT br; /* 文件读/写字节计数 */
u8 table[1024];
int32_t i=0 ;
res=f_open(&file,"0:/1.wav",FA_OPEN_EXISTING|FA_READ);//
if(res) //打开文件错误
printf("open file error.\\r\\n");
return 0;
f_read (&file,table,sizeof(table),&br); //读取文件到buf
while(1) //f_eof - 测试一个文件是否到达文件末尾
if(capFlag ==1)
capFlag=0;
PWMX_ACTOUT(CH_PWM6,(255-((u8)(table[i]+0x80))), High_Level, ENABLE);
i++;
if(i>=sizeof(table))
f_read (&file,table,sizeof(table),&br);
i=0;
// if(res||br==0) f_close(&file); /* 文件结束错误 */
// printf("文件大小%d\\n",f_size(&file));//获取文件大小
f_close(&file); //关闭文件
return 0;
参考文章:
STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)_Leung_ManWah的博客-CSDN博客_fatfs格式化sd卡FatFs 的用户层API接口应用简单介绍(基于STM32F1)_喜暖知寒的博客-CSDN博客
STM32基于 FatFs R0.14b&SD Card 的MP3音乐播放器(也算是FatFs的简单应用了吧)_喜暖知寒的博客-CSDN博客_stm32播放mp3
以上是关于JavaScript 实现页面播放特定音符(如:C4)的主要内容,如果未能解决你的问题,请参考以下文章
CH582M,PWM模拟DAC实现WAV播放,FATFS文件