#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "sonawave.h"
// Target sample rate
#define SONA_SAMPLE_RATE 10650
//***************************************************************************
// save_sonawave
// Takes in the loaded waveform and generates a SonaWave file out of it.
//---------------------------------------------------------------------------
// param filename ... file name to save into
// param wave ....... pointer to waveform data
//---------------------------------------------------------------------------
// return ........... 0 on success, -1 on failure
//***************************************************************************
int save_sonawave(const char *filename, const Waveform *wave)
{
// Create new file
FILE *file = fopen(filename, "wb");
if (file == NULL) {
fprintf(stderr, "Error[\"%s\"]: can't create SonaWave file\n",
filename);
return -1;
}
// Go through entire waveform and store it into the file
// We use Bresenham's algorithm (or something close enough to it) to
// resample it to the target sample rate regardless of whatever the
// WAV file originally was.
size_t pos = 0; // Current position within waveform
size_t written = 0; // Number of written bytes so far
size_t accum = 0; // Accumulated advance for resampling
while (pos < wave->len) {
// Fetch sample that we're gonna write
// 0xFF is not a valid value, so we change it to something safer (this
// makes the conversion a bit lossy but shouldn't be noticeable)
uint8_t byte = wave->data[pos];
if (byte == 0xFF) byte = 0xFE;
// Write sample into the file
fputc(byte, file);
if (ferror(file)) goto write_error;
written++;
// Advance forwards accounting for sample rate
// (this is where we resample to Sona's sample rate)
accum += wave->rate;
if (accum >= SONA_SAMPLE_RATE) {
size_t advance = accum / SONA_SAMPLE_RATE;
accum -= advance * SONA_SAMPLE_RATE;
pos += advance;
}
}
// Pad with the silence to the next 32 byte boundary
while ((written & 0x1F) != 0x00) {
fputc(0x7F, file);
written++;
}
if (ferror(file)) {
goto write_error;
}
// Write terminator block
for (int i = 0; i < 32; i++)
fputc(0xFF, file);
if (ferror(file))
goto write_error;
// We're done
fclose(file);
return 0;
// We reach here if something went wrong trying to write the file
// (hardware disconnected? out of space?)
write_error:
fprintf(stderr, "Error[\"%s\"]: problem writing SonaWave file\n",
filename);
fclose(file);
return -1;
}