📄 abx.c
字号:
{ if ( strlen (name) < strlen (ext) ) return 0; name += strlen (name) - strlen (ext); return strcasecmp (name, ext) ? 0 : 1;}typedef struct { const char* const extention; const char* const command;} decoder_t;#define REDIR " 2> /dev/null"#define STDOUT "/dev/fd/1"#define PATH PATH_OF_EXTERNAL_TOOLS_FOR_UNCOMPRESSINGconst decoder_t decoder [] = { { ".mp1" , PATH"mpg123 -w - %s" REDIR }, // MPEG Layer I : www.iis.fhg.de, www.mpeg.org { ".mp2" , PATH"mpg123 -w - %s" REDIR }, // MPEG Layer II : www.iis.fhg.de, www.uq.net.au/~zzmcheng, www.mpeg.org { ".mp3" , PATH"mpg123 -w - %s" REDIR }, // MPEG Layer III : www.iis.fhg.de, www.mp3dev.org/mp3, www.mpeg.org { ".mp3pro" , PATH"mpg123 -w - %s" REDIR }, // MPEG Layer III : www.iis.fhg.de, www.mp3dev.org/mp3, www.mpeg.org { ".mpt" , PATH"mpg123 -w - %s" REDIR }, // MPEG Layer III : www.iis.fhg.de, www.mp3dev.org/mp3, www.mpeg.org { ".mpp" , PATH"mppdec %s -" REDIR }, // MPEGplus : www.stud.uni-hannover.de/user/73884 { ".mpc" , PATH"mppdec %s -" REDIR }, // MPEGplus : www.stud.uni-hannover.de/user/73884 { ".mp+" , PATH"mppdec %s -" REDIR }, // MPEGplus : www.stud.uni-hannover.de/user/73884 { ".aac" , PATH"faad -t.wav -w %s" REDIR }, // Advanced Audio Coding: psytel.hypermart.net, www.aac-tech.com, sourceforge.net/projects/faac, www.aac-audio.com, www.mpeg.org { "aac.lqt" , PATH"faad -t.wav -w %s" REDIR }, // Advanced Audio Coding: psytel.hypermart.net, www.aac-tech.com, sourceforge.net/projects/faac, www.aac-audio.com, www.mpeg.org { ".ac3" , PATH"ac3dec %s" REDIR }, // Dolby AC3 : www.att.com { "ac3.lqt" , PATH"ac3dec %s" REDIR }, // Dolby AC3 : www.att.com { ".ogg" , PATH"ogg123 -d wav -o file:"STDOUT" %s" REDIR }, // Ogg Vorbis : www.xiph.org/ogg/vorbis/index.html { ".pac" , PATH"lpac -x %s "STDOUT REDIR }, // Lossless predictive Audio Compression: www-ft.ee.tu-berlin.de/~liebchen/lpac.html (liebchen@ft.ee.tu-berlin.de) { ".shn" , PATH"shorten -x < %s" REDIR }, // Shorten : shnutils.freeshell.org, www.softsound.com/Shorten.html (shnutils@freeshell.org, shorten@softsound.com) { ".wav.gz" , "gzip -d < %s | sox -twav - -twav -sw -" REDIR }, // gziped WAV { ".wav.sz" , PATH"szip -d < %s | sox -twav - -twav -sw -"REDIR }, // sziped WAV { ".wav.sz2", PATH"szip2 -d < %s | sox -twav - -twav -sw -"REDIR }, // sziped WAV { ".raw" , "sox -r44100 -sw -c2 -traw %s -twav -sw -" REDIR }, // raw files are treated as CD like audio { ".cdr" , "sox -r44100 -sw -c2 -traw %s -twav -sw -" REDIR }, // CD-DA files are treated as CD like audio, no preemphasis info available { ".rm" , "echo %s '???'" REDIR }, // Real Audio : www.real.com { ".epc" , "echo %s '???'" REDIR }, // ePAC : www.audioveda.com, www.lucent.com/ldr { ".mov" , "echo %s '???'" REDIR }, // QDesign Music 2 : www.qdesign.com { ".vqf" , "echo %s '???'" REDIR }, // TwinVQ : www.yamaha-xg.com/english/xg/SoundVQ, www.vqf.com, sound.splab.ecl.ntt.co.jp/twinvq-e { ".wma" , "echo %s '???'" REDIR }, // Microsoft Media Audio: www.windowsmedia.com, www.microsoft.com/windows/windowsmedia { ".flac" , PATH"flac -c -d %s" REDIR }, // Free Lossless Audio Coder: flac.sourceforge.net/ { ".fla" , PATH"flac -c -d %s" REDIR }, // Free Lossless Audio Coder: flac.sourceforge.net/ { ".ape" , "( "PATH"MAC %s _._.wav -d > /dev/null; cat _._.wav; rm _._.wav )" REDIR }, // Monkey's Audio Codec : www.monkeysaudio.com (email@monkeysaudio.com) { ".rka" , "( "PATH"rkau %s _._.wav > /dev/null; cat _._.wav; rm _._.wav )" REDIR }, // RK Audio: { ".rkau" , "( "PATH"rkau %s _._.wav > /dev/null; cat _._.wav; rm _._.wav )" REDIR }, // RK Audio: { ".mod" , "xmp -b16 -c -f44100 --stereo -o- %s | sox -r44100 -sw -c2 -traw - -twav -sw -" REDIR }, // Amiga's Music on Disk: { "" , "sox %s -twav -sw -" REDIR }, // Rest, may be sox can handle it};#undef REDIR#undef STDOUT#undef PATHint readwave ( stereo_t* buff, size_t maxlen, const char* name, size_t* len ){ char* command = malloc (2*strlen(name) + 512); char* name_q = malloc (2*strlen(name) + 128); unsigned short header [22]; FILE* fp; size_t i; size_t j; // The *nice* shell quoting i = j = 0; if ( name[i] == '-' ) name_q[j++] = '.', name_q[j++] = '/'; while (name[i]) { if ( !isalnum (name[i]) && name[i]!='-' && name[i]!='_' && name[i]!='.' ) name_q[j++] = '\\'; name_q[j++] = name[i++]; } name_q[j] = '\0'; fprintf (stderr, "Reading %s", name ); for ( i = 0; i < sizeof(decoder)/sizeof(*decoder); i++ ) if ( has_ext (name, decoder[i].extention) ) { sprintf ( command, decoder[i].command, name_q ); break; } free (name_q); if ( (fp = popen (command, "r")) == NULL ) { fprintf (stderr, "Can't exec:\n%s\n", command ); exit (1); } free (command); fprintf (stderr, " ..." ); fread ( header, sizeof(*header), sizeof(header)/sizeof(*header), fp ); switch ( header[11] ) { case 2: *len = fread ( buff, sizeof(stereo_t), maxlen, fp ); break; case 1: *len = fread ( buff, sizeof(sample_t), maxlen, fp ); for ( i = *len; i-- > 0; ) buff[i][0] = buff[i][1] = ((sample_t*)buff) [i]; break; case 0: fprintf (stderr, "\b\b\b\b, Standard Open Source Bug detected, try murksaround ..." ); *len = fread ( buff, sizeof(stereo_t), maxlen, fp ); header[11] = 2; header[12] = 65534; /* use that of the other channel */ break; default: fprintf (stderr, "Only 1 or 2 channels are supported, not %u\n", header[11] ); pclose (fp); return -1; } pclose ( fp ); fprintf (stderr, "\n" ); return header[12] ? header[12] : 65534;}double cross_analyze ( const stereo_t* p1, const stereo_t *p2, size_t len ){ float P1 [MAX] [2]; float P2 [MAX] [2]; int i; int maxindex; double sum1; double sum2; double max; double y1; double y2; double y3; double yo; double xo; double tmp; double tmp1; double tmp2; int ret = 0; int cnt = 5; // Calculating effective voltage sum1 = sum2 = 0.; for ( i = 0; i < len; i++ ) { sum1 += (double)p1[i][0] * p1[i][0]; sum2 += (double)p2[i][0] * p2[i][0]; } sum1 = sqrt ( sum1/len ); sum2 = sqrt ( sum2/len ); // Searching beginning of signal (not stable for pathological signals) for ( i = 0; i < len; i++ ) if ( abs (p1[i][0]) >= sum1 && abs (p2[i][0]) >= sum2 ) break; p1 += i; p2 += i; len -= i; if ( len <= MAX ) return 0; // Filling arrays for FFT do { sum1 = sum2 = 0.; for ( i = 0; i < MAX; i++ ) {#ifdef USEDIFF tmp1 = p1 [i][0] - p1 [i+1][0]; tmp2 = p2 [i+ret][0] - p2 [i+ret+1][0];#else tmp1 = p1 [i][0]; tmp2 = p2 [i+ret][0];#endif sum1 += tmp1*tmp1; sum2 += tmp2*tmp2; P1 [i][0] = tmp1; P2 [i][0] = tmp2; P1 [i][1] = 0.; P2 [i][1] = 0.; } fft (P1, MAX); fft (P2, MAX); for ( i = 0; i < MAX; i++ ) { double a0 = P1 [i][0]; double a1 = P1 [i][1]; double b0 = P2 [(MAX-i)&(MAX-1)][0]; double b1 = P2 [(MAX-i)&(MAX-1)][1]; P1 [i][0] = a0*b0 - a1*b1; P1 [i][1] = a0*b1 + a1*b0; } fft (P1, MAX); max = P1 [maxindex = 0][0]; for ( i = 1; i < MAX; i++ ) if ( P1[i][0] > max ) max = P1 [maxindex = i][0]; y2 = P1 [ maxindex ][0]; y1 = P1 [(maxindex-1)&(MAX-1)][0] - y2; y3 = P1 [(maxindex+1)&(MAX-1)][0] - y2; xo = 0.5 * (y1-y3) / (y1+y3); yo = 0.5 * ( (y1+y3)*xo + (y3-y1) ) * xo; if (maxindex > MAX/2 ) maxindex -= MAX; ret += maxindex; tmp = 100./MAX/sqrt(sum1*sum2); if (verbose) printf ( "[%5d]%8.4f [%5d]%8.4f [%5d]%8.4f [%10.4f]%8.4f\n", ret- 1, (y1+y2)*tmp, ret , y2 *tmp, ret+ 1, (y3+y2)*tmp, ret+xo, (yo+y2)*tmp ); } while ( maxindex && cnt-- ); return ret + xo;}short to_short ( int x ){ return x == (short)x ? (short)x : (short) ((x >> 31) ^ 0x7FFF);}void DC_cancel ( stereo_t* p, size_t len ){ double sum1 = 0; double sum2 = 0; size_t i; int diff1; int diff2; for (i = 0; i < len; i++ ) { sum1 += p[i][0]; sum2 += p[i][1]; } if ( fabs(sum1) < len && fabs(sum2) < len ) return; diff1 = round ( sum1 / len ); diff2 = round ( sum2 / len ); if (verbose) fprintf (stderr, "Removing DC (left=%d, right=%d)\n", diff1, diff2 ); for (i = 0; i < len; i++ ) { p[i][0] = to_short ( p[i][0] - diff1); p[i][1] = to_short ( p[i][1] - diff2); }}void multiply ( char c, stereo_t* p, size_t len, double fact ){ size_t i; if ( fact == 1. ) return; if (verbose) fprintf (stderr, "Multiplying %c by %7.5f\n", c, fact ); for (i = 0; i < len; i++ ) { p[i][0] = to_short ( p[i][0] * fact ); p[i][1] = to_short ( p[i][1] * fact ); }}int maximum ( stereo_t* p, size_t len ){ int max = 0; size_t i; for (i = 0; i < len; i++ ) { if (abs(p[i][0]) > max) max = abs(p[i][0]); if (abs(p[i][1]) > max) max = abs(p[i][1]); } return max;}void usage ( void ){ fprintf ( stderr, "usage: abx [-v] File_A File_B\n" "\n" "File_A and File_B loaded and played. File_A should be the better/reference\n" "file, File_B the other. You can press the following keys:\n" "\n" " a/A: Listen to File A\n" " b/B: Listen to File B\n" " x/X: Listen to the randomly selected File X, which is A or B\n" " Ctrl-A: You vote for X=A\n" " Ctrl-B: You vote for X=B\n" " m: Alternating playing A and B. Fast switching\n" " M: Alternating playing A and B. Slow switching\n" " d/D/Ctrl-D/Alt-d/Alt-D:\n" " Listen to the difference A-B (+0 dB...+40 dB)\n" " o/p: Chunk select\n" " hjkl: Chunk fine adjust (hj: start, kl: stop)\n" " Space: Chunk deselect\n" " 0...9: Listen to B, but difference A-B is amplified by 0-9 dB\n" " Q: Quit the program\n" "\n" );}int main ( int argc, char** argv ){ stereo_t* _A = calloc ( sizeof(stereo_t), MAX_LEN ); stereo_t* _B = calloc ( sizeof(stereo_t), MAX_LEN ); stereo_t* A = _A; stereo_t* B = _B; size_t len_A; size_t len_B; size_t len; int max_A; int max_B; int max; long freq1; long freq2; int shift; double fshift; double ampl; int ampl_X; korr_t k; if (argc > 1 && 0 == strcmp (argv[1], "-v") ) { verbose = 1; argc--; argv++; } switch ( argc ) { case 0: case 1: case 2: default: usage (); return 1; case 3: usage(); break; } freq1 = readwave ( A, MAX_LEN, argv[1], &len_A ); DC_cancel ( A, len_A ); freq2 = readwave ( B, MAX_LEN, argv[2], &len_B ); DC_cancel ( B, len_B ); if ( freq1 == 65534 && freq2 != 65534 ) freq1 = freq2; else if ( freq2 == 65534 && freq1 != 65534 ) freq2 = freq1; else if ( freq1 == 65534 && freq2 == 65534 ) freq1 = freq2 = 44100; if ( freq1 != freq2 ) { fprintf ( stderr, "Different sample frequencies currently not supported\n"); fprintf ( stderr, "A: %ld, B: %ld\n", freq1, freq2 ); return 2; } len = len_A < len_B ? len_A : len_B; fshift = cross_analyze ( A, B, len ); shift = floor ( fshift + 0.5 ); if ( verbose ) { fprintf ( stderr, "Delay Ch1 is %.4f samples\n", fshift ); fprintf ( stderr, "Delay Ch2 is %.4f samples\n", cross_analyze ( (stereo_t*)(((sample_t*)A)+1), (stereo_t*)(((sample_t*)B)+1), len ) ); } if (shift > 0) { if (verbose) fprintf ( stderr, "Delaying A by %d samples\n", +shift); B += shift; len_B -= shift; } if (shift < 0) { if (verbose) fprintf ( stderr, "Delaying B by %d samples\n", -shift); A -= shift; len_A += shift; } len = len_A < len_B ? len_A : len_B; memset ( &k, 0, sizeof(k) ); analyze_stereo ( A, B, len, &k ); ampl = report (&k); max_A = maximum ( A, len ); max_B = maximum ( B, len ); if ( ampl <= 0.98855 ) { /* < -0.05 dB */ max = max_A*ampl < max_B ? max_B : max_A*ampl; ampl_X = (int)(29203 / max); if ( ampl_X < 2 ) ampl_X = 1; multiply ( 'A', A, len, ampl*ampl_X ); multiply ( 'B', B, len, ampl_X ); } else if ( ampl >= 1.01158 ) { /* > +0.05 dB */ max = max_A < max_B/ampl ? max_B/ampl : max_A; ampl_X = (int)(29203 / max); if ( ampl_X < 2 ) ampl_X = 1; multiply ( 'A', A, len, ampl_X ); multiply ( 'B', B, len, 1./ampl*ampl_X ); } else { max = max_A < max_B ? max_B : max_A; ampl_X = (int)(29203 / max); if ( ampl_X < 2 ) ampl_X = 1; multiply ( 'A', A, len, ampl_X ); multiply ( 'B', B, len, ampl_X ); } set (); Set_Realtime (); testing ( A, B, len, freq1 ); reset (); free (_A); free (_B); return 0;}/* end of abx.c */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -