summaryrefslogtreecommitdiffstats
path: root/src/archive
diff options
context:
space:
mode:
Diffstat (limited to 'src/archive')
-rw-r--r--src/archive/tinywav.c271
-rw-r--r--src/archive/tinywav.h131
-rw-r--r--src/archive/wavetable.c182
-rw-r--r--src/archive/wavetable.h24
4 files changed, 608 insertions, 0 deletions
diff --git a/src/archive/tinywav.c b/src/archive/tinywav.c
new file mode 100644
index 0000000..7d7f43c
--- /dev/null
+++ b/src/archive/tinywav.c
@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2015-2022, Martin Roth (mhroth@gmail.com)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+
+#include <assert.h>
+#include <string.h>
+#if _WIN32
+#include <winsock.h>
+#include <malloc.h>
+#pragma comment(lib, "Ws2_32.lib")
+#else
+#include <alloca.h>
+#include <netinet/in.h>
+#endif
+#include "tinywav.h"
+
+int tinywav_open_write(TinyWav *tw,
+ int16_t numChannels, int32_t samplerate,
+ TinyWavSampleFormat sampFmt, TinyWavChannelFormat chanFmt,
+ const char *path) {
+#if _WIN32
+ errno_t err = fopen_s(&tw->f, path, "w");
+ assert(err == 0);
+#else
+ tw->f = fopen(path, "w");
+#endif
+ assert(tw->f != NULL);
+ tw->numChannels = numChannels;
+ tw->numFramesInHeader = -1; // not used for writer
+ tw->totalFramesReadWritten = 0;
+ tw->sampFmt = sampFmt;
+ tw->chanFmt = chanFmt;
+
+ // prepare WAV header
+ TinyWavHeader h;
+ h.ChunkID = htonl(0x52494646); // "RIFF"
+ h.ChunkSize = 0; // fill this in on file-close
+ h.Format = htonl(0x57415645); // "WAVE"
+ h.Subchunk1ID = htonl(0x666d7420); // "fmt "
+ h.Subchunk1Size = 16; // PCM
+ h.AudioFormat = (tw->sampFmt-1); // 1 PCM, 3 IEEE float
+ h.NumChannels = numChannels;
+ h.SampleRate = samplerate;
+ h.ByteRate = samplerate * numChannels * tw->sampFmt;
+ h.BlockAlign = numChannels * tw->sampFmt;
+ h.BitsPerSample = 8*tw->sampFmt;
+ h.Subchunk2ID = htonl(0x64617461); // "data"
+ h.Subchunk2Size = 0; // fill this in on file-close
+
+ // write WAV header
+ fwrite(&h, sizeof(TinyWavHeader), 1, tw->f);
+
+ return 0;
+}
+
+int tinywav_open_read(TinyWav *tw, const char *path, TinyWavChannelFormat chanFmt) {
+ tw->f = fopen(path, "rb");
+ assert(tw->f != NULL);
+ size_t ret = fread(&tw->h, sizeof(TinyWavHeader), 1, tw->f);
+ assert(ret > 0);
+ assert(tw->h.ChunkID == htonl(0x52494646)); // "RIFF"
+ assert(tw->h.Format == htonl(0x57415645)); // "WAVE"
+ //assert(tw->h.Subchunk1ID == htonl(0x666d7420)); // "fmt "
+
+ // skip over any other chunks before the "data" chunk
+ bool additionalHeaderDataPresent = false;
+ while (tw->h.Subchunk2ID != htonl(0x64617461)) { // "data"
+ fseek(tw->f, 4, SEEK_CUR);
+ fread(&tw->h.Subchunk2ID, 4, 1, tw->f);
+ additionalHeaderDataPresent = true;
+ }
+ assert(tw->h.Subchunk2ID == htonl(0x64617461)); // "data"
+ if (additionalHeaderDataPresent) {
+ // read the value of Subchunk2Size, the one populated when reading 'TinyWavHeader' structure is wrong
+ fread(&tw->h.Subchunk2Size, 4, 1, tw->f);
+ }
+
+ tw->numChannels = tw->h.NumChannels;
+ tw->chanFmt = chanFmt;
+
+ if (tw->h.BitsPerSample == 32 && tw->h.AudioFormat == 3) {
+ tw->sampFmt = TW_FLOAT32; // file has 32-bit IEEE float samples
+ } else if (tw->h.BitsPerSample == 16 && tw->h.AudioFormat == 1) {
+ tw->sampFmt = TW_INT16; // file has 16-bit int samples
+ } else {
+ tw->sampFmt = TW_FLOAT32;
+ printf("Warning: wav file has %d bits per sample (int), which is not natively supported yet. Treating them as float; you may want to convert them manually after reading.\n", tw->h.BitsPerSample);
+ }
+
+ tw->numFramesInHeader = tw->h.Subchunk2Size / (tw->numChannels * tw->sampFmt);
+ tw->totalFramesReadWritten = 0;
+
+ return 0;
+}
+
+int tinywav_read_f(TinyWav *tw, void *data, int len) {
+ switch (tw->sampFmt) {
+ case TW_INT16: {
+ int16_t *interleaved_data = (int16_t *) alloca(tw->numChannels*len*sizeof(int16_t));
+ size_t samples_read = fread(interleaved_data, sizeof(int16_t), tw->numChannels*len, tw->f);
+ int valid_len = (int) samples_read / tw->numChannels;
+ switch (tw->chanFmt) {
+ case TW_INTERLEAVED: { // channel buffer is interleaved e.g. [LRLRLRLR]
+ for (int pos = 0; pos < tw->numChannels * valid_len; pos++) {
+ ((float *) data)[pos] = (float) interleaved_data[pos] / INT16_MAX;
+ }
+ return valid_len;
+ }
+ case TW_INLINE: { // channel buffer is inlined e.g. [LLLLRRRR]
+ for (int i = 0, pos = 0; i < tw->numChannels; i++) {
+ for (int j = i; j < valid_len * tw->numChannels; j += tw->numChannels, ++pos) {
+ ((float *) data)[pos] = (float) interleaved_data[j] / INT16_MAX;
+ }
+ }
+ return valid_len;
+ }
+ case TW_SPLIT: { // channel buffer is split e.g. [[LLLL],[RRRR]]
+ for (int i = 0, pos = 0; i < tw->numChannels; i++) {
+ for (int j = 0; j < valid_len; j++, ++pos) {
+ ((float **) data)[i][j] = (float) interleaved_data[j*tw->numChannels + i] / INT16_MAX;
+ }
+ }
+ return valid_len;
+ }
+ default: return 0;
+ }
+ }
+ case TW_FLOAT32: {
+ float *interleaved_data = (float *) alloca(tw->numChannels*len*sizeof(float));
+ size_t samples_read = fread(interleaved_data, sizeof(float), tw->numChannels*len, tw->f);
+ int valid_len = (int) samples_read / tw->numChannels;
+ switch (tw->chanFmt) {
+ case TW_INTERLEAVED: { // channel buffer is interleaved e.g. [LRLRLRLR]
+ memcpy(data, interleaved_data, tw->numChannels*valid_len*sizeof(float));
+ return valid_len;
+ }
+ case TW_INLINE: { // channel buffer is inlined e.g. [LLLLRRRR]
+ for (int i = 0, pos = 0; i < tw->numChannels; i++) {
+ for (int j = i; j < valid_len * tw->numChannels; j += tw->numChannels, ++pos) {
+ ((float *) data)[pos] = interleaved_data[j];
+ }
+ }
+ return valid_len;
+ }
+ case TW_SPLIT: { // channel buffer is split e.g. [[LLLL],[RRRR]]
+ for (int i = 0, pos = 0; i < tw->numChannels; i++) {
+ for (int j = 0; j < valid_len; j++, ++pos) {
+ ((float **) data)[i][j] = interleaved_data[j*tw->numChannels + i];
+ }
+ }
+ return valid_len;
+ }
+ default: return 0;
+ }
+ }
+ default: return 0;
+ }
+
+ return len;
+}
+
+void tinywav_close_read(TinyWav *tw) {
+ fclose(tw->f);
+ tw->f = NULL;
+}
+
+int tinywav_write_f(TinyWav *tw, void *f, int len) {
+ switch (tw->sampFmt) {
+ case TW_INT16: {
+ int16_t *z = (int16_t *) alloca(tw->numChannels*len*sizeof(int16_t));
+ switch (tw->chanFmt) {
+ case TW_INTERLEAVED: {
+ const float *const x = (const float *const) f;
+ for (int i = 0; i < tw->numChannels*len; ++i) {
+ z[i] = (int16_t) (x[i] * (float) INT16_MAX);
+ }
+ break;
+ }
+ case TW_INLINE: {
+ const float *const x = (const float *const) f;
+ for (int i = 0, k = 0; i < len; ++i) {
+ for (int j = 0; j < tw->numChannels; ++j) {
+ z[k++] = (int16_t) (x[j*len+i] * (float) INT16_MAX);
+ }
+ }
+ break;
+ }
+ case TW_SPLIT: {
+ const float **const x = (const float **const) f;
+ for (int i = 0, k = 0; i < len; ++i) {
+ for (int j = 0; j < tw->numChannels; ++j) {
+ z[k++] = (int16_t) (x[j][i] * (float) INT16_MAX);
+ }
+ }
+ break;
+ }
+ default: return 0;
+ }
+
+ tw->totalFramesReadWritten += len;
+ size_t samples_written = fwrite(z, sizeof(int16_t), tw->numChannels*len, tw->f);
+ return (int) samples_written / tw->numChannels;
+ }
+ case TW_FLOAT32: {
+ float *z = (float *) alloca(tw->numChannels*len*sizeof(float));
+ switch (tw->chanFmt) {
+ case TW_INTERLEAVED: {
+ tw->totalFramesReadWritten += len;
+ return (int) fwrite(f, sizeof(float), tw->numChannels*len, tw->f);
+ }
+ case TW_INLINE: {
+ const float *const x = (const float *const) f;
+ for (int i = 0, k = 0; i < len; ++i) {
+ for (int j = 0; j < tw->numChannels; ++j) {
+ z[k++] = x[j*len+i];
+ }
+ }
+ break;
+ }
+ case TW_SPLIT: {
+ const float **const x = (const float **const) f;
+ for (int i = 0, k = 0; i < len; ++i) {
+ for (int j = 0; j < tw->numChannels; ++j) {
+ z[k++] = x[j][i];
+ }
+ }
+ break;
+ }
+ default: return 0;
+ }
+
+ tw->totalFramesReadWritten += len;
+ size_t samples_written = fwrite(z, sizeof(float), tw->numChannels*len, tw->f);
+ return (int) samples_written / tw->numChannels;
+ }
+ default: return 0;
+ }
+}
+
+void tinywav_close_write(TinyWav *tw) {
+ uint32_t data_len = tw->totalFramesReadWritten * tw->numChannels * tw->sampFmt;
+
+ // set length of data
+ fseek(tw->f, 4, SEEK_SET);
+ uint32_t chunkSize_len = 36 + data_len;
+ fwrite(&chunkSize_len, sizeof(uint32_t), 1, tw->f);
+
+ fseek(tw->f, 40, SEEK_SET);
+ fwrite(&data_len, sizeof(uint32_t), 1, tw->f);
+
+ fclose(tw->f);
+ tw->f = NULL;
+}
+
+bool tinywav_isOpen(TinyWav *tw) {
+ return (tw->f != NULL);
+}
diff --git a/src/archive/tinywav.h b/src/archive/tinywav.h
new file mode 100644
index 0000000..3abbd65
--- /dev/null
+++ b/src/archive/tinywav.h
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2015-2022, Martin Roth (mhroth@gmail.com)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+
+#ifndef _TINY_WAV_
+#define _TINY_WAV_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// http://soundfile.sapp.org/doc/WaveFormat/
+
+typedef struct TinyWavHeader {
+ uint32_t ChunkID;
+ uint32_t ChunkSize;
+ uint32_t Format;
+ uint32_t Subchunk1ID;
+ uint32_t Subchunk1Size;
+ uint16_t AudioFormat;
+ uint16_t NumChannels;
+ uint32_t SampleRate;
+ uint32_t ByteRate;
+ uint16_t BlockAlign;
+ uint16_t BitsPerSample;
+ uint32_t Subchunk2ID;
+ uint32_t Subchunk2Size;
+} TinyWavHeader;
+
+typedef enum TinyWavChannelFormat {
+ TW_INTERLEAVED, // channel buffer is interleaved e.g. [LRLRLRLR]
+ TW_INLINE, // channel buffer is inlined e.g. [LLLLRRRR]
+ TW_SPLIT // channel buffer is split e.g. [[LLLL],[RRRR]]
+} TinyWavChannelFormat;
+
+typedef enum TinyWavSampleFormat {
+ TW_INT16 = 2, // two byte signed integer
+ TW_FLOAT32 = 4 // four byte IEEE float
+} TinyWavSampleFormat;
+
+typedef struct TinyWav {
+ FILE *f;
+ TinyWavHeader h;
+ int16_t numChannels;
+ int32_t numFramesInHeader; ///< number of samples per channel declared in wav header (only populated when reading)
+ uint32_t totalFramesReadWritten; ///< total numSamples per channel which have been read or written
+ TinyWavChannelFormat chanFmt;
+ TinyWavSampleFormat sampFmt;
+} TinyWav;
+
+/**
+ * Open a file for writing.
+ *
+ * @param numChannels The number of channels to write.
+ * @param samplerate The sample rate of the audio.
+ * @param sampFmt The sample format (e.g. 16-bit integer or 32-bit float) to be used in the file.
+ * @param chanFmt The channel format (how the channel data is layed out in memory)
+ * @param path The path of the file to write to. The file will be overwritten.
+ *
+ * @return The error code. Zero if no error.
+ */
+int tinywav_open_write(TinyWav *tw,
+ int16_t numChannels, int32_t samplerate,
+ TinyWavSampleFormat sampFmt, TinyWavChannelFormat chanFmt,
+ const char *path);
+
+/**
+ * Open a file for reading.
+ *
+ * @param path The path of the file to read.
+ * @param chanFmt The channel format (how the channel data is layed out in memory) when read.
+ *
+ * @return The error code. Zero if no error.
+ */
+int tinywav_open_read(TinyWav *tw, const char *path, TinyWavChannelFormat chanFmt);
+
+/**
+ * Read sample data from the file.
+ *
+ * @param tw The TinyWav structure which has already been prepared.
+ * @param data A pointer to the data structure to read to. This data is expected to have the
+ * correct memory layout to match the specifications given in tinywav_open_read().
+ * @param len The number of frames to read.
+ *
+ * @return The number of frames (samples per channel) read from file.
+ */
+int tinywav_read_f(TinyWav *tw, void *data, int len);
+
+/** Stop reading the file. The Tinywav struct is now invalid. */
+void tinywav_close_read(TinyWav *tw);
+
+/**
+ * Write sample data to file.
+ *
+ * @param tw The TinyWav structure which has already been prepared.
+ * @param f A pointer to the sample data to write.
+ * @param len The number of frames to write.
+ *
+ * @return The number of frames (samples per channel) written to file.
+ */
+int tinywav_write_f(TinyWav *tw, void *f, int len);
+
+/** Stop writing to the file. The Tinywav struct is now invalid. */
+void tinywav_close_write(TinyWav *tw);
+
+/** Returns true if the Tinywav struct is available to write or write. False otherwise. */
+bool tinywav_isOpen(TinyWav *tw);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _TINY_WAV_
diff --git a/src/archive/wavetable.c b/src/archive/wavetable.c
new file mode 100644
index 0000000..ab3308a
--- /dev/null
+++ b/src/archive/wavetable.c
@@ -0,0 +1,182 @@
+#include "wavetable.h"
+#include <sndfile.h>
+
+static wave_t wvt_sine_data;
+static wave_t wvt_saw_data;
+static wave_t wvt_digisaw_data;
+static wave_t wvt_tri_data;
+static wave_t wvt_sqr_data;
+static wave_t wvt_sound_data;
+
+
+void
+wvt_sine_init(size_t size)
+{
+ wvt_sine_data.size = size;
+ wvt_sine_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ wvt_sine_data.data[i] = sinf(2 * M_PI * i / size);
+ }
+}
+
+void
+wvt_saw_init(size_t size)
+{
+ wvt_saw_data.size = size;
+ wvt_saw_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ float dOutput = 0.0;
+ for (float n = 1.0; n < 25; n++)
+ dOutput += sinf(n * 2.0 * M_PI * i / size) / n;
+ wvt_saw_data.data[i] = 0.6 * dOutput;
+ }
+}
+
+void
+wvt_digisaw_init(size_t size)
+{
+ wvt_digisaw_data.size = size;
+ wvt_digisaw_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ wvt_digisaw_data.data[i] = (float)i / size;
+ }
+}
+
+void
+wvt_sqr_init(size_t size)
+{
+ wvt_sqr_data.size = size;
+ wvt_sqr_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ if (i < size / 2) {
+ wvt_sqr_data.data[i] = -1.0f;
+ } else {
+ wvt_sqr_data.data[i] = 1.0f;
+ }
+ }
+}
+
+void
+wvt_sound_init(char * path)
+{
+ SNDFILE* file;
+ SF_INFO fileInfo;
+ int numSamplesRead;
+
+ // Open the WAV file
+ file = sf_open(path, SFM_READ, &fileInfo);
+ if (!file) {
+ printf("Failed to open the WAV file: %s\n", sf_strerror(NULL));
+ return;
+ }
+ printf("frames: %ld sr: %d chan: %d format: %d sections: %d seekable: %d\n", fileInfo.frames, fileInfo.samplerate, fileInfo.channels, fileInfo.format, fileInfo.sections, fileInfo.seekable);
+ // Ensure the WAV file has only one channel
+ if (fileInfo.channels != 1) {
+ printf("Only mono WAV files are supported.\n");
+ sf_close(file);
+ return;
+ }
+
+ wvt_sound_data.size = fileInfo.frames ;
+ wvt_sound_data.data = (float *) malloc(sizeof(float) * fileInfo.frames);
+
+ // Read the WAV file into the buffer
+ numSamplesRead = sf_readf_float(file, wvt_sound_data.data, fileInfo.frames);
+
+ float max = -1000;
+ float min = 1000;
+ for (int i = 0; i < numSamplesRead; i++) {
+ float s = wvt_sound_data.data[i];
+ // printf("Sample %d: %f\n", i, s);
+ if (s < min) min = s;
+ if (s > max) max = s;
+ }
+ printf("Min: %f Max: %f\n", min, max);
+
+ // Close the WAV file
+ sf_close(file);
+}
+
+void
+wvt_init()
+{
+ wvt_sine_init(44100);
+ wvt_saw_init(128);
+ wvt_digisaw_init(48000);
+ wvt_sqr_init(48000);
+ wvt_tri_data.size = 2;
+ wvt_tri_data.data = (float *) malloc(sizeof(float) * 2);
+ wvt_tri_data.data[0] = -1.0f;
+ wvt_tri_data.data[1] = 1.0f;
+ wvt_sound_init("/home/gramanas/code/synth-project/waves/test1.wav");
+}
+
+
+float
+wvt_gen(wave_t * wave, float * index, float freq, int sample_rate)
+{
+ if (*index >= wave->size) *index = 0;
+
+ int current_index = (int)*index;
+ int next_index = current_index + 1 >= wave->size ? 0 : current_index + 1;
+
+ // linear interpolation between current_index and next_index
+ float m = (wave->data[next_index] - wave->data[current_index]) / (1 - 0);
+ float b = wave->data[current_index] /* + m * 0 */;
+
+ // the value to pick
+ float x = *index - current_index;
+
+ float sample = m * x + b;
+ float step = freq * ((float)wave->size / sample_rate);
+
+ *index += step;
+ if (*index >= wave->size) {
+ *index -= wave->size;
+ }
+
+ return sample;
+}
+
+float
+wvt_next(float (* wvt_fun)(float *, float, int), float freq, int sample_rate, float * wvt_index)
+{
+ float sample = wvt_fun(wvt_index, freq, sample_rate);
+ return sample;
+}
+
+float
+wvt_saw(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_saw_data, index, freq, sample_rate);
+}
+
+float
+wvt_digisaw(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_digisaw_data, index, freq, sample_rate);
+}
+
+float
+wvt_sine(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_sine_data, index, freq, sample_rate);
+}
+
+float
+wvt_tri(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_tri_data, index, freq, sample_rate);
+}
+
+float
+wvt_sqr(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_sqr_data, index, freq, sample_rate);
+}
+
+float
+wvt_sound(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_sound_data, index, freq, sample_rate);
+}
diff --git a/src/archive/wavetable.h b/src/archive/wavetable.h
new file mode 100644
index 0000000..46174b4
--- /dev/null
+++ b/src/archive/wavetable.h
@@ -0,0 +1,24 @@
+#ifndef WAVETABLE_H
+#define WAVETABLE_H
+
+#include "synth_engine.h"
+
+#define WVT_MAX 44100
+
+typedef struct wave_t {
+ float * data;
+ size_t size;
+} wave_t;
+
+float wvt_sine(float *, float, int);
+float wvt_saw(float *, float, int);
+float wvt_digisaw(float *, float, int);
+float wvt_tri(float *, float, int);
+float wvt_sqr(float *, float, int);
+float wvt_sound(float *, float, int);
+
+float wvt_next(float (* wvt_fun)(float *, float, int), float freq, int sample_rate, float * wvt_index);
+
+void wvt_init();
+
+#endif /* WAVETABLE_H */