我找到了一种添加样本的新方法,使样本永远不会超过给定范围。基本思想是将-1到1范围内的值转换为大约-Infinity到+Infinity之间的范围,将所有内容加在一起并反转初始转换。为此,我想出了以下公式:
我试过了,它确实有效,但是对于多个响亮的声音,生成的音频听起来比仅仅将样本加在一起并剪裁每个太大的值更糟糕。我使用以下代码对此进行了测试:
#include <math.h>
#include <stdio.h>
#include <float.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <sndfile.h>
// fabs wasn't accurate enough
long double ldabs(long double x){
return x < 0 ? -x : x;
}
// -Inf<input<+Inf, -1<=output<=+1
long double infiniteToFinite( long double sample ){
// if the input value was too big, we'll just map it to -1 or 1
if( isinf(sample) )
return sample < 0 ? -1. : 1.;
long double ret = sample / ( ldabs(sample) + 1 );
// Just in case of calculation errors
if( isnan(ret) )
ret = sample < 0 ? -1. : 1.;
if( ret < -1. )
ret = -1.;
if( ret > 1. )
ret = 1.;
return ret;
}
// -1<=input<=+1, -Inf<output<+Inf
long double finiteToInfinite( long double sample ){
// if out of range, clamp to 1 or -1
if( sample > 1. )
sample = 1.;
if( sample < -1. )
sample = -1.;
long double res = -( sample / ( ldabs(sample) - 1. ) );
// sample was too close to 1 or -1, return largest long double
if( isinf(res) )
return sample < 0 ? -LDBL_MAX : LDBL_MAX;
return res;
}
// -1<input<1, -1<=output<=1 | Try to avoid input values too close to 1 or -1
long double addSamples( size_t count, long double sample[] ){
long double sum = 0;
while( count-- ){
sum += finiteToInfinite( sample[count] );
if( isinf(sum) )
sum = sum < 0 ? -LDBL_MAX : LDBL_MAX;
}
return infiniteToFinite( sum );
}
#define BUFFER_LEN 256
int main( int argc, char* argv[] ){
if( argc < 3 ){
fprintf(stderr,"Usage: %s output.wav input1.wav [input2.wav...]\n",*argv);
return 1;
}
{
SNDFILE *outfile, *infiles[argc-2];
SF_INFO sfinfo;
SF_INFO sfinfo_tmp;
memset( &sfinfo, 0, sizeof(sfinfo) );
for( int i=0; i<argc-2; i++ ){
memset( &sfinfo_tmp, 0, sizeof(sfinfo_tmp) );
if(!( infiles[i] = sf_open( argv[i+2], SFM_READ, &sfinfo_tmp ) )){
fprintf(stderr,"Could not open file: %s\n",argv[i+2]);
puts(sf_strerror(0));
goto cleanup;
}
printf("Sample rate %d, channel count %d\n",sfinfo_tmp.samplerate,sfinfo_tmp.channels);
if( i ){
if( sfinfo_tmp.samplerate != sfinfo.samplerate
|| sfinfo_tmp.channels != sfinfo.channels
){
fprintf(stderr,"Mismatching sample rate or channel count\n");
goto cleanup;
}
}else{
sfinfo = sfinfo_tmp;
}
continue;
cleanup: {
while(i--)
sf_close(infiles[i]);
return 2;
}
}
if(!( outfile = sf_open(argv[1], SFM_WRITE, &sfinfo) )){
fprintf(stderr,"Could not open file: %s\n",argv[1]);
puts(sf_strerror(0));
for( int i=0; i<argc-2; i++ )
sf_close(infiles[i]);
return 3;
}
double inbuffer[argc-2][BUFFER_LEN];
double outbuffer[BUFFER_LEN];
size_t max_read;
do {
max_read = 0;
memset(outbuffer,0,BUFFER_LEN*sizeof(double));
for( int i=0; i<argc-2; i++ ){
memset( inbuffer[i], 0, BUFFER_LEN*sizeof(double) );
size_t read_count = sf_read_double( infiles[i], inbuffer[i], BUFFER_LEN );
if( read_count > max_read )
max_read = read_count;
}
long double insamples[argc-2];
for( size_t j=0; j<max_read; j++ ){
for( int i=0; i<argc-2; i++ )
insamples[i] = inbuffer[i][j];
outbuffer[j] = addSamples( argc-2, insamples );
}
sf_write_double( outfile, outbuffer, max_read );
} while( max_read );
sf_close(outfile);
for( int i=0; i<argc-2; i++ )
sf_close(infiles[i]);
}
return 0;
}