Skip to content

Commit fde836e

Browse files
committed
Adding ability to record and playback WAV files.
1 parent 2f39e70 commit fde836e

File tree

3 files changed

+372
-0
lines changed

3 files changed

+372
-0
lines changed

Adafruit_VS1053.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,73 @@ void Adafruit_VS1053::startRecordOgg(boolean mic) {
619619
;
620620
}
621621

622+
void Adafruit_VS1053::startRecordWav(boolean mic, uint16_t samplerate) {
623+
softReset();
624+
while (!readyForData())
625+
;
626+
627+
sciWrite(VS1053_SCI_AICTRL0, samplerate); // Sample rate between 8000 and
628+
// 48000
629+
sciWrite(VS1053_SCI_AICTRL1, 0); // Recording gain : 1024 : 1.If 0, use AGC
630+
sciWrite(VS1053_SCI_AICTRL2, 4096); // Maximum AGC level: 1024 = 1. Only used
631+
// if SCI_AICTRL1 is set to 0.
632+
// Miscellaneous bits that also must be set before recording.
633+
// sciWrite(VS1053_SCI_AICTRL3, 0); //joint stereo AGC + IMA ADPCM
634+
sciWrite(VS1053_SCI_AICTRL3, 2); // LEFT only
635+
// sciWrite(VS1053_SCI_AICTRL3, 3); //RIGHT only
636+
// sciWrite(VS1053_SCI_AICTRL3, 4); //joint stereo AGC + LINEAR PCM
637+
638+
while (!readyForData())
639+
;
640+
641+
if (mic) {
642+
sciWrite(VS1053_REG_MODE, VS1053_MODE_SM_RESET | VS1053_MODE_SM_ADPCM |
643+
VS1053_MODE_SM_SDINEW);
644+
} else {
645+
sciWrite(VS1053_REG_MODE, VS1053_MODE_SM_RESET | VS1053_MODE_SM_LINE1 |
646+
VS1053_MODE_SM_ADPCM | VS1053_MODE_SM_SDINEW);
647+
}
648+
649+
while (!readyForData())
650+
;
651+
652+
// IMA fix patch
653+
sciWrite(VS1053_REG_WRAMADDR, 0x8010);
654+
sciWrite(VS1053_REG_WRAM, 0x3e12);
655+
sciWrite(VS1053_REG_WRAM, 0xb817);
656+
sciWrite(VS1053_REG_WRAM, 0x3e14);
657+
sciWrite(VS1053_REG_WRAM, 0xf812);
658+
sciWrite(VS1053_REG_WRAM, 0x3e01);
659+
sciWrite(VS1053_REG_WRAM, 0xb811);
660+
sciWrite(VS1053_REG_WRAM, 0x0007);
661+
sciWrite(VS1053_REG_WRAM, 0x9717);
662+
sciWrite(VS1053_REG_WRAM, 0x0020);
663+
sciWrite(VS1053_REG_WRAM, 0xffd2);
664+
sciWrite(VS1053_REG_WRAM, 0x0030);
665+
sciWrite(VS1053_REG_WRAM, 0x11d1);
666+
sciWrite(VS1053_REG_WRAM, 0x3111);
667+
sciWrite(VS1053_REG_WRAM, 0x8024);
668+
sciWrite(VS1053_REG_WRAM, 0x3704);
669+
sciWrite(VS1053_REG_WRAM, 0xc024);
670+
sciWrite(VS1053_REG_WRAM, 0x3b81);
671+
sciWrite(VS1053_REG_WRAM, 0x8024);
672+
sciWrite(VS1053_REG_WRAM, 0x3101);
673+
sciWrite(VS1053_REG_WRAM, 0x8024);
674+
sciWrite(VS1053_REG_WRAM, 0x3b81);
675+
sciWrite(VS1053_REG_WRAM, 0x8024);
676+
sciWrite(VS1053_REG_WRAM, 0x3f04);
677+
sciWrite(VS1053_REG_WRAM, 0xc024);
678+
sciWrite(VS1053_REG_WRAM, 0x2808);
679+
sciWrite(VS1053_REG_WRAM, 0x4800);
680+
sciWrite(VS1053_REG_WRAM, 0x36f1);
681+
sciWrite(VS1053_REG_WRAM, 0x9811);
682+
sciWrite(VS1053_REG_WRAMADDR, 0x8028);
683+
sciWrite(VS1053_REG_WRAM, 0x2a00);
684+
sciWrite(VS1053_REG_WRAM, 0x040e);
685+
}
686+
687+
void Adafruit_VS1053::stopRecordWav(void) { sciWrite(VS1053_SCI_AICTRL3, 1); }
688+
622689
void Adafruit_VS1053::GPIO_pinMode(uint8_t i, uint8_t dir) {
623690
if (i > 7)
624691
return;

Adafruit_VS1053.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,16 @@ class Adafruit_VS1053 {
255255
* @brief Stop the recording
256256
*/
257257
void stopRecordOgg(void);
258+
/*!
259+
* @brief Start recording
260+
* @param mic mic=true for microphone input
261+
* @param samplerate sample rate in hz (8000 - 48000)
262+
*/
263+
void startRecordWav(boolean mic, uint16_t samplerate);
264+
/*!
265+
* @brief Stop the recording
266+
*/
267+
void stopRecordWav(void);
258268
/*!
259269
* @brief Returns the number of words recorded
260270
* @return 2-byte unsigned int with the number of words

examples/record_wav/record_wav.ino

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/***************************************************
2+
This is an example for the Adafruit VS1053 Codec Breakout
3+
4+
Designed specifically to work with the Adafruit VS1053 Codec Breakout
5+
----> https://www.adafruit.com/products/1381
6+
7+
Adafruit invests time and resources providing this open source code,
8+
please support Adafruit and open-source hardware by purchasing
9+
products from Adafruit!
10+
11+
Based on code written by Limor Fried/Ladyada for Adafruit Industries.
12+
Wav file recording by Ben Hitchcock
13+
BSD license, all text above must be included in any redistribution
14+
****************************************************/
15+
16+
// This is a beta demo of Wav file recording.
17+
// Connect a button between digital 7 on the Arduino and ground,
18+
// press and hold to record. Once the button is lifted, the recording will play back.
19+
20+
// A mic or line-in connection is required. See page 13 of the
21+
// datasheet for wiring
22+
23+
// include SPI, MP3 and SD libraries
24+
#include <SPI.h>
25+
#include <Adafruit_VS1053.h>
26+
#include <SD.h>
27+
28+
// define the pins used
29+
#define RESET 9 // VS1053 reset pin (output)
30+
#define CS 10 // VS1053 chip select pin (output)
31+
#define DCS 8 // VS1053 Data/command select pin (output)
32+
#define CARDCS 4 // Card chip select pin
33+
#define DREQ 3 // VS1053 Data request, ideally an Interrupt pin
34+
35+
#define REC_BUTTON 7
36+
37+
Adafruit_VS1053_FilePlayer musicPlayer = Adafruit_VS1053_FilePlayer(RESET, CS, DCS, DREQ, CARDCS);
38+
39+
File recording; // the file we will save our recording to
40+
#define RECBUFFSIZE 128 // 64 or 128 bytes.
41+
uint8_t recording_buffer[RECBUFFSIZE];
42+
43+
void setup() {
44+
Serial.begin(9600);
45+
Serial.println("Adafruit VS1053 Wav Recording Test");
46+
47+
// initialise the music player
48+
if (!musicPlayer.begin()) {
49+
Serial.println("VS1053 not found");
50+
while (1); // don't do anything more
51+
}
52+
53+
musicPlayer.sineTest(0x44, 500); // Make a tone to indicate VS1053 is working
54+
55+
56+
if (!SD.begin(CARDCS)) {
57+
Serial.println("SD failed, or not present");
58+
while (1); // don't do anything more
59+
}
60+
Serial.println("SD OK!");
61+
62+
// Set volume for left, right channels. lower numbers == louder volume!
63+
musicPlayer.setVolume(10,10);
64+
65+
// when the button is pressed, record!
66+
pinMode(REC_BUTTON, INPUT);
67+
digitalWrite(REC_BUTTON, HIGH);
68+
69+
musicPlayer.softReset();
70+
}
71+
72+
uint8_t isRecording = false;
73+
uint16_t bytesWritten = 0;
74+
uint16_t sampleRate = 8000;
75+
76+
void loop() {
77+
char filename[15];
78+
79+
if (!isRecording && !digitalRead(REC_BUTTON)) {
80+
Serial.println("Begin recording");
81+
isRecording = true;
82+
83+
// Check if the file exists already
84+
strcpy(filename, "RECORD00.WAV");
85+
for (uint8_t i = 0; i < 100; i++) {
86+
filename[6] = '0' + i/10;
87+
filename[7] = '0' + i%10;
88+
// create if does not exist, do not open existing, write, sync after write
89+
if (! SD.exists(filename)) {
90+
break;
91+
}
92+
}
93+
Serial.print("Recording to "); Serial.println(filename);
94+
95+
createWavTemplate(filename, sampleRate);
96+
97+
recording = SD.open(filename, FILE_WRITE);
98+
if (! recording) {
99+
Serial.println("Couldn't open file to record!");
100+
while (1);
101+
}
102+
musicPlayer.startRecordWav(true, sampleRate); // use microphone (for linein, pass in 'false')
103+
}
104+
105+
if (isRecording)
106+
saveRecordedData(isRecording);
107+
108+
if (isRecording && digitalRead(REC_BUTTON)) {
109+
110+
Serial.println("End recording");
111+
112+
isRecording = false;
113+
// flush all the data!
114+
bytesWritten = saveRecordedData(isRecording);
115+
116+
// Note: We need to read a full block before telling the VS1053 to stop recording.
117+
musicPlayer.stopRecordWav();
118+
119+
// close it up
120+
recording.close();
121+
122+
finalizeWavTemplate(filename);
123+
124+
Serial.print(bytesWritten); Serial.println(" Bytes Written");
125+
126+
musicPlayer.softReset();
127+
128+
Serial.print("Playing track "); Serial.println(filename);
129+
130+
musicPlayer.playFullFile(filename);
131+
Serial.println("Finished Playing track");
132+
}
133+
}
134+
135+
uint16_t saveRecordedData(boolean isrecord) {
136+
uint16_t written = 0;
137+
138+
// read how many words are waiting for us
139+
uint16_t wordswaiting = musicPlayer.recordedWordsWaiting();
140+
141+
// try to process 128 words (256 bytes) at a time, for best speed
142+
while (wordswaiting > 128) {
143+
//Serial.print("Waiting: "); Serial.println(wordswaiting);
144+
// for example 128 bytes x 4 loops = 512 bytes
145+
for (int x=0; x < 256/RECBUFFSIZE; x++) {
146+
// fill the buffer!
147+
for (uint16_t addr=0; addr < (RECBUFFSIZE); addr+=2) {
148+
uint16_t t = musicPlayer.recordedReadWord();
149+
//Serial.println(t, HEX);
150+
recording_buffer[addr] = highByte(t);
151+
recording_buffer[addr+1] = lowByte(t);
152+
}
153+
if (! recording.write(recording_buffer, RECBUFFSIZE)) {
154+
Serial.print("Couldn't write "); Serial.println(RECBUFFSIZE);
155+
while (1);
156+
}
157+
}
158+
// flush 256 bytes at a time
159+
recording.flush();
160+
written += 128;
161+
wordswaiting -= 128;
162+
}
163+
164+
if (!isrecord) {
165+
166+
// The recorder prefers us to stop at the block boundary (128 words for mono, 256 words for stereo).
167+
// Hence we have to wait a bit for the recorder to fill up the block, and read that entire block
168+
// before continuing.
169+
// Here we take note of how many words are waiting when the user let go of the button, and we
170+
// pause until there is a full block able to be read.
171+
172+
wordswaiting = musicPlayer.recordedWordsWaiting();
173+
uint16_t wordsToSave = wordswaiting;
174+
175+
// Pause until a full block is waiting to be read.
176+
while(wordswaiting < 128){
177+
wordswaiting = musicPlayer.recordedWordsWaiting();
178+
}
179+
180+
// Read a full block (128 words for mono)
181+
uint16_t addr = 0;
182+
for (int x=0; x < 128; x++) {
183+
// Read a sample from the recorder
184+
uint16_t t = musicPlayer.recordedReadWord();
185+
186+
// If we're reading samples from before the button was released, then write them to the buffer
187+
if(x < wordsToSave){
188+
recording_buffer[addr] = highByte(t);
189+
recording_buffer[addr+1] = lowByte(t);
190+
addr += 2;
191+
192+
// If the buffer is full, save it
193+
if (addr >= RECBUFFSIZE) {
194+
if (!recording.write(recording_buffer, addr)) {
195+
Serial.println("Couldn't write!"); while (1);
196+
}
197+
written += addr;
198+
addr = 0;
199+
}
200+
}
201+
}
202+
203+
// Save any remaining samples
204+
if (addr != 0) {
205+
if (!recording.write(recording_buffer, addr)) {
206+
Serial.println("Couldn't write!"); while (1);
207+
}
208+
written += addr;
209+
}
210+
211+
recording.flush();
212+
}
213+
214+
return written;
215+
}
216+
217+
// Create a WAV file header
218+
void createWavTemplate(const char* filename, unsigned int sampleRate){
219+
220+
if(SD.exists(filename)){
221+
SD.remove(filename);
222+
}
223+
224+
File sFile = SD.open(filename, FILE_WRITE);
225+
if(!sFile){
226+
return;
227+
} else {
228+
229+
sFile.seek(0);
230+
sFile.write((byte*)"RIFF WAVEfmt ", 16); // Note: the data after "RIFF" will be overwritten with the file size
231+
232+
// NOTE: Little endian! The order of the data bytes is reversed.
233+
234+
// Chunk size: 0 0 0 20, format code 0x11 for IMA ADPCM, number channels 0 1, sampleRate (4 bytes)
235+
byte data[] = {20,0,0,0, 0x11,0, 1,0, lowByte(sampleRate),highByte(sampleRate), 0, 0};
236+
sFile.write((byte*)data,12);
237+
238+
unsigned int byteRate = 0xfd7; // 4 bit ADPCM 8 kHz Mono
239+
data[0] = lowByte(byteRate); data[1] = highByte(byteRate); data[2] = 0; data[3] = 0; // Data rate: Sample rate * bytes per sample
240+
241+
data[4] = 0; data[5] = 1; // BlockAlign, in this case it is 16 (0x01 0x00)
242+
243+
data[6] = 0x04; data[7] = 0; // Bits per sample, in this case 4-bit ADPCM
244+
245+
data[8] = 0x02; data[9] = 0; // Byte Extra Data
246+
247+
data[10] = 0xf9; data[11] = 0x01; // Samples per block (505)
248+
249+
sFile.write((byte*)data, 12);
250+
251+
sFile.write((byte*)"fact", 4); // SubChunk2ID
252+
253+
data[0] = 0x04; data[1] = 0x00; data[2] = 0x00; data[3] = 0x00; // SubChunk2Size - in this case 4 bytes
254+
255+
data[4] = 0x00; data[5] = 0x00; data[6] = 0x00; data[7] = 0x00; // NumOfSamples - will be overwritten when finalizing the wav header
256+
sFile.write((byte*)data,8);
257+
258+
sFile.write((byte*)"data ", 8); // Start of data portion of the file
259+
260+
sFile.close();
261+
}
262+
}
263+
264+
void finalizeWavTemplate(const char* filename){
265+
unsigned long fSize = 0;
266+
267+
#ifdef FILE_APPEND
268+
File sFile = SD.open(filename, FILE_WRITE);
269+
#else
270+
File sFile = SD.open(filename, O_READ | O_WRITE);
271+
#endif
272+
273+
if(!sFile){
274+
return;
275+
}
276+
fSize = sFile.size() - 8;
277+
278+
// Set the file size header after the RIFF placeholder
279+
sFile.seek(4); byte data[4] = {lowByte(fSize),highByte(fSize), (byte)(fSize >> 16), (byte)(fSize >> 24)};
280+
sFile.write(data, 4);
281+
282+
// Set the data size header (bytes)
283+
sFile.seek(56);
284+
fSize = fSize - 52;
285+
data[0] = lowByte(fSize); data[1]=highByte(fSize); data[2]=(byte)(fSize >> 16); data[3]= (byte)(fSize >> 24);
286+
sFile.write((byte*)data,4);
287+
288+
// Set the number of samples header (for IMA ADPCM this is: numBlocks * 505)
289+
unsigned long numSamples = fSize / 256 * 505; // For IMA ADPCM there are 256 bytes to a block.
290+
data[0] = lowByte(numSamples); data[1] = highByte(numSamples); data[2] = (byte)(numSamples >> 16); data[3] = (byte)(numSamples >> 24);
291+
sFile.seek(48);
292+
sFile.write((byte*)data,4);
293+
294+
sFile.close();
295+
}

0 commit comments

Comments
 (0)