Saving and loading 10-bit samples...

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. :wink:

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.

	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. :wink: 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. :wink:

Wk

Well, after googling this morning I found some info on how to use bitset on C++. 8)

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

	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];

		bitset<8> readyBit1(0);
		bitset<8> readyBit2(0);
		bitset<8> readyBit3(0);
		bitset<8> readyBit4(0);
		bitset<8> readyBit5(0);

		for (w=0; w<8; w++) 
		{
			readyBit1[w] = out40Bits[w];
			readyBit2[w] = out40Bits[w+8];
			readyBit3[w] = out40Bits[w+16];
			readyBit4[w] = out40Bits[w+24];
			readyBit5[w] = out40Bits[w+32];
		}
		
		fprintf(fdwrite,"%d,", readyBit1.to_ulong());
		fprintf(fdwrite,"%d,", readyBit2.to_ulong());
		fprintf(fdwrite,"%d,", readyBit3.to_ulong());
		fprintf(fdwrite,"%d,", readyBit4.to_ulong());
		fprintf(fdwrite,"%d",  readyBit5.to_ulong());

		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. :wink:

Wk

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!! :slight_smile:

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. :wink:

Here's some snips from the code so far.

Converter, will post the whole thing later on.

	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

/*

  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) 
  {
    incomingByte = Serial.read();
    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;
//uint8_t readbuf[5];
// ======================================================================================= //
#define nextBuffer(voice)\
{\
  if ((bfcounter[voice]+1) >= 4)\
  {\
    if (sample[voice] >= sampleLen[voice])\
    {\
      samplePCM[voice] = 0;\
      bfcounter[voice] = 0;\
      buffer[voice][0] = 363;\
    }\
    else\
    {\
      buffer[voice][0] = (unsigned int)pgm_read_byte(&samplePCM[voice][sample[voice]]);\
      buffer[voice][1] = (unsigned int)pgm_read_byte(&samplePCM[voice][sample[voice]+1]);\
      buffer[voice][2] = (unsigned int)pgm_read_byte(&samplePCM[voice][sample[voice]+2]);\
      buffer[voice][3] = (unsigned int)pgm_read_byte(&samplePCM[voice][sample[voice]+3]);\
      tempbuffer = pgm_read_byte(&samplePCM[voice][sample[voice]+4]);\
      if (bitRead(tempbuffer,7)) bitSet(buffer[voice][0],9);\
      if (bitRead(tempbuffer,6)) bitSet(buffer[voice][0],8);\
      if (bitRead(tempbuffer,5)) bitSet(buffer[voice][1],9);\
      if (bitRead(tempbuffer,4)) bitSet(buffer[voice][1],8);\
      if (bitRead(tempbuffer,3)) bitSet(buffer[voice][2],9);\
      if (bitRead(tempbuffer,2)) bitSet(buffer[voice][2],8);\
      if (bitRead(tempbuffer,1)) bitSet(buffer[voice][3],9);\
      if (bitRead(tempbuffer,0)) bitSet(buffer[voice][3],8);\
    }\
    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

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?

static inline void nextBuffer(uint8_t voice)
{
  unsigned int *bufptr;
  uint8_t readbuf[5];

  bufptr = buffer[voice];

  if ((bfcounter[voice]+1) >= 4) 
  {
    if (sample[voice] >= sampleLen[voice]) 
    {
      samplePCM[voice] = 0;
      bfcounter[voice] = 0;
      bufptr[0] = 363;
    }
    else
    { 
      memcpy_P(readbuf, &samplePCM[voice][sample[voice]]/*in PROGMEM*/, 5);
      bufptr[0] = readbuf[0] | ((uint16_t)(readbuf[5]&0xC0) << 2);
      bufptr[1] = readbuf[1] | ((uint16_t)(readbuf[5]&0x30) << 4);
      bufptr[2] = readbuf[2] | ((uint16_t)(readbuf[5]&0x0C) << 6);
      bufptr[3] = readbuf[3] | ((uint16_t)(readbuf[5]&0x03) << 8);
    }
    sample[voice] += 5;
    bfcounter[voice] = 0;
  }
  else bfcounter[voice]++; 
}

Wk