feat: normalize selected audio clips to a bounded sample peak

This commit is contained in:
Johannes Findeisen 2026-11-06 00:46:14 +01:00
commit d676d74a11
10 changed files with 94 additions and 6 deletions

View file

@ -1392,6 +1392,10 @@ int main(int argc, char **argv)
size_t notes=0;xtc_project_note_count(s->project(),&notes);CHECK(notes==3);
CHECK(s->arpeggiate(80,50,false)==XTC_OK);xtc_project_note_count(s->project(),&notes);CHECK(notes==3);
CHECK(window.undo()==XTC_OK);xtc_project_note_count(s->project(),&notes);CHECK(notes==3);
char sample[4096];CHECK(realpath("examples/projects/original-pluck.wav",sample)!=nullptr);
CHECK(window.import_audio(sample)==XTC_OK);CHECK(s->normalize_audio(.25f)==XTC_OK);
xtc_clip_info normalized{};xtc_project_find_clip(s->project(),s->selected_clip(),&normalized);CHECK(normalized.gain>0 && normalized.gain<=4);
CHECK(window.undo()==XTC_OK);
window.do_callback();
}
if (failures != 0) return 1;

View file

@ -8,7 +8,15 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
static void put32(unsigned char *p,uint32_t value){p[0]=(unsigned char)value;p[1]=(unsigned char)(value>>8);p[2]=(unsigned char)(value>>16);p[3]=(unsigned char)(value>>24);}
static void test_wave(const char *path,float amplitude)
{
unsigned char h[44]={0},bytes[4];FILE *f=fopen(path,"wb");assert(f);
memcpy(h,"RIFF",4);put32(h+4,52);memcpy(h+8,"WAVEfmt ",8);put32(h+16,16);h[20]=3;h[22]=1;put32(h+24,1000);put32(h+28,4000);h[32]=4;h[34]=32;memcpy(h+36,"data",4);put32(h+40,16);
assert(fwrite(h,1,44,f)==44);for(size_t i=0;i<4;++i){uint32_t bits;float v=i%2 ? -amplitude*.5f : amplitude;memcpy(&bits,&v,4);put32(bytes,bits);assert(fwrite(bytes,1,4,f)==4);}assert(fclose(f)==0);
}
static void markers(void)
{
xtc_project *p=NULL,*q=NULL;xtc_marker m;size_t count;char path[]="/tmp/xtc-sections-XXXXXX";
@ -163,4 +171,24 @@ static void arpeggio(void)
assert(xtc_project_arpeggiate(p,&c,1,1,100,0)==XTC_ERR_CAPACITY);
assert(xtc_project_note_count(p,&count)==XTC_OK && count==1);xtc_project_destroy(p);
}
int main(void){markers();sections();ripple();duplicate_track();split_midi();merge_midi();humanize();chords();arpeggio();puts("Next DAW workflow tests passed.");return 0;}
static void normalize(void)
{
xtc_project *p=NULL;uint64_t t,ids[2];xtc_clip_info c;char dir[]="/tmp/xtc-normal-XXXXXX",path[512];
assert(mkdtemp(dir));snprintf(path,sizeof(path),"%s/source.wav",dir);test_wave(path,.5f);
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Track",&t)==XTC_OK);
assert(xtc_project_import_audio(p,t,path,0,&ids[0])==XTC_OK);
assert(xtc_project_normalize_audio(p,ids,1,.25f)==XTC_OK);
assert(xtc_project_find_clip(p,ids[0],&c)==XTC_OK && c.gain==.5f);
assert(xtc_project_edit_clip(p,ids[0],t,0,2,2)==XTC_OK);
assert(xtc_project_normalize_audio(p,ids,1,.25f)==XTC_OK);
assert(xtc_project_find_clip(p,ids[0],&c)==XTC_OK && c.gain==1);
assert(xtc_project_edit_clip(p,ids[0],t,0,8,0)==XTC_OK && xtc_project_set_clip_audio(p,ids[0],.5f,0,0)==XTC_OK);
assert(xtc_project_add_clip(p,t,"Missing",100,960,&ids[1])==XTC_OK && xtc_project_set_clip_asset(p,ids[1],"/nonexistent/xtc.wav")==XTC_OK);
assert(xtc_project_normalize_audio(p,ids,2,1)!=XTC_OK);
assert(xtc_project_find_clip(p,ids[0],&c)==XTC_OK && c.gain==.5f);
test_wave(path,.001f);assert(xtc_project_normalize_audio(p,ids,1,1)==XTC_ERR_CAPACITY);
test_wave(path,0);assert(xtc_project_normalize_audio(p,ids,1,1)==XTC_ERR_INVALID_STATE);
assert(xtc_project_normalize_audio(p,ids,1,NAN)==XTC_ERR_INVALID_ARGUMENT);
xtc_project_destroy(p);assert(unlink(path)==0 && rmdir(dir)==0);
}
int main(void){markers();sections();ripple();duplicate_track();split_midi();merge_midi();humanize();chords();arpeggio();normalize();puts("Next DAW workflow tests passed.");return 0;}