Dear Pete
Firstly, thank you for the response!

Originally Posted by
el_supremo
Have you tried the unmodified WavFileWriter code to make sure that it is creating the file correctly using unmodified data?
Pete
Could you elaborate more on this? I ran WavFilerWriter.ino and it worked, but since then I have only been modifying WavFileWriter.Ino. The picture up there (apologies) is from WavFileWriter.cpp which I am trying to change... I will post the code now!
Code:
/*
- PROJECT (E) 448 (SKIPSIE == FINAL YEAR PROJECT)
- Jonathan Paul Hendricks, University of Stellenbosch, 2020
- TOPIC: Long term-low cost acoustic monitor
- Monitor dolphin sounds and other marine fauna.
*/
//Includes & Declarations & Constants
#include "WavFileWriter.hpp"
#include <SerialFlash.h>
#include <Audio.h>
#include <Wire.h>
#include <TimeLib.h>
#define FILE_BASE_NAME "REC"
#include <OpenAudio_ArduinoLibrary.h>
const int Fs = 44100; // Frequency to sample
const int myMic = AUDIO_INPUT_MIC; // Mic input
unsigned long prevMil = 0;
unsigned long prevMil2 = 0;
unsigned long prevMil3 = 0;
const long interval = 15000;
const long a = 12000;
int flag;
int flag2;
int flag3;
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
char fileName[] = FILE_BASE_NAME "00.wav";
int buttonState = 0;
const int buttonPin = 1;
AudioPlaySdWav kingSD; //Kingston SD Card
AudioInputI2S audioIn; //Audio Connection
AudioOutputI2S audioOut; //Audio Connection
AudioRecordQueue queue1; //To write data to SD Card
AudioConnection patchCord1(audioIn, 0, queue1, 0); //-Record data and queue it
AudioConnection patchCord2(kingSD, 0, audioOut, 0); //-perform read/write functions to save
AudioConnection patchCord3(kingSD, 0, audioOut, 1); // data and play through Audio Connection output
AudioControlSGTL5000_Extended AudioShield;
WavFileWriter wavWriter(queue1);
// ---------------
// Initial setup run at start of program
void setup() {
SdFile::dateTimeCallback(dateTime);
Serial.begin(9600);
AudioMemory(60);
AudioShield.enable();
AudioShield.inputSelect(myMic);
AudioShield.micGain(63); //0-63
AudioShield.volume(0.75); //0-1
AudioShield.adcHighPassFilterDisable();
AudioShield.micBiasEnable(1.25);
setSyncProvider(getTeensy3Time); //Initialise this
Serial.println("Powered up & Ready to go!");
pinMode(buttonPin, INPUT);
}
// ---------------
//loop function
void loop() {
if (digitalRead(buttonPin) == LOW) {
unsigned long currMil = millis();
if (flag3 == 0) {
flag3 = 1;
Serial.println("Start Recording");
wavWriter.open(fileName, Fs, 1);
}
if (currMil - prevMil >= interval) {
while (SD.exists(fileName)) {
if (fileName[BASE_NAME_SIZE + 1] != '9') {
fileName[BASE_NAME_SIZE + 1]++;
} else if (fileName[BASE_NAME_SIZE] != '9') {
fileName[BASE_NAME_SIZE + 1] = '0';
fileName[BASE_NAME_SIZE]++;
} else {
Serial.println(F("Can't create file name"));
return;
}
}
prevMil = currMil;
Serial.println("Start Recording!");
wavWriter.open(fileName, Fs, 1);
}
if (currMil - prevMil2 >= a + interval) {
prevMil2 = currMil - a;
wavWriter.close();
Serial.println("Stop Recording");
digitalClockDisplay();
}
if ((currMil - prevMil3) >= a && flag2 == 1) {
Serial.println("Stop recording");
wavWriter.close();
flag2 = 0;
digitalClockDisplay();
}
if (wavWriter.isWriting()) {
wavWriter.update();
}
} else {
unsigned long currMil = millis();
prevMil = currMil;
prevMil2 = currMil;
prevMil3 = currMil;
flag2 = 1;
flag3 = 0;
wavWriter.close();
delay(500);
}
}
// ---------------
/*Additional functions for implementation of acoustic monitor*/
//Timing functions
time_t getTeensy3Time() {
return Teensy3Clock.get();
}
void digitalClockDisplay() {
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
/* code to process time sync messages from the serial port */
#define TIME_HEADER "T" // Header tag for serial time sync message
unsigned long processSyncMessage() {
unsigned long pctime = 0L;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if (Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
return pctime;
if ( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
pctime = 0L; // return 0 to indicate that the time is not valid
}
}
return pctime;
}
void printDigits(int digits) {
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
void dateTime(uint16_t* date, uint16_t* time)
{
*date = FAT_DATE(year(), month(), day());
*time = FAT_TIME(hour(), minute(), second());
}
That is my code for WavFileWriter.Ino, where I simply do some scheduling operations to be able to record every interval for a certain amount of time and store the data to an SD card in wav format (process starts when pressing a lock-button).
Code:
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//
// This code was inspired by the Recorder example of Paul Stoffregens Audio library
// and the SFML SoundFileWriterWav class
// Copyright (C) 2018 Maximilian Wagenbach (aka Foaly)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#include "WavFileWriter.hpp"
#include <SPI.h>
namespace {
// Teensy Audio Shield Defaults
const uint8_t SDcard_CS_Pin = 10;
const uint8_t SDcard_MOSI_Pin = 11;
const uint8_t SDcard_SCK_Pin = 13;
// Use these with the Teensy 3.5 & 3.6 SD card (Should work, but not yet tested)
//const uint8_t SDcard_CS_Pin = BUILTIN_SDCARD
//const uint8_t SDcard_MOSI_Pin = 11 // not actually used
//const uint8_t SDcard_SCK_Pin = 13 // not actually used
// Use these for the SD+Wiz820 or other adaptors (Should work, but not yet tested)
//const uint8_t SDcard_CS_Pin = 4
//const uint8_t SDcard_MOSI_Pin = 11
//const uint8_t SDcard_SCK_Pin = 13
// The following functions takes integers in host byte order
// and writes them to a stream as little endian
void encode(File& file, uint16_t value) {
uint8_t bytes[] =
{
static_cast<uint8_t>(value & 0xFF),
static_cast<uint8_t>(value >> 8)
};
file.write(reinterpret_cast<const uint8_t*>(bytes), sizeof(bytes));
}
void encode(File& file, uint32_t value) {
uint8_t bytes[] =
{
static_cast<uint8_t> (value & 0x000000FF),
static_cast<uint8_t>((value & 0x0000FF00) >> 8),
static_cast<uint8_t>((value & 0x00FF0000) >> 16),
static_cast<uint8_t>((value & 0xFF000000) >> 24),
};
file.write(reinterpret_cast<const uint8_t*>(bytes), sizeof(bytes));
}
}
WavFileWriter::WavFileWriter(AudioRecordQueue& queue) :
m_isWriting(false),
m_queue(queue)
{
}
bool WavFileWriter::open(const char *fileName, unsigned int sampleRate, unsigned int channelCount) {
if (m_isWriting) {
Serial.println("Cannot write WAV file. Already writing one.");
return false;
}
// Initialize the SD card
SPI.setMOSI(SDcard_MOSI_Pin);
SPI.setSCK(SDcard_SCK_Pin);
if (!(SD.begin(SDcard_CS_Pin))) {
Serial.println("Unable to access the SD card while trying to write WAV file.");
return false;
}
if (SD.exists(fileName)) {
SD.remove(fileName);
}
m_file = SD.open(fileName, FILE_WRITE);
if (!m_file) {
Serial.println("Could not open file while trying to write WAV file.");
return false;
}
m_queue.begin();
m_isWriting = true;
m_totalBytesWritten = 0;
writeHeader(sampleRate, channelCount);
return true;
}
bool WavFileWriter::isWriting() {
return m_isWriting;
}
void WavFileWriter::writeHeader(unsigned int sampleRate, unsigned int channelCount) {
// Write the main chunk ID
uint8_t mainChunkId[4] = {'R', 'I', 'F', 'F'};
m_file.write(mainChunkId, sizeof(mainChunkId));
// Write the main chunk header
uint32_t mainChunkSize = 0; // placeholder, will be written on closing
encode(m_file, mainChunkSize);
uint8_t mainChunkFormat[4] = {'W', 'A', 'V', 'E'};
m_file.write(mainChunkFormat, sizeof(mainChunkFormat));
// Write the sub-chunk 1 ("format") id and size
uint8_t fmtChunkId[4] = {'f', 'm', 't', ' '};
m_file.write(fmtChunkId, sizeof(fmtChunkId));
uint32_t fmtChunkSize = 16;
encode(m_file, fmtChunkSize);
// Write the format (PCM)
uint16_t format = 1;
encode(m_file, format);
// Write the sound attributes
encode(m_file, static_cast<uint16_t>(channelCount));
encode(m_file, static_cast<uint32_t>(sampleRate));
uint32_t byteRate = sampleRate * channelCount * 2;
encode(m_file, byteRate);
uint16_t blockAlign = channelCount * 2;
encode(m_file, blockAlign);
uint16_t bitsPerSample = 16;
encode(m_file, bitsPerSample);
// Write the sub-chunk 2 ("data") id and size
uint8_t dataChunkId[4] = {'d', 'a', 't', 'a'};
m_file.write(dataChunkId, sizeof(dataChunkId));
uint32_t dataChunkSize = 0; // placeholder, will be written on closing
encode(m_file, dataChunkSize);
m_totalBytesWritten += 44;
}
bool WavFileWriter::update() {
if (!m_isWriting)
return false;
if (m_queue.available() < 2)
return false;
// Fetch 2 blocks from the audio library and copy
// into a 512 byte buffer. The Arduino SD library
// is most efficient when full 512 byte sector size
// writes are used.
memcpy(m_buffer, m_queue.readBuffer(), 256);
m_queue.freeBuffer();
memcpy(m_buffer + 256, m_queue.readBuffer(), 256);
m_queue.freeBuffer();
for (int k = 0; k < 512; k++) {
m_smooth = 0.985f * (m_smooth + m_buffer[k] - m_buffer[k - 1]) ;
m_out[k] = m_smooth;
}
// write all 512 bytes to the SD card
//elapsedMicros usec = 0;
m_file.write(m_out, 512);
// Uncomment these lines to see how long SD writes
// are taking. A pair of audio blocks arrives every
// 5802 microseconds, so hopefully most of the writes
// take well under 5802 us. Some will take more, as
// the SD library also must write to the FAT tables
// and the SD card controller manages media erase and
// wear leveling. The m_queue object can buffer
// approximately 301700 us of audio, to allow time
// for occasional high SD card latency, as long as
// the average write time is under 5802 us.
//Serial.print("SD write, us=");
// Serial.println(usec);
m_totalBytesWritten += 512;
return true;
}
bool WavFileWriter::close() {
if (!m_isWriting)
return false;
m_queue.end();
while (m_queue.available() > 0) {
m_file.write(reinterpret_cast<const uint8_t*>(m_queue.readBuffer()), 256);
m_queue.freeBuffer();
m_totalBytesWritten += 256;
}
Serial.print("Done! Max no. of audio blocks used: ");
Serial.println(AudioMemoryUsageMax());
Serial.print("Bytes written: ");
Serial.println(m_totalBytesWritten);
m_file.flush();
// Update the main chunk size and data sub-chunk size
uint32_t mainChunkSize = m_totalBytesWritten - 8; // 8 bytes RIFF header
uint32_t dataChunkSize = m_totalBytesWritten - 44; // 44 bytes RIFF + WAVE headers
m_file.seek(4);
encode(m_file, mainChunkSize);
m_file.seek(40);
encode(m_file, dataChunkSize);
m_file.close();
m_isWriting = false;
return true;
}
And that is the code I am currently trying to work on, specifically at "WavFileWriter::update() " where I want to alter the data before saving it to the SD card. I was busy trying to do the peak detection in order to vary the gain as well, but I first want to figure out how to alter the data received in the queue object... I hope it makes sense now? Let me know if I can provide more?
EDIT: Here is my WavFilerWriter.hpp
Code:
#include <SD.h>
////////////////////////////////////////////////////////////
//
// This code was inspired by the Recorder example of Paul Stoffregens Audio library
// and the SFML SoundFileWriterWav class
// Copyright (C) 2018 Maximilian Wagenbach (aka Foaly)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#ifndef WAVFILEWRITER_INCLUDE
#define WAVFILEWRITER_INCLUDE
#include <SD.h>
#include <SD_t3.h>
#include "utility/SdFat.h"
#include <record_queue.h>
class WavFileWriter
{
public:
WavFileWriter(AudioRecordQueue& queue);
bool open(const char *fileName, unsigned int sampleRate, unsigned int channelCount);
bool isWriting();
bool update();
bool close();
private:
void writeHeader(unsigned int sampleRate, unsigned int channelCount);
bool m_isWriting;
File m_file;
AudioRecordQueue& m_queue;
uint32_t m_totalBytesWritten;
uint8_t m_buffer[512];
uint8_t m_out[512];
float m_smooth;
};
#endif // WAVFILEWRITER_INCLUDE