2.9 KiB
2.9 KiB
Sampling and Recording (WAV Load/Save)
The looper supports loading a WAV file into channel 0 and saving the current loop of channel 0 as a WAV file. Both operations use the libsndfile library, ensuring correct handling of RIFF headers, chunk sizes, and sample format conversion.
Load Command
- MIDI note 70 with the control key (note 64) triggers loading.
- The file
loop.wav(located in the working directory) is read bywav_read()insrc/wav.c. - The function calls
sf_open(path, SFM_READ, &info). - It accepts only mono PCM WAV files. If the file is not mono or has an invalid sample rate, it returns
-1. - The number of frames read is capped at
LOOP_BUF_SIZE(5 seconds at 48 kHz). - The data is stored in
channels[0].loop_bufferandchannels[0].loop_countis set atomically. - The state of channel 0 is set to
STATE_LOOPINGandprev_stateis set to-1to trigger the loop start in the next audio cycle.
Save Command
- MIDI note 71 with the control key (note 64) triggers saving.
- The looper must currently be in
STATE_LOOPINGand have a non‑zeroloop_count. - A ring buffer (
RingBuf) is allocated with capacity2 × loop_countsamples. - The pointer to the ring buffer is published via
atomic_store_explicitonchannels[0].save_ring. - In each audio callback cycle, if the channel is looping and a save ring exists, the audio output data is written into the ring buffer.
- A dedicated writer thread (
writer_thread) is launched (detached) to consume the ring buffer. - The writer thread reads
loop_countsamples from the ring buffer, sleeping 10 ms between empty reads. - Once all samples are collected, it writes them to
save.wavusingsf_writef_float(). - After writing, the ring buffer is destroyed and freed, and the save ring pointer is set to
NULL.
Dependencies
- libsndfile must be installed (development headers). Add
-lsndfileto your linker flags (already present in the providedmakefile).
Implementation Files
src/wav.c– containswav_read()andwav_write()based on libsndfile.src/looper.c– contains the load/save command handling inlooper_process_commands()and the writer thread function.src/channel.h– definessave_ringas_Atomic RingBuf *.
Testing
- The integration test
test_wav_loadcreates a short 440 Hz WAV file, loads it via MIDI, and checks for ≥3 bursts of audio output. - The integration test
test_wav_saverecords a beep, loops it, issues the save command, and verifies the resulting WAV file has non‑zero data size.
Notes
- The save operation is asynchronous: the writer thread runs in the background while the audio callback continues to fill the ring buffer. The test waits 2 s for the file to be written before checking.
- The load operation is synchronous: the callback sleeps 1 s after the MIDI command to give the main loop time to process it.