system
August 11, 2012, 12:13pm
1
Hi all,
I think this is a simple question, I just can't figure it out myself. I'm using a bit of code that Stimmer ported to Arduino before (see here http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265488118 ). As it is, it simply outputs the chiptune music on digital pin 3. I would like to change the output to pin 5. I'm not sure how to change the code, although I presume it is somewhere in the "main"-function, where the port registers are declared (DDRD = oxff, etc.). I looked at the Arduino page about port registers, but I can't figure out where in the code it says that the output is on port 3 (and so I can't change it to port 5).
I post the end of the code here, because the whole code is too long to upload. However, the whole code can be found here: https://docs.google.com/leaf?id=0B5O7TidPmOdzYWRiODI1ZGYtNjNkYS00NmM4LWE5MjQtMWI2YzhkN2Q0M2Zl&hl=en
Could someone help? Thanks!
/* BEGINNING OF CODE OMITTED*/
int main() {
asm("cli");
watchdogoff();
CLKPR = 0x80;
CLKPR = 0x80;
DDRC = 0x12;
DDRD = 0xff;
PORTC = 0;
timetoplay = 0;
trackwait = 0;
trackpos = 0;
playsong = 1;
songpos = 0;
osc[0].volume = 0;
channel[0].inum = 0;
osc[1].volume = 0;
channel[1].inum = 0;
osc[2].volume = 0;
channel[2].inum = 0;
osc[3].volume = 0;
channel[3].inum = 0;
initresources();
TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz
TCCR2A=0b10100011;
TCCR2B=0b00000001;
TIMSK0 = 0x02;
asm("sei");
for(;;) {
while(!timetoplay);
timetoplay--;
playroutine();
}
}
ISR(TIMER0_COMPA_vect) // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;
OCR2B = lastsample;
newbit = 0;
if(noiseseed & 0x80000000L) newbit ^= 1;
if(noiseseed & 0x01000000L) newbit ^= 1;
if(noiseseed & 0x00000040L) newbit ^= 1;
if(noiseseed & 0x00000200L) newbit ^= 1;
noiseseed = (noiseseed << 1) | newbit;
if(callbackwait) {
callbackwait--;
} else {
timetoplay++;
callbackwait = 180 - 1;
}
acc = 0;
for(i = 0; i < 4; i++) {
s8 value; // [-32,31]
switch(osc[i].waveform) {
case WF_TRI:
if(osc[i].phase < 0x8000) {
value = -32 + (osc[i].phase >> 9);
} else {
value = 31 - ((osc[i].phase - 0x8000) >> 9);
}
break;
case WF_SAW:
value = -32 + (osc[i].phase >> 10);
break;
case WF_PUL:
value = (osc[i].phase > osc[i].duty)? -32 : 31;
break;
case WF_NOI:
value = (noiseseed & 63) - 32;
break;
default:
value = 0;
break;
}
osc[i].phase += osc[i].freq;
acc += value * osc[i].volume; // rhs = [-8160,7905]
}
// acc [-32640,31620]
lastsample = 128 + (acc >> 8); // [1,251]
}
/* Time for some Cake! */
}
It's not in the main(). Maybe in playroutine()?
system
August 11, 2012, 1:28pm
3
Here's the whole code, minus the songdata:
/*
Chiptune Music!
Open Source Code
Connect Speaker or Amp to Digital Pin3 and GND
Sound is made with PWM
Enjoy!
Happy Electric Circuits!
http://happyelectriccircuits.com/
*/
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#define TRACKLEN 32
#define MAXTRACK 0x92
#define SONGLEN 0x37
#include <avr/pgmspace.h>
extern "C" {
typedef unsigned char u8;
typedef unsigned short u16;
typedef char s8;
typedef short s16;
typedef unsigned long u32;
byte songdata[] PROGMEM = {
/* SONGDATA*/
};
volatile u8 callbackwait;
volatile u8 lastsample;
volatile u8 timetoplay;
volatile u8 test;
volatile u8 testwait;
u8 trackwait;
u8 trackpos;
u8 playsong;
u8 songpos;
u32 noiseseed = 1;
u8 light[2];
/* Freq Table */
const u16 freqtable[] = {
/* FREQUENCIES*/
};
/* Simply replace the code above to code below to change tone,
const u16 freqtable[] = {
/* FREQUENCIES*/
};
*/
const s8 sinetable[] = {
/* SINES*/
};
const u8 validcmds[] = "0dfijlmtvw~+=";
enum {
WF_TRI,
WF_SAW,
WF_PUL,
WF_NOI
};
volatile struct oscillator {
u16 freq;
u16 phase;
u16 duty;
u8 waveform;
u8 volume; // 0-255
} osc[4];
struct trackline {
u8 note;
u8 instr;
u8 cmd[2];
u8 param[2];
};
struct track {
struct trackline line[TRACKLEN];
};
struct unpacker {
u16 nextbyte;
u8 buffer;
u8 bits;
};
struct channel {
struct unpacker trackup;
u8 tnum;
s8 transp;
u8 tnote;
u8 lastinstr;
u8 inum;
u16 iptr;
u8 iwait;
u8 inote;
s8 bendd;
s16 bend;
s8 volumed;
s16 dutyd;
u8 vdepth;
u8 vrate;
u8 vpos;
s16 inertia;
u16 slur;
} channel[4];
u16 resources[16 + MAXTRACK];
struct unpacker songup;
byte readsongbyte(u16 offset)
{
return pgm_read_byte_near(&songdata[0] + offset);
}
void watchdogoff()
{
}
void initup(struct unpacker *up, u16 offset) {
up->nextbyte = offset;
up->bits = 0;
}
u8 readbit(struct unpacker *up) {
u8 val;
if(!up->bits) {
up->buffer = readsongbyte(up->nextbyte++);
up->bits = 8;
}
up->bits--;
val = up->buffer & 1;
up->buffer >>= 1;
return val;
}
u16 readchunk(struct unpacker *up, u8 n) {
u16 val = 0;
u8 i;
for(i = 0; i < n; i++) {
if(readbit(up)) {
val |= (1 << i);
}
}
return val;
}
void readinstr(byte num, byte pos, byte *dest) {
dest[0] = readsongbyte(resources[num] + 2 * pos + 0);
dest[1] = readsongbyte(resources[num] + 2 * pos + 1);
}
void runcmd(u8 ch, u8 cmd, u8 param) {
switch(validcmds[cmd]) {
case '0':
channel[ch].inum = 0;
break;
case 'd':
osc[ch].duty = param << 8;
break;
case 'f':
channel[ch].volumed = param;
break;
case 'i':
channel[ch].inertia = param << 1;
break;
case 'j':
channel[ch].iptr = param;
break;
case 'l':
channel[ch].bendd = param;
break;
case 'm':
channel[ch].dutyd = param << 6;
break;
case 't':
channel[ch].iwait = param;
break;
case 'v':
osc[ch].volume = param;
break;
case 'w':
osc[ch].waveform = param;
break;
case '+':
channel[ch].inote = param + channel[ch].tnote - 12 * 4;
break;
case '=':
channel[ch].inote = param;
break;
case '~':
if(channel[ch].vdepth != (param >> 4)) {
channel[ch].vpos = 0;
}
channel[ch].vdepth = param >> 4;
channel[ch].vrate = param & 15;
break;
}
}
void playroutine() { // called at 50 Hz
u8 ch;
u8 lights;
if(playsong) {
if(trackwait) {
trackwait--;
} else {
trackwait = 4;
if(!trackpos) {
if(playsong) {
if(songpos >= SONGLEN) {
playsong = 0;
} else {
for(ch = 0; ch < 4; ch++) {
u8 gottransp;
u8 transp;
gottransp = readchunk(&songup, 1);
channel[ch].tnum = readchunk(&songup, 6);
if(gottransp) {
transp = readchunk(&songup, 4);
if(transp & 0x8) transp |= 0xf0;
} else {
transp = 0;
}
channel[ch].transp = (s8) transp;
if(channel[ch].tnum) {
initup(&channel[ch].trackup, resources[16 + channel[ch].tnum - 1]);
}
}
songpos++;
}
}
}
if(playsong) {
for(ch = 0; ch < 4; ch++) {
if(channel[ch].tnum) {
u8 note, instr, cmd, param;
u8 fields;
fields = readchunk(&channel[ch].trackup, 3);
note = 0;
instr = 0;
cmd = 0;
param = 0;
if(fields & 1) note = readchunk(&channel[ch].trackup, 7);
if(fields & 2) instr = readchunk(&channel[ch].trackup, 4);
if(fields & 4) {
cmd = readchunk(&channel[ch].trackup, 4);
param = readchunk(&channel[ch].trackup, 8);
}
if(note) {
channel[ch].tnote = note + channel[ch].transp;
if(!instr) instr = channel[ch].lastinstr;
}
if(instr) {
if(instr == 2) light[1] = 5;
if(instr == 1) {
light[0] = 5;
if(channel[ch].tnum == 4) {
light[0] = light[1] = 3;
}
}
if(instr == 7) {
light[0] = light[1] = 30;
}
channel[ch].lastinstr = instr;
channel[ch].inum = instr;
channel[ch].iptr = 0;
channel[ch].iwait = 0;
channel[ch].bend = 0;
channel[ch].bendd = 0;
channel[ch].volumed = 0;
channel[ch].dutyd = 0;
channel[ch].vdepth = 0;
}
if(cmd) runcmd(ch, cmd, param);
}
}
trackpos++;
trackpos &= 31;
}
}
}
for(ch = 0; ch < 4; ch++) {
s16 vol;
u16 duty;
u16 slur;
while(channel[ch].inum && !channel[ch].iwait) {
u8 il[2];
readinstr(channel[ch].inum, channel[ch].iptr, il);
channel[ch].iptr++;
runcmd(ch, il[0], il[1]);
}
if(channel[ch].iwait) channel[ch].iwait--;
if(channel[ch].inertia) {
s16 diff;
slur = channel[ch].slur;
diff = freqtable[channel[ch].inote] - slur;
//diff >>= channel[ch].inertia;
if(diff > 0) {
if(diff > channel[ch].inertia) diff = channel[ch].inertia;
} else if(diff < 0) {
if(diff < -channel[ch].inertia) diff = -channel[ch].inertia;
}
slur += diff;
channel[ch].slur = slur;
} else {
slur = freqtable[channel[ch].inote];
}
osc[ch].freq =
slur +
channel[ch].bend +
((channel[ch].vdepth * sinetable[channel[ch].vpos & 63]) >> 2);
channel[ch].bend += channel[ch].bendd;
vol = osc[ch].volume + channel[ch].volumed;
if(vol < 0) vol = 0;
if(vol > 255) vol = 255;
osc[ch].volume = vol;
duty = osc[ch].duty + channel[ch].dutyd;
if(duty > 0xe000) duty = 0x2000;
if(duty < 0x2000) duty = 0xe000;
osc[ch].duty = duty;
channel[ch].vpos += channel[ch].vrate;
}
lights = 0;
if(light[0]) {
light[0]--;
lights |= 0x02;
}
if(light[1]) {
light[1]--;
lights |= 0x10;
}
PORTC = lights;
}
void initresources() {
u8 i;
struct unpacker up;
initup(&up, 0);
for(i = 0; i < 16 + MAXTRACK; i++) {
resources[i] = readchunk(&up, 13);
}
initup(&songup, resources[0]);
}
int main() {
asm("cli");
watchdogoff();
CLKPR = 0x80;
CLKPR = 0x80;
DDRC = 0x12;
DDRD = 0xff;
PORTC = 0;
timetoplay = 0;
trackwait = 0;
trackpos = 0;
playsong = 1;
songpos = 0;
osc[0].volume = 0;
channel[0].inum = 0;
osc[1].volume = 0;
channel[1].inum = 0;
osc[2].volume = 0;
channel[2].inum = 0;
osc[3].volume = 0;
channel[3].inum = 0;
initresources();
TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz
TCCR2A=0b10100011;
TCCR2B=0b00000001;
TIMSK0 = 0x02;
asm("sei");
for(;;) {
while(!timetoplay);
timetoplay--;
playroutine();
}
}
ISR(TIMER0_COMPA_vect) // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;
OCR2B = lastsample;
newbit = 0;
if(noiseseed & 0x80000000L) newbit ^= 1;
if(noiseseed & 0x01000000L) newbit ^= 1;
if(noiseseed & 0x00000040L) newbit ^= 1;
if(noiseseed & 0x00000200L) newbit ^= 1;
noiseseed = (noiseseed << 1) | newbit;
if(callbackwait) {
callbackwait--;
} else {
timetoplay++;
callbackwait = 180 - 1;
}
acc = 0;
for(i = 0; i < 4; i++) {
s8 value; // [-32,31]
switch(osc[i].waveform) {
case WF_TRI:
if(osc[i].phase < 0x8000) {
value = -32 + (osc[i].phase >> 9);
} else {
value = 31 - ((osc[i].phase - 0x8000) >> 9);
}
break;
case WF_SAW:
value = -32 + (osc[i].phase >> 10);
break;
case WF_PUL:
value = (osc[i].phase > osc[i].duty)? -32 : 31;
break;
case WF_NOI:
value = (noiseseed & 63) - 32;
break;
default:
value = 0;
break;
}
osc[i].phase += osc[i].freq;
acc += value * osc[i].volume; // rhs = [-8160,7905]
}
// acc [-32640,31620]
lastsample = 128 + (acc >> 8); // [1,251]
}
/* Time for some Cake! */
}
Oh, I 've been looking for direct port manipulation, and couldn't find it first time, simple because he's using direct PWM port manipulation. Pin3 is internally associated to PWM-B of TIMER2, here is relevant part of the code:
TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz
TCCR2A=0b10100011;
TCCR2B=0b00000001;
TIMSK0 = 0x02;
asm("sei");
for(;;) {
while(!timetoplay);
timetoplay--;
playroutine();
}
}
ISR(TIMER0_COMPA_vect) // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;
OCR2B = lastsample;
Pin - 5 and 6 are belongs to TIMER0, which is busy doing time clocking via OCM - output compare match. You can't use them. You could, probably transfer it to pin 11 or pin 9 - 10 if TIMER1 isn't in use in your code
Other way around, would be swap TIMER2 - to do a clock job, and TIMER0 - for PWM
system
August 11, 2012, 2:15pm
6
Would it be a lot of work to change that in the code? Here is a link to the code file. Sorry, this all goes a bit over my head.
If it's too much work I will try it some other way. Thanks!
https://www.dropbox.com/s/c4my8owoa94y4m8/Chiptune.ino
Would it be a lot of work to change that in the code?
Which way? To play on pin11? Tryy this:
// OCR2B = lastsample; <<<--- replace with
OCR2A = lastsample;
system
August 11, 2012, 7:04pm
8
Thanks! And what would have to be changed to play it on pin 5?
Download a data sheet for AtMega328 (atmel.com ), full version. Look into section TIMER0. Using control register description, de-crypt this settings:
"TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz" In binary TCCR0A = 0b0000 0010 , as you can see bit 2 is set. Find out what it means. Than transfer settings to TIMER2, again using data sheet . It would be something like TCCR2A = 0b0000 0010 - just an example, I'm not sure if they similar in bit settings.
And TCCR2B = xxxxxxxx, and OCR2A = 125. After that TIMER2 would be responsible for 8 kHz clock, and pin 5 get freedom.
What left, is change:
TCCR0A=0b10100011; <<<- verify !
TCCR0B=0b00000001; <<<- verify !
TIMSK2 = 0x02;
-//-//--// skip, the same
ISR(TIMER2_COMPA_vect) // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;
OCR0B = lastsample;"