From 1646dfb9159e66c30821a5659e8f0a5eb3d52f05 Mon Sep 17 00:00:00 2001 From: steven-na Date: Wed, 5 Aug 2026 21:23:31 -0700 Subject: Formatting, bug fix, test coverage --- src/common.h | 2 +- src/dft.c | 17 +++++++++++++++-- src/dft.h | 10 +++++++--- src/string.h | 2 +- src/wav.c | 6 ++++-- src/wav.h | 14 +++++++------- 6 files changed, 35 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/common.h b/src/common.h index 1154bdf..f1e74f8 100644 --- a/src/common.h +++ b/src/common.h @@ -13,7 +13,7 @@ #endif // !PI #ifndef F64_EPSILON -#define F64_EPSILON = 1e-9 +#define F64_EPSILON 1e-9 #endif // !F64_EPSILON #define F64_EQ(x, y, eps) (fabs((x) - (y)) <= (eps)) diff --git a/src/dft.c b/src/dft.c index 7f90318..b7c7592 100644 --- a/src/dft.c +++ b/src/dft.c @@ -139,6 +139,7 @@ wav_data_t dft_data_to_wav(smrt_arena_t *arena, dft_data_t dft, u64 sample_rate, f64 p = dft.phases[freq_index]; amp += cos((t * f * 2.0 * PI) + p) * a; } + amp = amp < -1.0 ? -1.0 : amp > 1.0 ? 1.0 : amp; data[s_num] = (u8)((amp+1.0) * (UINT8_MAX/2)); } @@ -148,9 +149,12 @@ wav_data_t dft_data_to_wav(smrt_arena_t *arena, dft_data_t dft, u64 sample_rate, stft_data_t short_time_fourier_transform(smrt_arena_t *arena, u64 window_size, u64 hop_size, f64 *samples, u64 sample_count, u64 sample_rate) { assert(F64_EQ(round(log2(window_size)), log2(window_size), 1e-9) && "STFT input window_size must be a power of 2"); + assert(hop_size != 0 && "STFT input hop_size must be nonzero"); sample_count = ALIGN_UP_POW2(sample_count * sizeof(f64), STFT_SAMPLE_ALIGN_BYTES(window_size)) / sizeof(f64); + assert(sample_count >= window_size && "STFT input sample_count must be at least window_size"); + u64 segment_count = ((sample_count - window_size) / hop_size) + 1; stft_segment_t *segments = SMRTA_ALLOC_ARRAY(arena, stft_segment_t, segment_count); @@ -179,8 +183,11 @@ stft_data_t short_time_fourier_transform(smrt_arena_t *arena, u64 window_size, u return (stft_data_t){ .sample_rate=sample_rate, + .window_size=window_size, + .hop_size=hop_size, .segment_count=segment_count, .segments=segments, + .total_samples=sample_count, }; } @@ -233,10 +240,13 @@ void inverse_fast_fourier_transform(smrt_arena_t *arena, f64 const *real, f64 co smrta_scratch_end(scratch); } -f64 *inverse_short_time_fourier_transform(smrt_arena_t *arena, stft_data_t stft, u64 window_size, u64 hop_size, smrt_arena_t **conflicts, u64 num_conflicts) { +f64 *inverse_short_time_fourier_transform(smrt_arena_t *arena, stft_data_t stft, smrt_arena_t **conflicts, u64 num_conflicts) { + u64 window_size = stft.window_size; + u64 hop_size = stft.hop_size; + smrta_temp_t scratch = smrta_scratch_start(conflicts, num_conflicts); - u64 output_len = (stft.segment_count - 1) * hop_size + window_size; + u64 output_len = stft.total_samples; dft_data_t *frames = SMRTA_ALLOC_ARRAY(scratch.arena, dft_data_t, stft.segment_count); @@ -277,6 +287,9 @@ f64 *inverse_short_time_fourier_transform(smrt_arena_t *arena, stft_data_t stft, } void reconstruct_spectrum(smrt_arena_t *arena, dft_data_t const *data, u64 sample_count, f64 **real_o, f64 **imag_o) { + assert(data->freq_count == (sample_count / 2) + 1 && + "reconstruct_spectrum: data->freq_count must equal (sample_count / 2) + 1"); + f64 *real = *real_o = SMRTA_ALLOC_ARRAY(arena, f64, sample_count); f64 *imag = *imag_o = SMRTA_ALLOC_ARRAY(arena, f64, sample_count); diff --git a/src/dft.h b/src/dft.h index 6b63175..4834bce 100644 --- a/src/dft.h +++ b/src/dft.h @@ -39,7 +39,10 @@ typedef struct { typedef struct { stft_segment_t * segments; u64 segment_count; + u64 window_size; + u64 hop_size; u64 sample_rate; + u64 total_samples; } stft_data_t; /// Byte alignment required for a samples buffer to safely back @@ -59,6 +62,9 @@ stft_data_t short_time_fourier_transform(smrt_arena_t * arena , u64 sample_rate); /// Run inverse FFT on a complex spectrum (real/imag, length sample_count). +/// No 1/N normalization is applied here: real/imag must already be a correctly-scaled +/// full N-point spectrum (e.g. as produced by reconstruct_spectrum), not a raw +/// forward-FFT output, or the result will be off by a factor of N. /// Pass NULL for either out-param to skip allocating/returning that component void inverse_fast_fourier_transform(smrt_arena_t * arena , f64 const * real , @@ -68,11 +74,9 @@ void inverse_fast_fourier_transform(smrt_arena_t * arena , f64 ** imag_o , smrt_arena_t **conflicts, u64 num_conflicts); -/// Reconstruct samples from STFT data via overlap-add inverse FFT synthesis +/// Reconstruct samples from STFT data via overlap-add inverse FFT synthesis. f64 *inverse_short_time_fourier_transform( smrt_arena_t * arena , stft_data_t stft , - u64 window_size , - u64 hop_size , smrt_arena_t **conflicts, u64 num_conflicts); /// Reconstruct a full N-point complex spectrum from one-sided DFT amplitude/phase data diff --git a/src/string.h b/src/string.h index 7e3cf66..ef6d966 100644 --- a/src/string.h +++ b/src/string.h @@ -17,4 +17,4 @@ strng_t * strng_dup(smrt_arena_t *arena, strng_t const *src); b32 strng_set(strng_t *string, char const *c); void strng_clear(strng_t *string); -#define STRNG_FMT(s) (int)s->len, (char *)((u8*)s+STRNG_BASE_POS) +#define STRNG_FMT(s) (i32)s->len, (char *)((u8*)s+STRNG_BASE_POS) diff --git a/src/wav.c b/src/wav.c index ab8919d..e1e6e2c 100644 --- a/src/wav.c +++ b/src/wav.c @@ -254,9 +254,11 @@ f64 *read_32bps_float_data(smrt_arena_t *arena, wav_data_t data, u16 num_channel u8 two = data.samples[(i*4*num_channels)+1+channel*4]; u8 three = data.samples[(i*4*num_channels)+2+channel*4]; u8 four = data.samples[(i*4*num_channels)+3+channel*4]; - u32 intermediate = one | two << 8 | three << 16 | four << 24; + u32 intermediate = one | two << 8 | three << 16 | (u32)four << 24; - memcpy(&vs[i], &intermediate, sizeof(u32)); + f32 f; + memcpy(&f, &intermediate, sizeof(f32)); + vs[i] = (f64)f; } return vs; diff --git a/src/wav.h b/src/wav.h index 00ab37d..cba319e 100644 --- a/src/wav.h +++ b/src/wav.h @@ -44,11 +44,11 @@ typedef struct { /// Load WAV data from wav_file. Data will be allocated on arena, with allocation /// size aligned up to align_up_memoryn. -wav_data_t load_wav_file(smrt_arena_t * arena , - FILE *wav_file , - wav_master_chunk_t *master_o , - wav_fmt_chunk_t *format_o , - u64 align_up_memoryn); +wav_data_t load_wav_file(smrt_arena_t * arena , + FILE * wav_file , + wav_master_chunk_t * master_o , + wav_fmt_chunk_t * format_o , + u64 align_up_memoryn); /// Write WAV headers and data to wav_file. b32 write_wav_file(FILE * wav_file , @@ -61,14 +61,14 @@ wav_fmt_chunk_t make_wav_fmt_chunk(u32 num_channels , u16 bits_per_sample); /// Load WAV data into [-1.0, 1.0] f64 amplitudes. Populates -o inputs with information. -/// samples will be amplitude data for each channel from the WAV file, of length sample_count. +/// samples will be amplitude data for each channel from the WAV file, of length sample_count /// align_up_memoryn will align allocation up to nearest multiple (For FFT) void wav_load(smrt_arena_t *arena, FILE *wav, f64 *** samples_o , u16 *channel_count_o , u64 * sample_count_o , u32 * sample_rate_o , u64 align_up_memoryn , - smrt_arena_t **conflicts, u64 num_conflicts ); + smrt_arena_t **conflicts, u64 num_conflicts); f64 * read_8bps_data(smrt_arena_t *arena, wav_data_t data, u16 num_channels, u16 channel, u64 align_up_memoryn); f64 * read_16bps_data(smrt_arena_t *arena, wav_data_t data, u16 num_channels, u16 channel, u64 align_up_memoryn); -- cgit v1.2.3