极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 25778|回复: 15

使用粒子合成算法的arduino音效合成器

[复制链接]
发表于 2013-1-30 07:32:25 | 显示全部楼层 |阅读模式
本帖最后由 春泥蛋炒饭 于 2013-1-31 03:59 编辑

让arduino发声可以给项目增色不少,但产生音频不是arduino的长项。最常用的tone函数用PWM法生成方波脉冲刺耳而且单调,播放从SD卡读取的声音文件效果改善不少,但不能即兴产生效果音。这里介绍一种声音合成的算法,粒子合成法(Granular synthesis)。这个算法是产生一些短到刚好能分辨的声音小片段(声音粒子grain),然后对它们组合重排处理,在输入多变的参数时,可以形成丰满丰富的连续声音。

下面这个声音程序使用了粒子合成算法,它使用了预先算好的几个函数表,并直接操作寄存器,以节约运算时间。一共五个可以变化的输入参数,这里使用五个电位器来调节这些参数,实际运用时可以从各种传感器或预先编排的数据来作为参数输入,可以获得千变万化的音响效果。

硬件接法是五个电位器的两端都分别接Vcc和地,中间的活动端接arduino的A0到A4五个模拟口。音频信号在D3数字口输出(328芯片的板子),建议用8欧喇叭串联一个10uF的电容再接在输出口和地之间,或者接音频放大。下载程序运行后,通过调节这五个电位器来产生音效变化。

音响效果请看楼下回复里我加的视频。

[pre lang="arduino" line="1" file="auduino_v5.ino"]// Auduino, the Lo-Fi granular synthesiser
//
// by Peter Knight, Tinker.it http://tinker.it
//
// Help:      http://code.google.com/p/tinkerit/wiki/Auduino
// More help: http://groups.google.com/group/auduino
//
// Analog in 0: Grain 1 pitch
// Analog in 1: Grain 2 decay
// Analog in 2: Grain 1 decay
// Analog in 3: Grain 2 pitch
// Analog in 4: Grain repetition frequency
//
// Digital 3: Audio out (Digital 11 on ATmega8)
//
// Changelog:
// 19 Nov 2008: Added support for ATmega8 boards
// 21 Mar 2009: Added support for ATmega328 boards
// 7 Apr 2009: Fixed interrupt vector for ATmega328 boards
// 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega)

#include <avr/io.h>
#include <avr/interrupt.h>

uint16_t syncPhaseAcc;
uint16_t syncPhaseInc;
uint16_t grainPhaseAcc;
uint16_t grainPhaseInc;
uint16_t grainAmp;
uint8_t grainDecay;
uint16_t grain2PhaseAcc;
uint16_t grain2PhaseInc;
uint16_t grain2Amp;
uint8_t grain2Decay;

// Map Analogue channels
#define SYNC_CONTROL         (4)
#define GRAIN_FREQ_CONTROL   (0)
#define GRAIN_DECAY_CONTROL  (2)
#define GRAIN2_FREQ_CONTROL  (3)
#define GRAIN2_DECAY_CONTROL (1)


// Changing these will also requires rewriting audioOn()

#if defined(__AVR_ATmega8__)
//
// On old ATmega8 boards.
//    Output is on pin 11
//
#define LED_PIN       13
#define LED_PORT      PORTB
#define LED_BIT       5
#define PWM_PIN       11
#define PWM_VALUE     OCR2
#define PWM_INTERRUPT TIMER2_OVF_vect
#elif defined(__AVR_ATmega1280__)
//
// On the Arduino Mega
//    Output is on pin 3
//
#define LED_PIN       13
#define LED_PORT      PORTB
#define LED_BIT       7
#define PWM_PIN       3
#define PWM_VALUE     OCR3C
#define PWM_INTERRUPT TIMER3_OVF_vect
#else
//
// For modern ATmega168 and ATmega328 boards
//    Output is on pin 3
//
#define PWM_PIN       3
#define PWM_VALUE     OCR2B
#define LED_PIN       13
#define LED_PORT      PORTB
#define LED_BIT       5
#define PWM_INTERRUPT TIMER2_OVF_vect
#endif

// Smooth logarithmic mapping
//
uint16_t antilogTable[] = {
  64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109,
  54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341,
  45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968,
  38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768
};
uint16_t mapPhaseInc(uint16_t input) {
  return (antilogTable[input & 0x3f]) >> (input >> 6);
}

// Stepped chromatic mapping
//
uint16_t midiTable[] = {
  17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73,
  77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231,
  244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691,
  732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742,
  1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143,
  4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854,
  10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879,
  22121,23436,24830,26306
};
uint16_t mapMidi(uint16_t input) {
  return (midiTable[(1023-input) >> 3]);
}

// Stepped Pentatonic mapping
//
uint16_t pentatonicTable[54] = {
  0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346,
  411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288,
  3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306
};

uint16_t mapPentatonic(uint16_t input) {
  uint8_t value = (1023-input) / (1024/53);
  return (pentatonicTable[value]);
}


void audioOn() {
#if defined(__AVR_ATmega8__)
  // ATmega8 has different registers
  TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);
  TIMSK = _BV(TOIE2);
#elif defined(__AVR_ATmega1280__)
  TCCR3A = _BV(COM3C1) | _BV(WGM30);
  TCCR3B = _BV(CS30);
  TIMSK3 = _BV(TOIE3);
#else
  // Set up PWM to 31.25kHz, phase accurate
  TCCR2A = _BV(COM2B1) | _BV(WGM20);
  TCCR2B = _BV(CS20);
  TIMSK2 = _BV(TOIE2);
#endif
}


void setup() {
  pinMode(PWM_PIN,OUTPUT);
  audioOn();
  pinMode(LED_PIN,OUTPUT);
}

void loop() {
  // The loop is pretty simple - it just updates the parameters for the oscillators.
  //
  // Avoid using any functions that make extensive use of interrupts, or turn interrupts off.
  // They will cause clicks and poops in the audio.
  
  // Smooth frequency mapping
  //syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;
  
  // Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...
  //syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));
  
  // Stepped pentatonic mapping: D, E, G, A, B
  syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));

  grainPhaseInc  = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2;
  grainDecay     = analogRead(GRAIN_DECAY_CONTROL) / 8;
  grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2;
  grain2Decay    = analogRead(GRAIN2_DECAY_CONTROL) / 4;
}

SIGNAL(PWM_INTERRUPT)
{
  uint8_t value;
  uint16_t output;

  syncPhaseAcc += syncPhaseInc;
  if (syncPhaseAcc < syncPhaseInc) {
    // Time to start the next grain
    grainPhaseAcc = 0;
    grainAmp = 0x7fff;
    grain2PhaseAcc = 0;
    grain2Amp = 0x7fff;
    LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite
  }
  
  // Increment the phase of the grain oscillators
  grainPhaseAcc += grainPhaseInc;
  grain2PhaseAcc += grain2PhaseInc;

  // Convert phase into a triangle wave
  value = (grainPhaseAcc >> 7) & 0xff;
  if (grainPhaseAcc & 0x8000) value = ~value;
  // Multiply by current grain amplitude to get sample
  output = value * (grainAmp >> 8);

  // Repeat for second grain
  value = (grain2PhaseAcc >> 7) & 0xff;
  if (grain2PhaseAcc & 0x8000) value = ~value;
  output += value * (grain2Amp >> 8);

  // Make the grain amplitudes decay by a factor every sample (exponential decay)
  grainAmp -= (grainAmp >> 8) * grainDecay;
  grain2Amp -= (grain2Amp >> 8) * grain2Decay;

  // Scale output to the available range, clipping if necessary
  output >>= 9;
  if (output > 255) output = 255;

  // Output to PWM (this is faster than using analogWrite)  
  PWM_VALUE = output;
}
[/code]

参考网页:http://code.google.com/p/tinkerit/wiki/Auduino

评分

参与人数 1 +1 收起 理由
幻生幻灭 + 1 鼓励新人贴

查看全部评分

回复

使用道具 举报

 楼主| 发表于 2013-1-30 07:33:44 | 显示全部楼层
初来乍到,请各位多多提携
回复 支持 反对

使用道具 举报

发表于 2013-1-30 20:43:16 | 显示全部楼层
粒子算法?
不明觉厉
回复 支持 反对

使用道具 举报

发表于 2013-1-30 23:27:38 | 显示全部楼层
不明觉厉+1{:soso_e103:}我也实验一下先。。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-1-31 03:53:32 | 显示全部楼层
程序中因为满足实时音频合成的速度,使用了一些avr-gcc语句,所以比较晦涩难懂,但看看次程序的演示视频可能更直观。

回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-1-31 03:55:54 | 显示全部楼层
oditszapc 发表于 2013-1-30 20:43
粒子算法?
不明觉厉


算法比较抽象,重点是可以让arduino发出圆润的声音,请看上面新加的视频,里面有程序的音效效果
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-1-31 03:58:45 | 显示全部楼层
Ansifa 发表于 2013-1-30 23:27
不明觉厉+1我也实验一下先。。。

承认程序不好懂,先灌进板子试一试,很好玩的音响效果,控制好了可以演奏音乐。请参考我新加的一个程序演示视频。
回复 支持 反对

使用道具 举报

发表于 2013-1-31 08:35:24 来自手机 | 显示全部楼层
活捉小吧一个。。。。。。。^O^
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-1-31 09:16:14 | 显示全部楼层
小猪会轮滑 发表于 2013-1-31 08:35
活捉小吧一个。。。。。。。^O^

哈哈,老大也在这里玩啊,求罩。

看来带有avr-gcc语句的程序到哪里都不是人民群众喜闻乐见的。
回复 支持 反对

使用道具 举报

发表于 2013-1-31 22:57:18 | 显示全部楼层
这个音效杠杠的啊~~~~!貌似做音效发生器很牛啊
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-2-1 01:21:06 | 显示全部楼层
迷你强 发表于 2013-1-31 22:57
这个音效杠杠的啊~~~~!貌似做音效发生器很牛啊

谢老大支持新人!这个程序有很大的二次开发的余地,那5个输入参数可以来自传感器、预定数据或者乐器键盘的。
回复 支持 反对

使用道具 举报

发表于 2013-4-26 19:23:24 | 显示全部楼层
太帅了,我就喜欢这种电子音效。支持...
回复 支持 反对

使用道具 举报

发表于 2013-4-26 19:24:37 | 显示全部楼层
这种效果器很难找的,程序编起来肯定不简单,让我对arduino刮目相看
回复 支持 反对

使用道具 举报

发表于 2013-4-27 00:14:57 | 显示全部楼层
代码表示看不大懂,想外接MIDI键盘来控制发声~~
回复 支持 反对

使用道具 举报

发表于 2013-4-27 09:40:55 | 显示全部楼层
比较特别,有创意。
回复 支持 反对

使用道具 举报

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

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-5-20 12:02 , Processed in 0.061110 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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