summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTharo <tharo10600@gmail.com>2026-03-09 11:32:36 +0000
committerGitHub <noreply@github.com>2026-03-09 08:32:36 -0300
commit02eafced87237be63b32ddc187ea85faf92afe00 (patch)
tree136935ec80ac6a7d747cb0edfbbc9b7799cae09f
parent826bc7588e4ecdaa00bc23d25f8082e6ab011339 (diff)
sampleconv: Improve WAV SMPL chunk handling, properly handle (#1862)
odd chunk sizes * Read WAV SMPL chunks for note info when INST chunks are not available * Populate the SMPL chunk on conversion to wav * Properly handle unaligned (odd) WAV chunk sizes
-rw-r--r--tools/audio/sampleconv/src/container/container.h4
-rw-r--r--tools/audio/sampleconv/src/container/wav.c53
2 files changed, 48 insertions, 9 deletions
diff --git a/tools/audio/sampleconv/src/container/container.h b/tools/audio/sampleconv/src/container/container.h
index 680b64012..dae79344e 100644
--- a/tools/audio/sampleconv/src/container/container.h
+++ b/tools/audio/sampleconv/src/container/container.h
@@ -62,8 +62,8 @@ typedef struct container_data {
uint32_t num_samples; // also apparently called num_frames? but that's wrong
// Instrument details
- int8_t base_note;
- int8_t fine_tune;
+ int8_t base_note; // [0, 127]
+ int8_t fine_tune; // [-50, 50]
int8_t gain;
int8_t key_low;
int8_t key_hi;
diff --git a/tools/audio/sampleconv/src/container/wav.c b/tools/audio/sampleconv/src/container/wav.c
index ad345f76e..60ed73e6a 100644
--- a/tools/audio/sampleconv/src/container/wav.c
+++ b/tools/audio/sampleconv/src/container/wav.c
@@ -221,6 +221,25 @@ wav_read(container_data *out, const char *path, UNUSED bool matching)
smpl.num_sample_loops = le32toh(smpl.num_sample_loops);
smpl.sampler_data = le32toh(smpl.sampler_data);
+ if (!has_inst) {
+ // No INST chunk (yet, if there is one later it will take priority)
+ // Read the base note and fine tuning from the SMPL chunk.
+ // Note the INST and SMPL chunk use different representations for the fine tuning
+ // INST uses cents in [-50, 50]
+ // SMPL uses a fraction [0, 255] with 128 = 50 cents
+ // We use the INST format since AIFF uses that one
+ uint32_t base_note = smpl.unity_note;
+ // First scale down 128 -> 50, round to nearest
+ int32_t fine_tune = (smpl.fine_tune * 50 + 0x40) / 128;
+ // If the result is > 50, make it negative and adjust the base note
+ if (fine_tune > 50) {
+ base_note = (base_note - 1) % 128;
+ fine_tune = fine_tune - 100;
+ }
+ out->base_note = base_note;
+ out->fine_tune = fine_tune;
+ }
+
out->num_loops = smpl.num_sample_loops;
out->loops = NULL;
if (out->num_loops != 0) {
@@ -342,13 +361,14 @@ wav_read(container_data *out, const char *path, UNUSED bool matching)
}
long read_size = ftell(in) - start - 8;
+ uint32_t aligned_chunk_size = (chunk_size + 1) & ~1;
- if (read_size > chunk_size)
+ if (read_size > aligned_chunk_size)
error("overran chunk");
else if (!skipped && read_size < chunk_size)
warning("did not read entire %*s chunk: %lu vs %u", 4, cc4, read_size, chunk_size);
- fseek(in, start + 8 + chunk_size, SEEK_SET);
+ fseek(in, start + 8 + aligned_chunk_size, SEEK_SET);
}
if (!has_fmt)
@@ -361,8 +381,10 @@ wav_read(container_data *out, const char *path, UNUSED bool matching)
}
if (!has_inst) {
- out->base_note = 60; // C4
- out->fine_tune = 0;
+ if (!has_smpl) {
+ out->base_note = 60; // C4
+ out->fine_tune = 0;
+ }
out->key_low = 0;
out->key_hi = 127;
out->vel_low = 0;
@@ -452,13 +474,30 @@ wav_write(container_data *in, const char *path, bool matching)
CHUNK_WRITE(out, &inst);
CHUNK_END(out, chunk_start, htole32);
+ // INST and SMPL use a different representation for the fine tuning.
+ // INST represents it in cents in the range [-50, 50]
+ // SMPL represents it as a fraction in the range [0, 255] where 128 is 50 cents
+ // Our internal fine tuning follows the INST format, which AIFF also happens to agree with.
+ // When writing SMPL we need to convert to the fraction representation, which for
+ // positive fine tuning is simply a rescaling, while for negative fine tuning we need to
+ // adjust the base note to make the fine tuning positive first.
+ uint32_t smpl_unity_note = in->base_note;
+ uint32_t smpl_fine_tune = in->fine_tune;
+ if (in->fine_tune < 0) {
+ // [-50, -1] needs to increment unity_note and use the additive inverse mod 100
+ smpl_unity_note = (smpl_unity_note + 1) % 128;
+ smpl_fine_tune = 100 + smpl_fine_tune;
+ }
+ // Rescale 0..50 to 0..128, round to nearest
+ smpl_fine_tune = (smpl_fine_tune * 128 + 25) / 50;
+
wav_smpl smpl = {
.manufacturer = 0,
.product = 0,
- .sample_period = 0,
- .unity_note = 0,
+ .sample_period = htole32(1000000000 / in->sample_rate),
+ .unity_note = htole32(smpl_unity_note),
._pad = {0, 0, 0},
- .fine_tune = 0,
+ .fine_tune = smpl_fine_tune,
.format = 0,
.offset = 0,
.num_sample_loops = htole32(in->num_loops),