项目介绍
早期的电子风琴用涮簧装置产生混响。利用一组旋转的扬声器和一组固定的扬声器的相对运动,产生回旋音响。又在电子线路中用附加的振荡器调制音频电讯号,产生颤震音。人们将这些效果装置提取出来,在电子音乐合成器的基础上改进与发展,单独制成混响器(Reverb)、延时器(Delay)、移相器(Phaser)、弗兰格(Flanger)等,用于电吉他或其他电声乐器演奏摇滚乐。
哇音效果器(WAH)发音好似张嘴与合嘴产生的鸡——哇或哇——鸣声。缓慢时像人们切切丝语,快速时像青蛙叫,用于电吉他演奏诙谐、活泼的乐曲。也可U调出像拉弦乐器的音调。效果器的带通滤波器在低通衰减频率上设有一个共振峰值(resonant peak)。效果器上的踏板用来改变共振峰值的频率,将其上移或下移,类似于扫频。这就带来了拟声词中所谓的“哇哇”声,可以用来模仿人声或用弱音器演奏的小号声。
项目设计思路
1.通过 spi 实现SD 文件系统挂载
实现sdhc 驱动,基于硬件 spi 实现sd 卡单线挂载,主要实现wav 文件的读取,方便后续的音频播放。
sdhc 相关驱动官方SDK 已提供好,只需在项目工程中的project.mk 添加如下代码
override BOARD=FTHR_RevA
LIB_SDHC = 1
参考官方demo 可实现文件系统的挂载,文件创建、文件列举等功能。
static int mount()
{
fs = &fs_obj;
if ((err = f_mount(fs, "", 1)) != FR_OK) { //Mount the default drive to fs now
printf("Error opening SD card: %s\n", FF_ERRORS[err]);
f_mount(NULL, "", 0);
} else {
printf("SD card mounted.\n");
mounted = 1;
}
f_getcwd(cwd, sizeof(cwd)); //Set the Current working directory
return err;
}
static int getSize()
{
if (!mounted) {
mount();
}
if ((err = f_getfree(&volume, &clusters_free, &fs)) != FR_OK) {
printf("Error finding free size of card: %s\n", FF_ERRORS[err]);
f_mount(NULL, "", 0);
}
sectors_total = (fs->n_fatent - 2) * fs->csize;
sectors_free = clusters_free * fs->csize;
printf("Disk Size: %u bytes\n", sectors_total / 2);
printf("Available: %u bytes\n", sectors_free / 2);
return err;
}
static int ls()
{
if (!mounted) {
mount();
}
printf("Listing Contents of %s - \n", cwd);
if ((err = f_opendir(&dir, cwd)) == FR_OK) {
while (1) {
err = f_readdir(&dir, &fno);
if (err != FR_OK || fno.fname[0] == 0) {
break;
}
printf("%s/%s", cwd, fno.fname);
if (fno.fattrib & AM_DIR) {
printf("/");
}
printf("\n");
}
f_closedir(&dir);
} else {
printf("Error opening directory!\n");
return err;
}
printf("\nFinished listing contents\n");
return err;
}
2.通过MAX9867 实现 iis 音频播放,iic codec 配置
通过iic 实现codec 48k采样率播放,配置codec 为输出。
max9867 支持Line in /line out/麦克风输入 等多种配置,整体框图如下:
主要通过IIC 进行功能配置,这里配置为IIS master 线性输出 采样率48000 16bit 双通道格式。
int err;
uint8_t rev_id;
int psclk;
// Initialize I2C peripheral
if ((err = MXC_I2C_Init(i2c_inst, TRUE, 0)) != E_NO_ERROR)
return err;
MXC_I2C_SetFrequency(i2c_inst, I2C_FREQ);
// Initialize static I2C request members
i2c_req.i2c = i2c_inst;
i2c_req.addr = MAX9867_ADDR;
i2c_req.restart = 0;
i2c_req.callback = NULL;
// Probe for MAX9867
if ((err = reg_read(MAX9867_FF_REV_ID, &rev_id)) != E_NO_ERROR)
return err;
if (rev_id != MAX9867_REVID)
return E_NOT_SUPPORTED;
/*
Hardware configuration -- Digital Audio Interface
*/
// Shutdown MAX9867 during configuration
if ((err = reg_write(MAX9867_17_PWR_SYS, 0x00)) != E_NO_ERROR)
return err;
/* Clear all regs to POR state.
The MAX9867 does not not have an external reset signal. */
for (int i = MAX9867_04_INT_EN; i < MAX9867_17_PWR_SYS; i++) {
if ((err = reg_write(i, 0x00)) != E_NO_ERROR)
return err;
}
// Select MCLK prescaler. PSCLK divides MCLK to generate a PCLK between 10MHz and 20MHz.
if ((mclk < MHZ(10)) || (mclk > MHZ(60)))
return E_INVALID;
else if (mclk < MHZ(20))
psclk = 0x1;
else if (mclk < MHZ(40))
psclk = 0x2;
else
psclk = 0x3;
// Set prescaler, FREQ field is 0 for Normal or PLL mode
if ((err = reg_write(MAX9867_05_SYS_CLK, psclk << MAX9867_PSCLK_POS)) != E_NO_ERROR)
return err;
// // Enable PLL, NI[14:8] is 0 for PLL mode
// if ((err = reg_write(MAX9867_06_CLK_HIGH, MAX9867_PLL)) != E_NO_ERROR) return err;
// Configure codec to generate 48kHz sampling frequency in master mode
if ((err = reg_write(MAX9867_06_CLK_HIGH, MAX9867_NI_UPPER_48KHZ)) != E_NO_ERROR)
return err;
if ((err = reg_write(MAX9867_07_CLK_LOW, MAX9867_NI_LOWER_48KHZ)) != E_NO_ERROR)
return err;
if ((err = reg_write(MAX9867_09_DAI_CLOCK, MAX9867_BSEL_PCLK_DIV8)) != E_NO_ERROR)
return err;
// I2S format
if ((err = reg_write(MAX9867_08_DAI_FORMAT, MAX9867_MAS | MAX9867_DLY | MAX9867_HIZOFF)) !=
E_NO_ERROR)
return err;
// Left Justified
// if ((err = reg_write(MAX9867_08_DAI_FORMAT, MAX9867_HIZOFF)) != E_NO_ERROR) return err;
// if ((err = reg_write(MAX9867_04_INT_EN, MAX9867_SDODLY)) != E_NO_ERROR) return err;
/*
Application configuration -- Stream Interface
*/
if ((err = reg_write(MAX9867_0A_DIG_FILTER, 0xA2)) != E_NO_ERROR)
return err;
// Select Digital microphone input
if ((err = reg_write(MAX9867_15_MIC, ((0x1 << MAX9867_DIGMICR_POS)))) != E_NO_ERROR)
return err;
// ADC level
if ((err = reg_write(MAX9867_0D_LVL_ADC, (3 << MAX9867_AVL_POS) | (3 << MAX9867_AVR_POS))) !=
E_NO_ERROR)
return err;
// Set line-in level, disconnect line input from playback amplifiers
if ((err = reg_write(MAX9867_0E_LVL_LINE_IN_LEFT,
(0x0C << MAX9867_LI_GAIN_POS) | MAX9867_LI_MUTE)) != E_NO_ERROR)
return err;
if ((err = reg_write(MAX9867_0F_LVL_LINE_IN_RIGHT,
(0x0C << MAX9867_LI_GAIN_POS) | MAX9867_LI_MUTE)) != E_NO_ERROR)
return err;
// Headphone mode
if ((err = reg_write(MAX9867_16_MODE, MAX9867_STEREO_SE_CLICKLESS << MAX9867_HPMODE_POS)) !=
E_NO_ERROR)
return err;
// Set playback volume
if ((err = reg_write(MAX9867_10_VOL_LEFT, 0x04 << MAX9867_VOL_POS)) != E_NO_ERROR)
return err;
if ((err = reg_write(MAX9867_11_VOL_RIGHT, 0x04 << MAX9867_VOL_POS)) != E_NO_ERROR)
return err;
// Enable device
// return reg_write(MAX9867_17_PWR_SYS, MAX9867_SHDN | MAX9867_LNLEN | MAX9867_ADLEN | MAX9867_DALEN | MAX9867_LNREN | MAX9867_ADREN | MAX9867_DAREN);
return reg_write(MAX9867_17_PWR_SYS,
MAX9867_SHDN | MAX9867_DALEN | MAX9867_DAREN | MAX9867_ADLEN);
3.实现bandpass 时域滤波器
实现biquad 滤波器模块,实现bandpass 滤波
biquad的计算参考音效书的实现
实现代码如下:
static void updataParams(void)
{
float K = tan(M_PI*wah_cfg.f_c/48000);
float KK = K*K;
float Q = wah_cfg.f_c/wah_cfg.f_b;
wah_cfg.b0 = K/(KK*Q +K + Q);
wah_cfg.b1 = 0;
wah_cfg.b2 = -wah_cfg.b0;
wah_cfg.a1 = (2 * Q * (KK - 1)) / (KK + K + Q);
wah_cfg.a2 = (KK * Q - K + Q) / (KK * Q + K + Q);
//finish prams calc
wah_cfg.update_flag = 0;
}
4.实现动态滤波,实现wah效果器
动态调整中心频率,使声音有种忽远忽近的感觉,具体实现如下
if(wah_cfg.f_c>3000)
{
flag = -1;
}
else if(wah_cfg.f_c <1000)
{
flag =1;
}
wah_cfg.f_c += flag*100;
updataParams();
实现效果
遇到的主要难题及解决方法,或未来的计划或建议等
- Max9867 DMA 驱动实现方式发现有卡顿,官方github 提出问题,后续更新了SDK 驱动,发现此问题消失
- 未来计划利用起第二个核做一些语音识别相关的demo