Go Down

WilliamK Govinda

Mar 08, 2011, 03:00 am
Guys, just playing around with a new code which will work with 10-bit samples instead of the current 8-bit samples.

I'm very tired so maybe I need to rest a bit, but still, I want to play around with this. ;-)

I already got 6 voices of my Arduino Mega, each to its own output, thanks to the 12 x 16-bit pwm outputs. But the problem is that I'm storing and reading the samples in 16 bit format, but I saved in a kinda 10-bit way. (0 to 726 max)

But I want to store the samples in groups of 4 samples, making it 40 bits = 5 x 8-bit numbers. That's going to save some space. But I wonder if it wouldn't take too many cycles to decode the thing up.

In any event, I need to figure a way to convert the samples to 10-bits. Here's the piece of C++ code of my converter.

Code: [Select]
for (i=0;i<(samples+4);i+=4)
{
if (i > 0) fprintf(fdwrite,",");

unsigned int xt[4] = { min(725,(int)(((data[i] + 1.0f) * 363.0f))),
min(725,(int)(((data[i+1] + 1.0f) * 363.0f))),
min(725,(int)(((data[i+2] + 1.0f) * 363.0f))),
min(725,(int)(((data[i+3] + 1.0f) * 363.0f))) };*/

unsigned char xc[5] = {0,0,0,0,0};  // Here's where the 40-bits will be stored //

if (i > 0 && i % 50 == 0) fprintf(fdwrite,"\n");
}

Anyway, I bet all this is silly, but heck, I'm tired, so I'm going away for now...

BTW: I will post the whole code when its working better. ;-) So far I got 6 x voices, each one in its own 10-bit PWM output at 22068hz, and it sounds great on my headphones even without any external filter. ;-)

Wk

WilliamK Govinda

#1
Mar 08, 2011, 02:26 pmLast Edit: Mar 08, 2011, 02:35 pm by WilliamK Reason: 1
Well, after googling this morning I found some info on how to use bitset on C++.

Its not pretty, but since is used only for converting, there's no problem on taking longer.

Code: [Select]
filePath[strlen(filePath)-4] = 0;
fprintf(fdwrite,"// -------- Sound: %s - Rate:  %d hz -------- //\n", filePath, fileRate);

if (allLens[0] != 0) strcat(allLens,",");
char xsamples[999] = {0};
sprintf(xsamples,"%d",samples);
strcat(allLens,xsamples);

fprintf(fdwrite,"unsigned char _PCMdata_%d[] PROGMEM =  { \n", counter+1);
for (i=0;i<(samples+4);i+=4)
{
if (i > 0) fprintf(fdwrite,",");

bitset<10> in10Bits1(min(725,(unsigned int)(((data[i] + 1.0f) * 363.0f))));
bitset<10> in10Bits2(min(725,(unsigned int)(((data[i+1] + 1.0f) * 363.0f))));
bitset<10> in10Bits3(min(725,(unsigned int)(((data[i+2] + 1.0f) * 363.0f))));
bitset<10> in10Bits4(min(725,(unsigned int)(((data[i+3] + 1.0f) * 363.0f))));

bitset<40> out40Bits(0);

int w = 0;
for (w=0; w<8; w++)
{
out40Bits[w] = in10Bits1[w];
out40Bits[w+8] = in10Bits2[w];
out40Bits[w+16] = in10Bits3[w];
out40Bits[w+24] = in10Bits4[w];
}

out40Bits[32] = in10Bits1[8];
out40Bits[33] = in10Bits1[9];
out40Bits[34] = in10Bits2[8];
out40Bits[35] = in10Bits2[9];
out40Bits[36] = in10Bits3[8];
out40Bits[37] = in10Bits3[9];
out40Bits[38] = in10Bits4[8];
out40Bits[39] = in10Bits4[9];

for (w=0; w<8; w++)
{
}

if (i > 0 && i % 14 == 0) fprintf(fdwrite,"\n");
}
fprintf(fdwrite,"};\n\n");

counter++;

I will post the complete code when its ready on both sides. ;-)

Wk

audioguytodd

#2
Mar 10, 2011, 06:31 am
Thanks for posting this stuff WK. While I can't use the stuff right now, I appreciate you taking the time to post to these forums. I am sure that when I am in need (and even capable)- I can find your posts again and get some great stuff!!

WilliamK Govinda

#3
Mar 10, 2011, 12:13 pm
Indeed, thanks for the nice words. I will post the complete code as soon as I'm able to. My internet is still down, and the one I'm using I don't want to overuse. ;-)

Here's some snips from the code so far.

Converter, will post the whole thing later on.

Code: [Select]
fprintf(fdwrite,"unsigned char _PCMdata_%d[] PROGMEM =  { \n", counter+1);
for (i=0;i<(samples+4);i+=4)
{
if (i > 0) fprintf(fdwrite,",");

bitset<10> xB1((min(725,(unsigned int)(((data[i] + 1.0f) * 363.0f)))));
bitset<10> xB2((min(725,(unsigned int)(((data[i+1] + 1.0f) * 363.0f)))));
bitset<10> xB3((min(725,(unsigned int)(((data[i+2] + 1.0f) * 363.0f)))));
bitset<10> xB4((min(725,(unsigned int)(((data[i+3] + 1.0f) * 363.0f)))));

fprintf(fdwrite,"B");
for (int q=0; q<8; q++) { fprintf(fdwrite,(xB1.test(7-q)) ? "1" : "0"); }
fprintf(fdwrite,",B");
for (q=0; q<8; q++) { fprintf(fdwrite,(xB2.test(7-q)) ? "1" : "0"); }
fprintf(fdwrite,",B");
for (q=0; q<8; q++) { fprintf(fdwrite,(xB3.test(7-q)) ? "1" : "0"); }
fprintf(fdwrite,",B");
for (q=0; q<8; q++) { fprintf(fdwrite,(xB4.test(7-q)) ? "1" : "0"); }

fprintf(fdwrite,",B");
fprintf(fdwrite,(xB1.test(9)) ? "1" : "0");
fprintf(fdwrite,(xB1.test(8)) ? "1" : "0");
fprintf(fdwrite,(xB2.test(9)) ? "1" : "0");
fprintf(fdwrite,(xB2.test(8)) ? "1" : "0");
fprintf(fdwrite,(xB3.test(9)) ? "1" : "0");
fprintf(fdwrite,(xB3.test(8)) ? "1" : "0");
fprintf(fdwrite,(xB4.test(9)) ? "1" : "0");
fprintf(fdwrite,(xB4.test(8)) ? "1" : "0");

if (i > 0 && i % 8 == 0) fprintf(fdwrite,"\n");

samplesB5 += 5;
}
fprintf(fdwrite,"};\n\n");

if (allLens[0] != 0) strcat(allLens,",");
char xsamples[999] = {0};
sprintf(xsamples,"%d",samplesB5);
strcat(allLens,xsamples);

counter++;

----

Arduino Sketch

Code: [Select]
/*

Created by WilliamK @ Wusik Dot Com (c) 2011 - http://arduino.wusik.com

10-Bit PCM Sound using PWM on the 16-bit Timer1 and Timer3 - a total of 6 voices at 22068hz
Sound on Pins 13, 12, 11, 5, 3 and 2

*/

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "PCM_Sound.h"

// ======================================================================================= //
#define MIDIactivityPin 52
#define MAXVOICES 6
unsigned long MIDIactivityMillis = millis()+25;
byte MIDIactivity = 0;
unsigned int sampleLen[MAXVOICES];
unsigned int sampleVel[MAXVOICES];
unsigned int sampleVel2[MAXVOICES];
unsigned int sample[MAXVOICES];
unsigned char* samplePCM[MAXVOICES];
unsigned char voice = 0;
byte incomingByte;
byte note;
byte velocity;
byte noteOn = 0;
byte state = 0;
byte sound = 0;

// Buffers //
unsigned int buffer[MAXVOICES][4];
unsigned char bfcounter[MAXVOICES];
byte tempbuffer = 0;

// ======================================================================================= //
void setup()
{
memset(sampleLen,0,sizeof(sampleLen));
memset(sample,0,sizeof(sample));
memset(samplePCM,0,sizeof(samplePCM));
memset(bfcounter,0,sizeof(bfcounter));
memset(buffer,0,sizeof(buffer));
memset(sampleVel,0,sizeof(sampleVel));
memset(sampleVel2,0,sizeof(sampleVel));

pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(5,OUTPUT);
pinMode(11,OUTPUT);
pinMode(12,OUTPUT);
pinMode(13,OUTPUT);

pinMode(MIDIactivityPin,OUTPUT);
digitalWrite(MIDIactivityPin,LOW);

Serial.begin(31250);

// 16 Bit Timer Setup //
// 16000000 (CPU CLOCK) / 726 (10 Bits) = 22068 samples-per-second (put a filter to remove anything above 10.5khz)
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(COM1C1) | _BV(WGM11); // Fast PWM Timer
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // Non-Phase Inverting - No Prescaler
ICR1 = (726-1); // Max value of 726 (9-bits is 511 and 10-bits is 1023) - 1 (the processor counts over)
TIMSK1 = _BV(TOIE1); // timer overflow interrupt
OCR1A = OCR1B = OCR1C = 363;

TCCR3A = _BV(COM3A1) | _BV(COM3B1) | _BV(COM3C1) | _BV(WGM11);
TCCR3B = _BV(WGM12) | _BV(WGM13) | _BV(CS10);
ICR3 = (726-1);
OCR3A = OCR3B = OCR3C = 363;

/*TCCR4A = _BV(COM4A1) | _BV(COM4B1) | _BV(COM4C1) | _BV(WGM11);
TCCR4B = _BV(WGM12) | _BV(WGM13) | _BV(CS10);
ICR4 = (726-1);
OCR4A = OCR4B = OCR4C = 363;*/

sei(); // enable global interrupts
}

// ======================================================================================= //
unsigned char getFreeVoice(void)
{
for (int x=0; x<MAXVOICES; x++)
{
if (samplePCM[x] == 0)
{
return x;
break;
}
}

return 5;
}
#define map2(x,in_min,in_max,out_min,out_max) ((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

// ======================================================================================= //
void playNote(unsigned char note, unsigned char velocity)
{
if (note < 60) return;
sound = note-60;
if (sound >= PCMtotal) return;

voice = getFreeVoice();
samplePCM[voice] = 0; // First stop any sound reference //
sample[voice] = 0;
buffer[voice][3] = 363;
bfcounter[voice] = 3; // This will force the sample buffer to be redone at the next sample //
sampleLen[voice] = PCMlen[sound];
sampleVel[voice] = map2(velocity,0,127,20,1);
sampleVel2[voice] = 363 - (363 / sampleVel[voice]);
samplePCM[voice] = PCMdata[sound];
}

// ======================================================================================= //
void loop()
{
if (MIDIactivity && MIDIactivityMillis < millis())
{
MIDIactivity = 0;
digitalWrite(MIDIactivityPin,LOW);
}

if (Serial.available() > 0)
{
switch (state)
{
case 0:
digitalWrite(MIDIactivityPin,HIGH);
MIDIactivityMillis = millis()+25;
MIDIactivity = 1;

if (incomingByte == 144) // Note On //
{
noteOn = 1;
state = 1;
}
else if (incomingByte == 128) // Note Off //
{
noteOn = 0;
state = 1;
}
break;

case 1:
if(incomingByte < 128) // Note Number //
{
note = incomingByte;
state = 2;
}
else state = 0;  // reset //
break;

case 2:
if(incomingByte < 128) // Velocity //
{
velocity = incomingByte;
if (noteOn == 1 && velocity > 0) playNote(note, velocity);
noteOn = 0;
}
state = 0;  // reset //
break;
}
}
}

//unsigned int *bufptr;
// ======================================================================================= //
#define nextBuffer(voice)\
{\
if ((bfcounter[voice]+1) >= 4)\
{\
if (sample[voice] >= sampleLen[voice])\
{\
samplePCM[voice] = 0;\
bfcounter[voice] = 0;\
buffer[voice][0] = 363;\
}\
else\
{\
}\
sample[voice] += 5;\
bfcounter[voice] = 0;\
}\
else bfcounter[voice]++;\
}

// ======================================================================================= //
ISR(TIMER1_OVF_vect)
{
// Do this first so the PWM is updated faster //
OCR1A = buffer[0][bfcounter[0]];
OCR1B = buffer[1][bfcounter[1]];
OCR1C = buffer[2][bfcounter[2]];
OCR3A = buffer[3][bfcounter[3]];
OCR3B = buffer[4][bfcounter[4]];
OCR3C = buffer[5][bfcounter[5]];

if (samplePCM[0] != 0) nextBuffer(0);
if (samplePCM[1] != 0) nextBuffer(1);
if (samplePCM[2] != 0) nextBuffer(2);
if (samplePCM[3] != 0) nextBuffer(3);
if (samplePCM[4] != 0) nextBuffer(4);
if (samplePCM[5] != 0) nextBuffer(5);
}

Wk

WilliamK Govinda

#4
Mar 10, 2011, 12:15 pm
What I need to do now is optimize nextBuffer. A good friend sent me a code that looks ok but the sound is bad. If anyone has a clue on what's going on, let me know, please?

Code: [Select]
static inline void nextBuffer(uint8_t voice)
{
unsigned int *bufptr;

bufptr = buffer[voice];

if ((bfcounter[voice]+1) >= 4)
{
if (sample[voice] >= sampleLen[voice])
{
samplePCM[voice] = 0;
bfcounter[voice] = 0;
bufptr[0] = 363;
}
else
{