VS1003 WAV recording

Writing software for systems that use VLSI Solution's devices as slave codecs to a host microcontroller.
User avatar
Henrik
VLSI Staff
Posts: 1041
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik » Wed 2017-03-22 13:13

Hello Zigon,

excellent to hear you got your data issues sorted out!

Now with the chunk sizes.

Let's assume that your file size is 65536, or 0x10000 bytes.
Zigon wrote:So for my header i need to calculate
("RIFF" header chunk)
0x70, 0x70, 0x70, 0x70, // Chunk payload size (calculate after rec!)
This is the size of the full file after this 8-byte header.
So it is fileSize-8 = 0xfff8, so bytes are 0xff, 0xf8, 0x00, 0x00.
Zigon wrote:("fact" header chunk)
0xff, 0xff, 0xff, 0xff // Number of Samples (calculate after rec!)
This is (fileSize-512)/256*505 = 128270 = 0x1f50e -> 0x0e, 0xf5, 0x01, 0x00.
Zigon wrote:("data" header chunk)
0x70, 0x70, 0x70, 0x70 // Chunk payload size (calculate after rec!)
This is the size of the data chunk after the 8-byte header.
So fileSize-512 = 0xff00 -> 0x00, 0xff, 0x00, 0x00.

If you still have problems after implementing this, please make a short recording (just a couple of seconds), post it here, and we'll get it sorted out!

Kind regards,
- Henrik

PS. Wee, my 1000th message!
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 » Wed 2017-03-22 23:15

Hi congratulations on your 1000th post :D
So tried recording and it doesn't play at all, just comes up with an error
I am guessing its because i am not writing to the whole 4 bytes
Image
Heres a link to the file through my onedrive
https://1drv.ms/u/s!Ank4msXJY_jqgbdQcAGODBXVIZ3Z1Q
Heres my modified code

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 (SD.exists("RECORD1.WAV"))
	{
		SD.remove("RECORD1.WAV");
	}
	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<10)
	{
		//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 long int paySize1 = record.size() - 8;
	unsigned long int numSamp = ((record.size() - 512)/256) * 505;
	unsigned long int paySize2 = record.size() - 512;
	Serial.print(",Number of samples:");
	Serial.print(numSamp);
	Serial.print(" ,Payload1 Size ");
	Serial.print(paySize1);
	Serial.print(" ,Payload2 Size ");
	Serial.println(paySize2);
	Serial.print("File size now: ");
	Serial.println(record.size());

	record.seek(4);
	record.write(paySize1);
	record.seek(48);
	record.write(numSamp);
	record.seek(508);
	record.write(paySize2);
	record.close();
};
After checking some stuff when i got the time:
Image
i think i am writing the LSB first, and if there is nothing left the others aren't edited so remain default

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

Re: VS1003 WAV recording

Post by Zigon » Thu 2017-03-23 9:05

Quick update
Image
I got the header updating correctly as far as i know,
Heres the new code

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];
	byte data[4];
	uint16_t dataToRead = 0;
	int idx = 0;
	if (SD.exists("RECORD1.WAV"))
	{
		SD.remove("RECORD1.WAV");
	}
	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<10)
	{
		//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 long int paySize1 = record.size() - 8;
	unsigned long int numSamp = ((record.size() - 512)/256) * 505;
	unsigned long int paySize2 = record.size() - 512;
	Serial.print(",Number of samples: 0x");
	Serial.print(numSamp,HEX);
	Serial.print(" ,Payload1 Size: 0x");
	Serial.print(paySize1,HEX);
	Serial.print(" ,Payload2 Size: 0x");
	Serial.println(paySize2,HEX);
	Serial.print("File size now: ");
	Serial.println(record.size());


	data[3] = paySize1 >> 24;//shift it over so only last byte exists
	data[2] = paySize1 >> 16;
	data[1] = paySize1 >> 8;
	data[0] = paySize1;
	record.seek(4);
	record.write(data,sizeof(data));


	data[3] = numSamp >> 24;//shift it over so only last byte exists
	data[2] = numSamp >> 16;
	data[1] = numSamp >> 8;
	data[0] = numSamp;
	record.seek(48);
	record.write(data, sizeof(data));

	data[3] = paySize2 >> 24;//shift it over so only last byte exists
	data[2] = paySize2 >> 16;
	data[1] = paySize2 >> 8;
	data[0] = paySize2;
	record.seek(508);
	record.write(data, sizeof(data));

	record.seek(4);	
	Serial.print("Payload 1");
	for (int i = 0; i<4; i++)
	{
		Serial.print(", Position ");
		Serial.print(4 + i);
		Serial.print(" is 0x");
		Serial.print(record.read(),HEX);
	}
	Serial.println();

	record.seek(48);
	Serial.print("Number of samples");
	for (int i = 0; i<4; i++)
	{
		Serial.print(", Position ");
		Serial.print(48 + i);
		Serial.print(" is 0x");
		Serial.print(record.read(),HEX);
	}
	Serial.println();

	record.seek(508);
	Serial.print("Payload 2");
	for (int i = 0; i<4; i++)
	{
		Serial.print(", Position ");
		Serial.print(508 + i);
		Serial.print(" is 0x");
		Serial.print(record.read(), HEX);
	}
	Serial.println();
	record.close();


};
Link to the new file
https://1drv.ms/u/s!Ank4msXJY_jqgbdRYdV72L89YI92UA

The file does not play at all though

P.S. Header was only 508 bytes long so i added a few a few more zeros in the middle to make it 512

User avatar
Henrik
VLSI Staff
Posts: 1041
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik » Thu 2017-03-23 11:17

Ok, your are now only four bytes off from being golden!

Your header+data+datalength in RECORD1.WAV is still only 508 bytes long while it should be 512. I have corrected the file for you and attached to this message. (It seems to only contain some background noise at around -35 dB level.)

I will show you the difference. Your file is 5628 bytes while it should be 5632 bytes (5632 is divisable with 512, and the software takes pains to make the file match with 512-byte block boundaries). Notice that because my file is different size, so are also the size fields and fact's number-of-samples field.

Original version:

Code: Select all

00000000  52 49 46 46 f4 15 00 00  57 41 56 45 66 6d 74 20  |RIFFô...WAVEfmt |
00000010  14 00 00 00 11 00 01 00  80 3e 00 00 d7 0f 00 00  |.........>..×...|
00000020  00 01 04 00 02 00 f9 01  66 61 63 74 c8 01 00 00  |......ù.factÈ...|
00000030  7b 25 00 00 30 30 30 30  30 30 30 30 30 30 30 30  |{%..000000000000|
00000040  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
000001f0  30 30 30 30 64 61 74 61  70 70 70 70 fc 13 00 00  |0000datappppü...|
00000200  6c 8b 49 a8 c3 f8 28 20  f2 92 c2 39 1c 80 0e 12  |l.I¨Ãø( ò.Â9....|
... much data deleted ...
000015f0  19 e0 21 08 2c 28 0a 62  eb 12 a9 41              |.à!.,(.bë.©A|
Corrected version:

Code: Select all

00000000  52 49 46 46 f8 15 00 00  57 41 56 45 66 6d 74 20  |RIFFø...WAVEfmt |
00000010  14 00 00 00 11 00 01 00  80 3e 00 00 d7 0f 00 00  |.........>..×...|
00000020  00 01 04 00 02 00 f9 01  66 61 63 74 c8 01 00 00  |......ù.factÈ...|
00000030  74 27 00 00 30 30 30 30  30 30 30 30 30 30 30 30  |t'..000000000000|
00000040  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
000001f0  30 30 30 30 30 30 30 30  64 61 74 61 00 14 00 00  |00000000data....|
00000200  00 00 00 00 6c 8b 49 a8  c3 f8 28 20 f2 92 c2 39  |....l.I¨Ãø( ò.Â9|
... much data deleted ...
000015f0  0b 02 fb 08 19 e0 21 08  2c 28 0a 62 eb 12 a9 41  |..û..à!.,(.bë.©A|
00001600
Notice that I had to regenerate bytes 0x200-0x203. They should be the first four bytes of the first 256-byte IMA ADPCM compression block, but you had overwritten them with size data in your file.

Kind regards,
- Henrik

PS. This is my 1003rd message to this forum, and very fittingly with the subject of VS1003, a truly classic IC! It took what we had in VS1002 and added WMA and MIDI playback. As a first it also added an internal clock multiplier PLL that allowed for a higher internal frequency clock.
Attachments
Record1_corrected.wav
Corrected IMA ADPCM file. Bytes 0x200 to 0x203 are still not correct because I didn't have the information for their original contents.
(5.5 KiB) Downloaded 21 times
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 » Thu 2017-03-23 22:34

Hi i fixed all that,
the file size if now divisible by 512
Image
windows media will play it, starts like haly war through though, but all it is is very low nothing
Theres the recording
RECORD1.WAV
(15.5 KiB) Downloaded 19 times
possibly something i am setting in the registers before recording?

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

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

Re: VS1003 WAV recording

Post by Zigon » Fri 2017-03-24 8:21

Quick update :)
If i use line in input, it records sorta, file size is pretty big for how long it records, also only windows media will play it sorta of, groove music just gives up on it.
I am guessing my file headers are still kinda off a bit :(

UPDATE again :)
so i if i change the header to sound sample rate, 8khz,groove music will play it

Oh and when i have the header set to 16khz, it is played very fast, guessing i am a bit off the 16khz speed
RECORD1.WAV
(1.09 MiB) Downloaded 20 times

User avatar
Henrik
VLSI Staff
Posts: 1041
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik » Fri 2017-03-24 11:35

Hello Zigon,

your headers are now fine - except for the sample rate which obviously still needs some work.

What do you have in SCI_CLOCKF? The formula for the sample rate is:
INTERNAL_CLOCK_SPEED / (256 * SCI_AICTRL0)

As for the issues with Groove Music: The original official sample rate for IMA ADPCM was 8 kHz, so it may be that some software (for no good reason) refuse to play IMA ADPCM files that have a different sample rate. If this bothers you, please contact the software developers and ask them to remove this artificial limitation.

Kind regards,
- Henrik

PS. This is my 1006th message. There is no VS1006 so I'll have to stop this "PS" series for the time being. Sigh.
Good signatures never die. They just fade away.

User avatar
Henrik
VLSI Staff
Posts: 1041
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik » Fri 2017-03-24 14:22

Hello again Zigon,
Henrik wrote:your headers are now fine - except for the sample rate which obviously still needs some work.

What do you have in SCI_CLOCKF? The formula for the sample rate is:
INTERNAL_CLOCK_SPEED / (256 * SCI_AICTRL0)
I did some detective work, and it seems clear now that your INTERNAL_CLOCK_SPEED is 2.5*12.288 MHz, when it should be 3.0*12.288 MHz if you use SCI_AICTRL=9 and want sample rate to be 16 kHz. Because your SCI_CLOCKF is incorrect, actual sample rate is 13333 Hz instead of 16000 Hz.

So, set SCI_CLOCKF to 0x8000 before recording to get 16 kHz (I assume you have a 12.288 MHz crystal).

(Detective work part: I googled for some of the lyrics in your song, found out the artist and song name (Sigala: Easy Love), listened to it in YouTube, then tested with different potential sample rates until I got a pitch / speed match. Playing the file back at 13333 Hz gave a perfect match.)

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 » Fri 2017-03-24 23:30

Hi
I had a feeling it was something like that, just didn't know how to change it correctly :)
Thanks for all your help, its much appreciated :)
You guys do an awesome job

Thanks again, Zigon

User avatar
Henrik
VLSI Staff
Posts: 1041
Joined: Tue 2010-06-22 14:10

Re: VS1003 WAV recording

Post by Henrik » Mon 2017-03-27 11:58

You're welcome!

Kind regards,
- Henrik
Good signatures never die. They just fade away.

Post Reply

Who is online

Users browsing this forum: No registered users