Hi guys,
I'm an Italian maker with a decent/good background in Arduino and C++. In the past I used Teensy to realize several projects, the last one was a sort of answering machine, made with Teensy 3.6. The project worked very well, but I found the approach to audio recording really bad compared to the one used for audio play. So I started writing a hereditary AudioStream class for recording WAV files on SD cards. The library is based on the example file Recorder.ino.
My setup:
- Teensy4.0 with SD card reader soldered under the board using pins 34-39
- microSDHC Class10 UHS-I Transcend 16GB
- Visual Studio Code wtih Platformio
I'm sure the setup is working properly, as the Recorder.ino sketch works.
The problem: after recording, the data block of the wav file is a long succession of 0.
My files:
record_sd_wav.h
record_sd_wav.cpp
main.h
Part of Serial Monitor output:
If I connect the sine with a DIY I2S DAC (with PCM5100) it feels great and if I print in the serial monitor part of the recorder buffer you can see that the values are not 0, so I think it's a saving problem on the SD card, but the header saves it correctly, so what can it be?
I also realized that if I don't declare an output, my custom class doesn't update it, why?
Thanks a lot.
I'm an Italian maker with a decent/good background in Arduino and C++. In the past I used Teensy to realize several projects, the last one was a sort of answering machine, made with Teensy 3.6. The project worked very well, but I found the approach to audio recording really bad compared to the one used for audio play. So I started writing a hereditary AudioStream class for recording WAV files on SD cards. The library is based on the example file Recorder.ino.
My setup:
- Teensy4.0 with SD card reader soldered under the board using pins 34-39
- microSDHC Class10 UHS-I Transcend 16GB
- Visual Studio Code wtih Platformio
I'm sure the setup is working properly, as the Recorder.ino sketch works.
The problem: after recording, the data block of the wav file is a long succession of 0.
My files:
record_sd_wav.h
Code:
#ifndef record_sd_wav_h_
#define record_sd_wav_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "SD.h"
#if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
#define STATE_STOP 13
#define STATE_REC 14
class AudioRecordSdWav : public AudioStream
{
public:
AudioRecordSdWav(void) : AudioStream(1, inputQueueArray) { begin(); }
void begin(void);
bool record(const char *filename);
bool isRecording(void) { return state_record == STATE_REC; }
void stop(void);
virtual void update(void);
private:
audio_block_t *inputQueueArray[1];
void write_header(void);
// wav header
uint32_t ChunkSize;
uint32_t Subchunk1Size = 16; //const ?
uint AudioFormat = 1; //const ?
uint NumChannels = 1;
uint32_t SampleRate = AUDIO_SAMPLE_RATE;
uint BitsPerSample = 16; //const ?
uint32_t ByteRate = SampleRate * NumChannels * (BitsPerSample / 8); // samplerate x channels x (bitspersample / 8)
uint BlockAlign = NumChannels * BitsPerSample / 8;
uint32_t Subchunk2Size = 0L;
uint32_t recByteSaved = 0L;
File wavfile;
uint8_t state_record;
audio_block_t *blocklist[2];
uint8_t buffer[512]; // __attribute__((aligned(4))); // I've tried both with and without
uint8_t i_block; // index block
};
#endif
#endif
record_sd_wav.cpp
Code:
#include "Arduino.h"
#include "record_sd_wav.h"
#include "spi_interrupt.h"
#if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
void AudioRecordSdWav::begin(void)
{
state_record = STATE_STOP;
recByteSaved = 0;
i_block = 0;
if (blocklist[0])
{
release(blocklist[0]);
blocklist[0] = NULL;
}
if (blocklist[1])
{
release(blocklist[1]);
blocklist[1] = NULL;
}
}
bool AudioRecordSdWav::record(const char *filename)
{
stop();
AudioStartUsingSPI();
__disable_irq();
wavfile = SD.open(filename, FILE_WRITE);
__enable_irq();
if (!wavfile)
{
AudioStopUsingSPI();
return false;
}
state_record = STATE_REC;
recByteSaved = 0;
i_block = 0;
return true;
};
void AudioRecordSdWav::stop(void)
{
__disable_irq();
if (state_record != STATE_STOP)
{
write_header();
wavfile.close();
release(blocklist[0]);
release(blocklist[1]);
__enable_irq();
AudioStopUsingSPI();
state_record = STATE_STOP;
}
else
{
__enable_irq();
}
}
void AudioRecordSdWav::update(void)
{
audio_block_t *block;
block = receiveReadOnly();
if (!block)
return;
if (state_record == STATE_STOP) // or !wavfile)
{
release(block);
return;
}
blocklist[i_block] = block;
i_block = (i_block + 1) % 2;
if (!i_block)
{
memcpy(buffer + 0x000, blocklist[0]->data, 256);
memcpy(buffer + 0x100, blocklist[1]->data, 256);
// only to debug
for (int i = 0; i < 8; i++)
{
Serial.print(buffer[i], HEX);
Serial.print(" ");
}
Serial.println();
// end only to debug
__disable_irq();
wavfile.write(buffer, 512);
recByteSaved += 512;
release(blocklist[0]);
release(blocklist[1]);
__enable_irq();
}
}
void AudioRecordSdWav::write_header(void)
{
Subchunk2Size = recByteSaved;
ChunkSize = Subchunk2Size + 36;
wavfile.seek(0);
// "RIFF" chunck descriptor
wavfile.write("RIFF"); // ChunkID
wavfile.write(ChunkSize & 0xff);
wavfile.write((ChunkSize >> 8) & 0xff);
wavfile.write((ChunkSize >> 16) & 0xff);
wavfile.write((ChunkSize >> 24) & 0xff);
wavfile.write("WAVE"); // Format
// "fmt" sub-chunck
wavfile.write("fmt "); // Subchunk1ID
wavfile.write(Subchunk1Size & 0xff);
wavfile.write((Subchunk1Size >> 8) & 0xff);
wavfile.write((Subchunk1Size >> 16) & 0xff);
wavfile.write((Subchunk1Size >> 24) & 0xff);
wavfile.write(AudioFormat & 0xff);
wavfile.write((AudioFormat >> 8) & 0xff);
wavfile.write(NumChannels & 0xff);
wavfile.write((NumChannels >> 8) & 0xff);
wavfile.write(SampleRate & 0xff);
wavfile.write((SampleRate >> 8) & 0xff);
wavfile.write((SampleRate >> 16) & 0xff);
wavfile.write((SampleRate >> 24) & 0xff);
wavfile.write(ByteRate & 0xff);
wavfile.write((ByteRate >> 8) & 0xff);
wavfile.write((ByteRate >> 16) & 0xff);
wavfile.write((ByteRate >> 24) & 0xff);
wavfile.write(BlockAlign & 0xff);
wavfile.write((BlockAlign >> 8) & 0xff);
wavfile.write(BitsPerSample & 0xff);
wavfile.write((BitsPerSample >> 8) & 0xff);
// "data" sub-chunk
wavfile.write("data"); // Subchunk2ID
wavfile.write(Subchunk2Size & 0xff);
wavfile.write((Subchunk2Size >> 8) & 0xff);
wavfile.write((Subchunk2Size >> 16) & 0xff);
wavfile.write((Subchunk2Size >> 24) & 0xff);
}
#endif
main.h
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
//#include <SD_t3.h> // I've tried both with and without
#include <SerialFlash.h>
#include "record_sd_wav.h"
//AudioInputI2S rec_source;
AudioSynthWaveformSine rec_source;
AudioRecordSdWav sdWav;
AudioOutputI2S out_i2s; // <- Why do I need this?
AudioConnection patchCord(rec_source, sdWav);
void setup()
{
AudioMemory(60);
rec_source.amplitude(1);
rec_source.frequency(432);
Serial.begin(115200);
while (!Serial)
{
}
// Initialize the SD card
while (!(SD.begin(BUILTIN_SDCARD)))
{
Serial.println("Unable to access the SD card");
delay(500);
}
Serial.println("Start recording");
pinMode(LED_BUILTIN, OUTPUT);
digitalWriteFast(LED_BUILTIN, HIGH);
Serial.println(sdWav.record("test.wav"));
delay(3000);
sdWav.stop();
digitalWriteFast(LED_BUILTIN, LOW);
Serial.println("Stop recording");
delay(1000);
// print wav file on Serial Monitor
File wavfile = SD.open("test.wav");
Serial.println("header:");
for (int i = 0; i < 44; i++)
{
int value = wavfile.read();
if (value < 16)
Serial.print("0");
Serial.print(value, HEX);
if (i % 4 == 3)
Serial.println();
else
Serial.print(" ");
}
Serial.println("\ndata:");
for (int i = 0; i < 20; i++)
{
int value = wavfile.read();
if (value < 16)
Serial.print("0");
Serial.print(value, HEX);
if (i % 4 == 3)
Serial.println();
else
Serial.print(" ");
}
Serial.println("\n\n");
}
bool var = true;
void loop()
{
delay(100);
}
Part of Serial Monitor output:
Code:
2D 80 D5 80 F5 81 92 83
58 7F 4F 7E CE 7C D1 7A
6E 81 D8 82 B9 84 14 87
7E 7D B3 7B 73 79 BB 76
E1 83 B 86 A8 88 BC 8B
Stop recording
header:
52 49 46 46
24 6E 04 00
57 41 56 45
66 6D 74 20
10 00 00 00
01 00 01 00
44 AC 00 00
88 58 01 00
02 00 10 00
64 61 74 61
00 6E 04 00
data:
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
If I connect the sine with a DIY I2S DAC (with PCM5100) it feels great and if I print in the serial monitor part of the recorder buffer you can see that the values are not 0, so I think it's a saving problem on the SD card, but the header saves it correctly, so what can it be?
I also realized that if I don't declare an output, my custom class doesn't update it, why?
Thanks a lot.