# 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 by `wav_read()` in `src/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_buffer` and `channels[0].loop_count` is set atomically. - The state of channel 0 is set to `STATE_LOOPING` and `prev_state` is set to `-1` to 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_LOOPING` and have a non‑zero `loop_count`. - A ring buffer (`RingBuf`) is allocated with capacity `2 × loop_count` samples. - The pointer to the ring buffer is published via `atomic_store_explicit` on `channels[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_count` samples from the ring buffer, sleeping 10 ms between empty reads. - Once all samples are collected, it writes them to `save.wav` using `sf_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 `-lsndfile` to your linker flags (already present in the provided `makefile`). ## Implementation Files - `src/wav.c` – contains `wav_read()` and `wav_write()` based on libsndfile. - `src/looper.c` – contains the load/save command handling in `looper_process_commands()` and the writer thread function. - `src/channel.h` – defines `save_ring` as `_Atomic RingBuf *`. ## Testing - The integration test `test_wav_load` creates a short 440 Hz WAV file, loads it via MIDI, and checks for ≥3 bursts of audio output. - The integration test `test_wav_save` records 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.