#include "xtc/xtc_sample.h" #include "wave_internal.h" #include #include #include struct xtc_sample {xtc_wave wave;}; static int range(const xtc_sample *s,size_t begin,size_t end){return s && beginwave.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;iwave.samples[i];if(vresult.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; } 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||firstc.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); } 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 && i256)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; } 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;xtc_sample_stats stats;double gain=1; if(!range(s,begin,end)||!out||(operationXTC_SAMPLE_NORMALIZE)||!isfinite(amount))return XTC_ERR_INVALID_ARGUMENT; if(operation>=XTC_SAMPLE_FADE_IN && operation<=XTC_SAMPLE_FADE_OUT_POWER && end-begin<2)return XTC_ERR_INVALID_ARGUMENT; r=xtc_sample_analyze(s,begin,end,&stats);if(r!=XTC_OK)return r; if(operation==XTC_SAMPLE_GAIN){if(amount<0||amount>16)return XTC_ERR_INVALID_ARGUMENT;gain=amount;} if(operation==XTC_SAMPLE_NORMALIZE){double peak=fmax(fabs(stats.minimum),fabs(stats.maximum));if(amount<=0||amount>1)return XTC_ERR_INVALID_ARGUMENT;if(peak<=0)return XTC_ERR_INVALID_STATE;gain=amount/peak;if(gain>16)return XTC_ERR_CAPACITY;} 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;iwave.frames;++i)copy->wave.samples[i]=s->wave.samples[i]; for(i=begin;iwave.samples[i]*gain;break; case XTC_SAMPLE_FADE_IN:case XTC_SAMPLE_FADE_OUT:case XTC_SAMPLE_FADE_IN_POWER:case XTC_SAMPLE_FADE_OUT_POWER:{ double fraction=(double)(i-begin)/(double)(end-begin-1);if(operation==XTC_SAMPLE_FADE_OUT||operation==XTC_SAMPLE_FADE_OUT_POWER)fraction=1-fraction; if(operation==XTC_SAMPLE_FADE_IN_POWER||operation==XTC_SAMPLE_FADE_OUT_POWER)fraction=sin(fraction*1.57079632679489661923); value=s->wave.samples[i]*fraction;break;} case XTC_SAMPLE_REMOVE_DC:value=(double)s->wave.samples[i]-stats.mean;break;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;} 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; } xtc_result xtc_sample_audible_bounds(const xtc_sample *s,size_t begin,size_t end, double threshold,size_t padding,size_t *first,size_t *last) { size_t b=begin,e=end; if(!range(s,begin,end)||!first||!last||first==last||!isfinite(threshold)||threshold<0||threshold>1||padding>XTC_SAMPLE_MAX_FRAMES)return XTC_ERR_INVALID_ARGUMENT; while(bwave.samples[b])<=threshold)++b; if(b==end)return XTC_ERR_NOT_FOUND; while(e>b && fabs(s->wave.samples[e-1])<=threshold)--e; *first=b-begin>padding ? b-padding : begin;*last=end-e>padding ? e+padding : end;return XTC_OK; } xtc_result xtc_sample_zero_crossing(const xtc_sample *s,size_t center,size_t radius,size_t *out) { size_t i,lo,hi,best=SIZE_MAX,distance=SIZE_MAX; if(!s||!out||center>s->wave.frames||radius>65536)return XTC_ERR_INVALID_ARGUMENT; lo=center>radius ? center-radius : 0;hi=center+radius;if(hi>s->wave.frames)hi=s->wave.frames; for(i=lo;i<=hi;++i){int crossing; if(i==0)crossing=s->wave.samples[0]==0; else if(i==s->wave.frames)crossing=s->wave.samples[i-1]==0; else {float a=s->wave.samples[i-1],b=s->wave.samples[i];crossing=b==0||(a<0&&b>0)||(a>0&&b<0);} if(crossing){size_t d=i>center ? i-center : center-i;if(d