2026-11-06 01:01:37 +01:00
|
|
|
#include "xtc/xtc_sample.h"
|
|
|
|
|
#include "wave_internal.h"
|
|
|
|
|
#include <stdlib.h>
|
2026-11-06 01:03:31 +01:00
|
|
|
#include <string.h>
|
2026-11-06 01:01:37 +01:00
|
|
|
#include <math.h>
|
|
|
|
|
struct xtc_sample {xtc_wave wave;};
|
|
|
|
|
static int range(const xtc_sample *s,size_t begin,size_t end){return s && begin<end && end<=s->wave.frames;}
|
|
|
|
|
void xtc_sample_destroy(xtc_sample *s){if(s){free(s->wave.samples);free(s);}}
|
|
|
|
|
xtc_result xtc_sample_load(const char *path,xtc_sample **out)
|
|
|
|
|
{
|
|
|
|
|
xtc_sample *s;xtc_result r;if(!path || !out)return XTC_ERR_INVALID_ARGUMENT;
|
|
|
|
|
s=calloc(1,sizeof(*s));if(!s)return XTC_ERR_OUT_OF_MEMORY;
|
|
|
|
|
r=xtc_wave_load(path,&s->wave);
|
|
|
|
|
if(r==XTC_OK && s->wave.frames>XTC_SAMPLE_MAX_FRAMES)r=XTC_ERR_CAPACITY;
|
|
|
|
|
if(r!=XTC_OK){xtc_sample_destroy(s);return r;}*out=s;return XTC_OK;
|
|
|
|
|
}
|
|
|
|
|
xtc_result xtc_sample_info(const xtc_sample *s,unsigned int *rate,size_t *frames)
|
|
|
|
|
{if(!s || !rate || !frames)return XTC_ERR_INVALID_ARGUMENT;*rate=s->wave.rate;*frames=s->wave.frames;return XTC_OK;}
|
|
|
|
|
xtc_result xtc_sample_read(const xtc_sample *s,size_t frame,float *out)
|
|
|
|
|
{if(!s || !out || frame>=s->wave.frames)return XTC_ERR_INVALID_ARGUMENT;*out=s->wave.samples[frame];return XTC_OK;}
|
|
|
|
|
xtc_result xtc_sample_analyze(const xtc_sample *s,size_t begin,size_t end,xtc_sample_stats *out)
|
|
|
|
|
{
|
|
|
|
|
size_t i;double sum=0,square=0;xtc_sample_stats result;
|
|
|
|
|
if(!range(s,begin,end) || !out)return XTC_ERR_INVALID_ARGUMENT;
|
|
|
|
|
result.minimum=result.maximum=s->wave.samples[begin];
|
|
|
|
|
for(i=begin;i<end;++i){float v=s->wave.samples[i];if(v<result.minimum)result.minimum=v;if(v>result.maximum)result.maximum=v;sum+=v;square+=(double)v*v;}
|
|
|
|
|
result.mean=sum/(double)(end-begin);result.rms=sqrt(square/(double)(end-begin));*out=result;return XTC_OK;
|
|
|
|
|
}
|
2026-11-06 01:02:08 +01:00
|
|
|
|
|
|
|
|
xtc_result xtc_project_trim_source_frames(xtc_project *p,uint64_t id,unsigned int rate,size_t begin,size_t end)
|
|
|
|
|
{
|
|
|
|
|
xtc_clip_info c;uint64_t first,last,denom,factor;xtc_result r;
|
|
|
|
|
if(!p||!rate||rate>768000||begin>=end||end>XTC_SAMPLE_MAX_FRAMES)return XTC_ERR_INVALID_ARGUMENT;
|
|
|
|
|
r=xtc_project_find_clip(p,id,&c);if(r!=XTC_OK)return r;
|
|
|
|
|
if(c.midi||!c.asset_path[0])return XTC_ERR_INVALID_STATE;
|
|
|
|
|
factor=(uint64_t)xtc_project_tempo(p)*XTC_TICKS_PER_QUARTER;denom=(uint64_t)rate*60000U;
|
|
|
|
|
first=((uint64_t)begin*factor+denom-1)/denom;last=(uint64_t)end*factor/denom;
|
|
|
|
|
if(first>=last||first<c.offset_tick||last>c.offset_tick+c.length_tick)return XTC_ERR_INVALID_ARGUMENT;
|
|
|
|
|
return xtc_project_edit_clip(p,id,c.track_id,c.start_tick+first-c.offset_tick,last-first,first);
|
|
|
|
|
}
|
2026-11-06 01:03:31 +01:00
|
|
|
|
|
|
|
|
xtc_result xtc_sample_write(const xtc_sample *s,size_t begin,size_t end,const char *path)
|
|
|
|
|
{
|
|
|
|
|
xtc_recorder *rec=NULL;xtc_result r;size_t i;
|
|
|
|
|
if(!range(s,begin,end)||!path||path[0]!='/'||strlen(path)>=512)return XTC_ERR_INVALID_ARGUMENT;
|
|
|
|
|
r=xtc_recorder_create(path,s->wave.rate,256,&rec);
|
|
|
|
|
for(i=begin;r==XTC_OK && i<end;){size_t n=end-i;if(n>256)n=256;xtc_recorder_push(rec,s->wave.samples+i,n);r=xtc_recorder_drain(rec);i+=n;}
|
|
|
|
|
if(r==XTC_OK)r=xtc_recorder_finish(rec);
|
|
|
|
|
if(r!=XTC_OK)xtc_recorder_abort(rec);
|
|
|
|
|
xtc_recorder_destroy(rec);return r;
|
|
|
|
|
}
|
2026-11-06 01:04:06 +01:00
|
|
|
|
|
|
|
|
xtc_result xtc_sample_process(const xtc_sample *s,size_t begin,size_t end,
|
|
|
|
|
xtc_sample_operation operation,double amount,xtc_sample **out)
|
|
|
|
|
{
|
|
|
|
|
xtc_sample *copy;size_t i;xtc_result r=XTC_OK;
|
2026-11-06 01:04:45 +01:00
|
|
|
if(!range(s,begin,end)||!out||(operation<XTC_SAMPLE_REVERSE||operation>XTC_SAMPLE_INVERT)||!isfinite(amount))return XTC_ERR_INVALID_ARGUMENT;
|
2026-11-06 01:04:06 +01:00
|
|
|
copy=calloc(1,sizeof(*copy));if(!copy)return XTC_ERR_OUT_OF_MEMORY;
|
|
|
|
|
copy->wave=s->wave;copy->wave.samples=malloc(s->wave.frames*sizeof(float));
|
|
|
|
|
if(!copy->wave.samples){free(copy);return XTC_ERR_OUT_OF_MEMORY;}
|
|
|
|
|
for(i=0;i<s->wave.frames;++i)copy->wave.samples[i]=s->wave.samples[i];
|
|
|
|
|
for(i=begin;i<end;++i){double value;
|
2026-11-06 01:04:45 +01:00
|
|
|
switch(operation){case XTC_SAMPLE_INVERT:value=-(double)s->wave.samples[i];break;case XTC_SAMPLE_REVERSE:value=s->wave.samples[end-1-(i-begin)];break;default:value=0;r=XTC_ERR_INVALID_ARGUMENT;break;}
|
2026-11-06 01:04:06 +01:00
|
|
|
if(!isfinite(value)||fabs(value)>3.4028234663852886e38){r=XTC_ERR_CAPACITY;break;}
|
|
|
|
|
copy->wave.samples[i]=(float)value;
|
|
|
|
|
}
|
|
|
|
|
if(r!=XTC_OK){xtc_sample_destroy(copy);return r;}*out=copy;return XTC_OK;
|
|
|
|
|
}
|