VS1003 WAV recording

Writing software for systems that use VLSI Solution's devices as slave codecs to a host microcontroller.
Zigon
User
Posts: 13
Joined: Mon 2016-12-19 5:09

VS1003 WAV recording

Post by Zigon »

Hi
I have been trying to work out how to get my vs1003 module board to encode sound to wav, unfortunately its not working, I have been working a library that is written in C, for the Arduino IDE

Here is the whole function but for some reason it gets stuck in the first do/while loop (only got 1 so should be easy to find, hopefully :) ),
even though i am only reading the buffer to see how many words are ready to be read, i get very weird readings
Image

Code: Select all

//Used to record sound to a wav file
void VS1003::wavRecord()
{
	File record;
	const unsigned char RIFFHeader0[] = {
		'R' , 'I' , 'F' , 'F' , // Chunk ID (RIFF)
		0x70, 0x70, 0x70, 0x70, // Chunk payload size (calculate after rec!)
		'W' , 'A' , 'V' , 'E' , // RIFF resource format type

		'f' , 'm' , 't' , ' ' , // Chunk ID (fmt )
		0x14, 0x00, 0x00, 0x00, // Chunk payload size (0x14 = 20 bytes)
		0x11, 0x00,             // Format Tag (IMA ADPCM)
		0x01, 0x00,             // Channels (1)
		0x80, 0x3e, 0x00, 0x00, // Sample Rate, 0x3e80 = 16.0kHz
		0xd7, 0x0f, 0x00, 0x00, // Average Bytes Per Second
		0x00, 0x01,             // Data Block Size (256 bytes) 
		0x04, 0x00,             // ADPCM encoded bits per sample (4 bits)
		0x02, 0x00,             // Extra data (2 bytes)
		0xf9, 0x01,             // Samples per Block (505 samples)

		'f' , 'a' , 'c' , 't' , // Chunk ID (fact)
		0xc8, 0x01, 0x00, 0x00, // Chunk payload size (456 bytes (zeropad!))
		0xff, 0xff, 0xff, 0xff  // Number of Samples (calculate after rec!)
	};
	const unsigned char RIFFHeader504[] = {
		'd' , 'a' , 't' , 'a' , // Chunk ID (data)
		0x70, 0x70, 0x70, 0x70  // Chunk payload size (calculate after rec!)
	};

	//data buffer for saving to disk
	unsigned char db[512];
	int dataToRead = 0;
	int idx = 0;
	if (!(record = SD.open("RECORD1.WAV", FILE_WRITE)))
	{
		Serial.println("Failed to open record file in write mode");
	}
	record.seek(0);
	// Create a wav file and create the necessary header
	record.write(RIFFHeader0,sizeof(RIFFHeader0));
	for (int i = 0; i<448; i++)
	{
		record.write('0');
	}
	record.seek(504);
	record.write(RIFFHeader504,sizeof(RIFFHeader504));
	record.flush();
	Serial.println(record.size());

	//Set sample rate divider=9
	writeRegister(SCI_AICTRL0, 0x00, 0x09);
	delay(100);
	//AutoGain
	writeRegister(SCI_AICTRL1, 0x10, 0x00);
	delay(100);
	//RECORD,NEWMODE,RESET
	writeRegister(SCI_MODE, 0x18, 0x04);
	delay(1000);

	int i = 0;
	while (i<100)
	{
		//wait until 512 bytes become available
		do {
			dataToRead = readRegister(SCI_HDAT1);
			Serial.print("data words to read: ");
			Serial.println(dataToRead);
			delay(10);
		} while (dataToRead < 256 || dataToRead >= 896);
		Serial.println("Reading data...");
		while (idx < 512)
		{
			uint16_t w = readRegister(SCI_HDAT0);
			db[idx++] = w >> 8;
			db[idx++] = w & 0xFF;
		}
		Serial.println("Finished reading data...");
		idx = 0;
		Serial.println("Writing to disk..");
		record.write(db,sizeof(db));
		Serial.println("Finished writing to disk..");
		record.flush();
		i++;
	}
	Serial.println("Recording finished :)");
	record.close();
}
I tried to follow viewtopic.php?t=1275 this example but to no avail, most of my code is based around it though
https://github.com/zigon15/VS1003_Libar ... VS1003.cpp is the full class with all the setting up etc

Thanks, Zigon
User avatar
Panu
VSDSP Expert
Posts: 2829
Joined: Tue 2010-06-22 13:43

Re: VS1003 WAV recording

Post by Panu »

Hi!'

Hmmm, does your readRegister function work?
What happens if you read MODE and STATUS registers?
Can you play MP3?
Check your SPI bus speed.
How long is the Delay(10)?
If you change the delay, do you get different results?

-Panu
Zigon
User
Posts: 13
Joined: Mon 2016-12-19 5:09

Re: VS1003 WAV recording

Post by Zigon »

As far i know my readResgister function works as when i read the version bit it returns the correct bits,
I am using example code for the readRegister function, i am assuming it should be good, assuming probably isn't a good idea :P

Code: Select all

//Read the 16-bit value of a VS10xx register
unsigned int VS1003::readRegister (unsigned char addressbyte)
{
  while (!digitalRead(_DREQ)) ; //Wait for DREQ to go high indicating IC is available
  digitalWrite(_XCS, LOW); //Select control
  //SCI consists of instruction byte, address byte, and 16-bit data word.
  SPI.transfer(0x03); //Read instruction
  SPI.transfer(addressbyte);
  char response1 = SPI.transfer(0xFF); //Read the first byte
  while (!digitalRead(_DREQ)) ; //Wait for DREQ to go high indicating command is complete
  char response2 = SPI.transfer(0xFF); //Read the second byte
  while (!digitalRead(_DREQ)) ; //Wait for DREQ to go high indicating command is complete
  digitalWrite(_XCS, HIGH); //Deselect Control
  int resultvalue = response1;
  resultvalue |= response2;
  return resultvalue;
};
Its reads both the low and high byte and then using the bitwise or to put it into 1 byte,if that is correct idk,

If i read the MODE and STATUS registers after i have set it to record

Code: Select all

//Set sample rate divider=9
	writeRegister(SCI_AICTRL0, 0x00, 0x09);
	delay(100);
	//AutoGain
	writeRegister(SCI_AICTRL1, 0x10, 0x00);
	delay(100);
	//RECORD,NEWMODE,RESET
	writeRegister(SCI_MODE, 0x18, 0x04);
	delay(1000);

	Serial.print("Mode REGISTER: ")
	Serial.print(readRegister(SCI_MODE),HEX)
	Serial.print(", Status REGISTER: ")
	Serial.print(readRegister(SCI_MODE), HEX)
This happens after i have read both registers
increasing the Delay(10) to Delay(100) does nothing except make the loop take longer to complete :)
Image
It plays mp3 files fine,
my spi spead is set the the Clock/4 so (16MHz / 4 = 4MHz)
Delay(10) would be 10 milliseconds, so delay(1000) is 1 second

Thanks, Zigon
User avatar
Henrik
VLSI Staff
Posts: 1294
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik »

Hello Zigon,

I think I can see the source for your problems.

First, this doesn't cause errors, but is unnecessary code: You don't need to check DREQ in the middle of readRegister(). Check it at the beginning, then you can do the whole 4-byte transaction.

The values you receive indicate that you get the most significant bit of either of the result bytes incorrectly from time to time. Also your function readRegister() handles signs incorrectly, and the statement for creating resultvalue is incorrect.

Below is an untested fix:

Code: Select all

//Read the 16-bit value of a VS10xx register
unsigned int VS1003::readRegister (unsigned char addressbyte)
{
  while (!digitalRead(_DREQ)) ; //Wait for DREQ to go high indicating IC is available
  digitalWrite(_XCS, LOW); //Select control
  //SCI consists of instruction byte, address byte, and 16-bit data word.
  SPI.transfer(0x03); //Read instruction
  SPI.transfer(addressbyte);
  unsigned char response1 = SPI.transfer(0xFF); //Read the first byte
  unsigned char response2 = SPI.transfer(0xFF); //Read the second byte
  digitalWrite(_XCS, HIGH); //Deselect Control
  return ((unsigned int)response1 << 8) | response2;
};
To test, make several writes (preferable with all values between 0x0000 and 0xffff) to register SCI_AITRL0 - SCI_AICTRL3, and see if you get the correct results and see if this helps. My guess is that you might still get bit 15 (the MSb) incorrect from time to time. This may come from too high SPI speed or wrong clock polarity.

Kind regards,
- Henrik

Kind regards,
- Henrik
Good signatures never die. They just fade away.
Zigon
User
Posts: 13
Joined: Mon 2016-12-19 5:09

Re: VS1003 WAV recording

Post by Zigon »

After changing the readRegister code, and testing, the test worked fine :) thanks :D
Image
After trying the record test again i still get a lot of negative data and it becomes positive in short bursts which is weird
Image
When i play the file it has only rubbish noise in it though :(
I have no idea why the number is negative as readRegister is working so errr
User avatar
Henrik
VLSI Staff
Posts: 1294
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik »

Zigon wrote:After changing the readRegister code, and testing, the test worked fine :) thanks :D
Question 1: Did you test the registers with all 65536 values between 0 and 0xffff? A number of specific interest to me is 128, or 0x80. The reason I am asking is that where you read SCI_HDAT1 = -128, I think the real result should be 128.
Zigon wrote:After trying the record test again i still get a lot of negative data and it becomes positive in short bursts which is weird
dataToRead should be an uint16_t, not an int. Still, if the system is working properly, it should not be possible to get values that would be interpreted as negative in a normal 16-bit integer. Could it perhaps be that there is a bug in the compiler? Perhaps test this as the last line of readRegister:

Code: Select all

return ((unsigned int)response1 << 8) | (response2 & 0xFF);
Question 2: What is your SPI speed?

Question 3: What is you SPI data clock polarity?

Kind regards,
- Henrik
Good signatures never die. They just fade away.
Zigon
User
Posts: 13
Joined: Mon 2016-12-19 5:09

Re: VS1003 WAV recording

Post by Zigon »

Q1, no i didn't check them all,I did now and found something weird, probably an issue i would say :?
Image
That was only a small test range, I am doing a full test now of all 65536 values

int can range from -32,768 to 32,767
I changed dataToRead to uint16_t instead of int, and tested the line of code you suggested, the numbers are always positive, very positive in fact :o
Image

Q2 Spi speed is 4mhz
Q3 To be 100% honest i have no idea what my spi polarity is, here is a reference to the SPI libary i am using https://www.arduino.cc/en/reference/SPI

This is my modified readRegister code

Code: Select all

//Read the 16-bit value of a VS10xx register
unsigned int VS1003::readRegister (unsigned char addressbyte)
{
  while (!digitalRead(_DREQ)) ; //Wait for DREQ to go high indicating IC is available
  digitalWrite(_XCS, LOW); //Select control
  //SCI consists of instruction byte, address byte, and 16-bit data word.
  SPI.transfer(0x03); //Read instruction
  SPI.transfer(addressbyte);
  char response1 = SPI.transfer(0xFF); //Read the first byte
  char response2 = SPI.transfer(0xFF); //Read the second byte
  digitalWrite(_XCS, HIGH); //Deselect Control
  return ((unsigned int)response1 << 8) | (response2);
};
User avatar
Henrik
VLSI Staff
Posts: 1294
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik »

Zigon wrote:Q1, no i didn't check them all,I did now and found something weird, probably an issue i would say :?
Image
Exactly as I expected: from value 0x80 onwards there are problems. This is the cause for your problem, and to me it looks like a compiler bug, not like a hardware issue (unsigned char variable response2 gets sign extended which is not right). No point in trying recording before this test passes, but when we get this issue resolved, then I am sure your recording will succeed.

Let's add some more debugs to your code, e.g. like below. The code also has a new way of trying to get around the assumed compiler bug:

Code: Select all

//Read the 16-bit value of a VS10xx register
unsigned int VS1003::readRegister (unsigned char addressbyte)
{
  while (!digitalRead(_DREQ)) ; //Wait for DREQ to go high indicating IC is available
  digitalWrite(_XCS, LOW); //Select control
  //SCI consists of instruction byte, address byte, and 16-bit data word.
  SPI.transfer(0x03); //Read instruction
  SPI.transfer(addressbyte);
  unsigned char response1 = SPI.transfer(0xFF); //Read the first byte
  unsigned char response2 = SPI.transfer(0xFF); //Read the second byte
  digitalWrite(_XCS, HIGH); //Deselect Control
  unsigned int res = ((unsigned int)response1 << 8) | ((unsigned int)response2 & 0xff);
  printf("r1 = %x, r2 = %x, res = %x\n",
    (unsigned int)response1, (unsigned int)response2, res); // Or whatever is required to get these printouts
  return res;
};
What do you get now? Please test with 0x0, 0x7f, 0x80, 0xff, and 0x100 to SCI_AICTRL0.

I'm sure we are very close now!

Kind regards,
- Henrik
Good signatures never die. They just fade away.
Zigon
User
Posts: 13
Joined: Mon 2016-12-19 5:09

Re: VS1003 WAV recording

Post by Zigon »

Hi
I tested your new code and i think it working :) finally! :D sorry bout have bad code to start with
Image
I tried then to wav record, i can play the file till the player gives an error, but all it has is minimal white noise, guessing it probably cos i didn't properly set up the header file after recording which i will try and get corrected now
Zigon
User
Posts: 13
Joined: Mon 2016-12-19 5:09

Re: VS1003 WAV recording

Post by Zigon »

So for my header i need to calculate
("RIFF" header chunk)
0x70, 0x70, 0x70, 0x70, // Chunk payload size (calculate after rec!)
("fact" header chunk)
0xff, 0xff, 0xff, 0xff // Number of Samples (calculate after rec!)
("data" header chunk)
0x70, 0x70, 0x70, 0x70 // Chunk payload size (calculate after rec!)

i am guessing both chunk payloads will be the same value?

there are 505 sample per block of data and each block is 256 bytes
so the number of blocks should be i think?
files size - 512(header size) /256
that means the number of samples would be
(files size - 512(header size) /256) * 505
I am guessing the payload size is just
files size - 512(header size)

Code: Select all

//Used to record sound to a wav file
void VS1003::wavRecord()
{
	File record;
	const unsigned char RIFFHeader0[] = {
		'R' , 'I' , 'F' , 'F' , // Chunk ID (RIFF)
		0x70, 0x70, 0x70, 0x70, // Chunk payload size (calculate after rec!)
		'W' , 'A' , 'V' , 'E' , // RIFF resource format type

		'f' , 'm' , 't' , ' ' , // Chunk ID (fmt )
		0x14, 0x00, 0x00, 0x00, // Chunk payload size (0x14 = 20 bytes)
		0x11, 0x00,             // Format Tag (IMA ADPCM)
		0x01, 0x00,             // Channels (1)
		0x80, 0x3e, 0x00, 0x00, // Sample Rate, 0x3e80 = 16.0kHz
		0xd7, 0x0f, 0x00, 0x00, // Average Bytes Per Second
		0x00, 0x01,             // Data Block Size (256 bytes) 
		0x04, 0x00,             // ADPCM encoded bits per sample (4 bits)
		0x02, 0x00,             // Extra data (2 bytes)
		0xf9, 0x01,             // Samples per Block (505 samples)

		'f' , 'a' , 'c' , 't' , // Chunk ID (fact)
		0xc8, 0x01, 0x00, 0x00, // Chunk payload size (456 bytes (zeropad!))
		0xff, 0xff, 0xff, 0xff  // Number of Samples (calculate after rec!)
	};
	const unsigned char RIFFHeader504[] = {
		'd' , 'a' , 't' , 'a' , // Chunk ID (data)
		0x70, 0x70, 0x70, 0x70  // Chunk payload size (calculate after rec!)
	};

	//data buffer for saving to disk
	unsigned char db[512];
	uint16_t dataToRead = 0;
	int idx = 0;
	if (!(record = SD.open("RECORD1.WAV", FILE_WRITE)))
	{
		Serial.println("Failed to open record file in write mode");
	}
	record.seek(0);
	// Create a wav file and create the nescary header
	record.write(RIFFHeader0,sizeof(RIFFHeader0));
	for (int i = 0; i<448; i++)
	{
		record.write('0');
	}
	record.seek(504);
	record.write(RIFFHeader504,sizeof(RIFFHeader504));
	record.flush();
	Serial.println(record.size());
	Serial.print("Mode REGISTER: 0x");
	Serial.print(readRegister(SCI_MODE), HEX);
	Serial.print(", Status REGISTER: 0x");
	Serial.println(readRegister(SCI_STATUS), HEX);
	//Set sample rate divider=9
	writeRegister(SCI_AICTRL0, 0x00, 0x09);
	delay(100);
	//AutoGain
	writeRegister(SCI_AICTRL1, 0x10, 0x00);
	delay(100);
	//RECORD,NEWMODE,RESET
	writeRegister(SCI_MODE, 0x18, 0x04);
	delay(1000);
	
	Serial.print("Mode REGISTER: 0x");
	Serial.print(readRegister(SCI_MODE), HEX);
	Serial.print(", Status REGISTER: 0x");
	Serial.println(readRegister(SCI_STATUS), HEX);

	int i = 0;
	while (i<100)
	{
		//wait until 512 bytes become available
		do {
			dataToRead = readRegister(SCI_HDAT1);
			Serial.print("data words to read: ");
			Serial.println(dataToRead);
			delay(100);
		} while (dataToRead < 256 || dataToRead >= 896);

		Serial.println("Reading data...");
		while (idx < 512)
		{
			uint16_t w = readRegister(SCI_HDAT0);
			db[idx++] = w >> 8;
			db[idx++] = w & 0xFF;
		}
		Serial.println("Finished reading data...");
		idx = 0;
		Serial.println("Writing to disk..");
		record.write(db,sizeof(db));
		Serial.println("Finished writing to disk..");
		record.flush();
		i++;
	}
	Serial.print("Recording finished :), File Size");
	Serial.println(record.size());
	Serial.print("Writing meta data..");
	unsigned int numSamp = (record.size() - 512) * 505;
	unsigned int paySize = record.size() - 512;
	Serial.print(",Number of samples:");
	Serial.print(numSamp);
	Serial.print(" ,Payload Size");
	Serial.print(paySize);
	record.seek(4);
	record.write(paySize);
	record.seek(48);
	record.write(numSamp);
	record.seek(508);
	record.write(paySize);
	record.close();
};
There what the debug values are
Image
oh and that didn't work :(
Post Reply