📄 offsetcalculator.java
字号:
package abchr.audio;
import abchr.crypto.Util;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.IOException;
public class OffsetCalculator {
private static final int SAMPLE_POINTS=400;
private static final int MAX_OFFSET=400;
private static final int WINDOW_SIZE=2000;
private static final int BUFFER_LENGTH=MAX_OFFSET+WINDOW_SIZE+10;
public static char[] resample(char[] buffer,int length) {
if(buffer.length==length){return buffer;}
char[] result=new char[length];
if(length<buffer.length) {
for(int i=0;i<length;i++) {
result[i]=buffer[(int)(((long)i)*buffer.length/length)];
}
}
return result;
}
private static int calcOffset(char[] a,char[] b,int length) {
int maxPossibleOffset=(int)(length*(MAX_OFFSET/(double)BUFFER_LENGTH));
int window=(int)(length*(WINDOW_SIZE/(double)BUFFER_LENGTH));
long max=0;
int maxOffset=0;
long product;
int skip=window/SAMPLE_POINTS;
for(int offset=0;offset<maxPossibleOffset;offset++) {
product=0;
for(int j=0,k=offset;j<window;j+=skip,k+=skip) {
product+=(a[j]-32768)*(b[k]-32768);
}
if(product>max) {
maxOffset=offset;
max=product;
}
product=0;
for(int j=0,k=offset;j<window;j+=skip,k+=skip) {
product+=(a[k]-32768)*(b[j]-32768);
}
if(product>max) {
maxOffset=-offset;
max=product;
}
}
return maxOffset;
}
public static float calcVolumeDifference(char[] a,char[] b,int length,int offset) {
int window=length-Math.abs(offset)-10;
long ssqa=0,ssqb=0;
if(offset>0) {
for(int i=0;i<window;i++) {
ssqa+=(a[i]-32768)*(a[i]-32768);
}
for(int i=offset;i<offset+window;i++) {
ssqb+=(b[i]-32768)*(b[i]-32768);
}
} else {
for(int i=-offset;i<window-offset;i++) {
ssqa+=(a[i]-32768)*(a[i]-32768);
}
for(int i=0;i<window;i++) {
ssqb+=(b[i]-32768)*(b[i]-32768);
}
}
double scale=Math.sqrt(ssqa/(double)ssqb);
//System.out.println("scale="+scale);
float gain=(float)(20*Math.log(scale)/Math.log(10));
//System.out.println("gain="+gain);
return gain;
}
private static char[] getSamples(Sample sample) throws IOException,UnsupportedAudioFileException {
AudioInputStream stream=sample.getStream();
AudioFormat format=stream.getFormat();
if(format.getSampleSizeInBits()!=16) {
throw new UnsupportedAudioFileException("Only 16bit files are supported for offset correction.");
}
stream.skip(3*((int)format.getFrameRate())*format.getFrameSize());
byte[] temp=new byte[(int)(((int)format.getFrameRate())*format.getFrameSize()*(BUFFER_LENGTH)/1000.0d)];
stream.read(temp);
stream.close();
char[] buffer=Util.bytesToChars(temp,format.isBigEndian());
if(format.getChannels()!=1 && format.getChannels()!=2) {
throw new UnsupportedAudioFileException("Number of channels invalid.");
}
if(format.getChannels()==2) {
for(int i=0;i<buffer.length/2;i++) {
buffer[i]=(char)((buffer[2*i]+buffer[2*i+1])/2);
}
char[] newBuffer=new char[buffer.length/2];
System.arraycopy(buffer,0,newBuffer,0,buffer.length/2);
buffer=newBuffer;
}
return buffer;
}
public static void configureOffsets(Sample[] files) throws IOException {
final int n=files.length;
char[][] samples=new char[n][];
for(int i=0;i<n;i++) {
try {
samples[i]=getSamples(files[i]);
} catch(Exception e) {
throw new IOException("Could not open file "+files[i]);
}
}
int minLength=equalizeSampleRate(samples);
int[] results=new int[n];
int minOffset=0;
for(int i=1;i<n;i++) {
results[i]=(int)Math.round((calcOffset(samples[0],samples[i],minLength)*BUFFER_LENGTH)/(double)minLength);
//System.out.println("result["+i+"]="+results[i]);
if(results[i]<minOffset){minOffset=results[i];}
}
if(minOffset<0) {
for(int i=0;i<n;i++) {
results[i]=results[i]-minOffset;
//System.out.println("results["+i+"]="+results[i]);
}
}
for(int i=0;i<n;i++) {
samples[i]=null;
SampleOffset.setOffset(files[i],results[i]);
}
samples=null;
}
private static int equalizeSampleRate(char[][] samples) {
final int n=samples.length;
int minLength=100000;
for(int i=0;i<n;i++) {
if(samples[i].length<minLength){minLength=samples[i].length;}
}
//System.out.println("minSampleRate="+minSampleRate);
for(int i=0;i<n;i++) {
samples[i]=resample(samples[i],minLength);
}
return minLength;
}
public static void configureOffset(Sample a,Sample b) throws IOException,UnsupportedAudioFileException {
char[] aSamples=getSamples(a);
char[] bSamples=getSamples(b);
int minLength=aSamples.length<bSamples.length?aSamples.length:bSamples.length;
aSamples=resample(aSamples,minLength);
bSamples=resample(bSamples,minLength);
int offset=calcOffset(aSamples,bSamples,minLength);
offset=(int)Math.round((offset*BUFFER_LENGTH)/(double)minLength);
//System.out.println("offset="+offset);
if(offset>0){SampleOffset.setOffset(b,offset);}else{SampleOffset.setOffset(a,-offset);}
}
public static void configureGain(Sample a,Sample b) throws IOException,UnsupportedAudioFileException {
float gain=0;
gain=calcVolumeDifference(a,b);
if(gain<=0){SampleGain.setGain(b,gain);}else{SampleGain.setGain(a,-gain);}
}
// buffer length will be 2^VOL_CORR_BUFFER_LENGTH
private static final int VOL_CORR_BUFFER_LENGTH=7;
// in seconds
private static final int VOL_CORR_MAX_WINDOW=60;
private static long calcMSMono(char[] buffer,int offset,int length) {
if(length<=32) {
long squareSum=0;
for(int i=offset;i<offset+length;i++) {
squareSum+=(buffer[i]-32768)*(buffer[i]-32768);
}
return squareSum >> 5;
} else {
int newLength=length>>1;
return (calcMSMono(buffer,offset,newLength)+calcMSMono(buffer,offset+newLength,newLength))>>1;
}
}
// maxLength can be used to limit the maximum window size (in seconds) that is
// used for RMS calculation
private static double calcRMS(AudioInputStream stream,int maxLength) throws IOException, UnsupportedAudioFileException {
AudioFormat format=stream.getFormat();
if(format.getSampleSizeInBits()!=16) {
throw new UnsupportedAudioFileException("Only 16bit files are supported for volume correction.");
}
if(format.getChannels()!=1 && format.getChannels()!=2) {
throw new UnsupportedAudioFileException("The number of channels is not supported for volume correction.");
}
// length in frames
long length=stream.getFrameLength();
long limit=(maxLength<=VOL_CORR_MAX_WINDOW?maxLength:VOL_CORR_MAX_WINDOW)*(int)format.getFrameRate();
if(length>limit){length=limit;}
//length=1<<(int)(Math.log(length)/Math.log(2));
//System.out.println("using length: "+length);
byte[] byteBuffer=new byte[1<<(VOL_CORR_BUFFER_LENGTH+1)];
if(format.getChannels()==1) {
int n=(int)(length>>VOL_CORR_BUFFER_LENGTH);
char[] buffer;
long meanSum=0;
int temp;
int read;
for(int i=0;i<n;i++) {
read=stream.read(byteBuffer);
buffer=Util.bytesToChars(byteBuffer,0,read,format.isBigEndian());
for(int j=0,m=buffer.length;j<m;j++) {
temp=buffer[j]-32768;
meanSum+=temp*temp;
}
//meanSum+=calcMSMono(buffer,0,VOL_CORR_BUFFER_LENGTH);
}
meanSum/=n;
return Math.sqrt(meanSum);
} else {
int n=(int)(length>>(VOL_CORR_BUFFER_LENGTH-1));
char[] buffer;
long meanSumLeft=0;
long meanSumRight=0;
int temp;
int read;
for(int i=0;i<n;i++) {
read=stream.read(byteBuffer);
buffer=Util.bytesToChars(byteBuffer,0,read,format.isBigEndian());
for(int j=0,m=buffer.length;j<m;j+=2) {
temp=buffer[j]-32768;
meanSumLeft+=temp*temp;
}
for(int j=1,m=buffer.length;j<m;j+=2) {
temp=buffer[j]-32768;
meanSumRight+=temp*temp;
}
}
meanSumLeft/=n;
meanSumRight/=n;
return Math.sqrt((meanSumLeft+meanSumRight)>>1);
}
}
private static char maxSampleValue(AudioInputStream stream) throws IOException,UnsupportedAudioFileException {
AudioFormat format=stream.getFormat();
if(format.getSampleSizeInBits()!=16) {
throw new UnsupportedAudioFileException("Only 16bit files are supported for volume correction.");
}
if(format.getChannels()!=1 && format.getChannels()!=2) {
throw new UnsupportedAudioFileException("The number of channels is not supported for volume correction.");
}
// length in frames
long length=stream.getFrameLength();
byte[] byteBuffer=new byte[1<<(VOL_CORR_BUFFER_LENGTH+1)];
if(format.getChannels()==1) {
int n=(int)(length>>VOL_CORR_BUFFER_LENGTH);
char[] buffer;
char maxValue=0;
char temp;
int read;
for(int i=0;i<n;i++) {
read=stream.read(byteBuffer);
buffer=Util.bytesToChars(byteBuffer,0,read,format.isBigEndian());
for(int j=0,m=buffer.length;j<m;j++) {
temp=(char)Math.abs(buffer[j]-32768);
if(maxValue<temp){maxValue=temp;}
}
//meanSum+=calcMSMono(buffer,0,VOL_CORR_BUFFER_LENGTH);
}
return maxValue;
} else {
int n=(int)(length>>(VOL_CORR_BUFFER_LENGTH-1));
char[] buffer;
char maxValue=0;
char temp;
int read;
for(int i=0;i<n;i++) {
read=stream.read(byteBuffer);
buffer=Util.bytesToChars(byteBuffer,0,read,format.isBigEndian());
for(int j=0,m=buffer.length;j<m;j+=2) {
temp=(char)Math.abs(buffer[j]-32768);
if(maxValue<temp){maxValue=temp;}
}
for(int j=1,m=buffer.length;j<m;j+=2) {
temp=(char)Math.abs(buffer[j]-32768);
if(maxValue<temp){maxValue=temp;}
}
}
return maxValue;
}
}
public static float calcVolumeDifference(Sample a,Sample b) throws IOException, UnsupportedAudioFileException {
AudioInputStream aStream=a.getStream();
AudioFormat aFormat=a.getFormat();
AudioInputStream bStream=b.getStream();
AudioFormat bFormat=b.getFormat();
int aOffsetFrames=(int)(aFormat.getFrameRate()*SampleOffset.getOffset(a)/1000.0f);
aStream.skip(aOffsetFrames*aFormat.getFrameSize());
int bOffsetFrames=((int)(bFormat.getFrameRate()*SampleOffset.getOffset(b)/1000.0f));
bStream.skip(bOffsetFrames*bFormat.getFrameSize());
int minLength=Math.min((int)((aStream.getFrameLength()-aOffsetFrames)/aFormat.getFrameRate()),(int)((bStream.getFrameLength()-bOffsetFrames)/bFormat.getFrameRate()));
double aRMS=calcRMS(aStream,minLength);
double bRMS=calcRMS(bStream,minLength);
double scale=aRMS/bRMS;
//System.out.println("scale="+scale);
float gain=(float)(20*Math.log(scale)/Math.log(10));
//System.out.println("gain="+gain);
return gain;
}
private OffsetCalculator(){}
public static void configureGains(Sample ref,Sample[] samples) throws IOException,UnsupportedAudioFileException {
Sample[] s=new Sample[samples.length+1];
s[0]=ref;
System.arraycopy(samples,0,s,1,samples.length);
//configureOffsets(s);
AudioInputStream refStream=ref.getStream();
AudioFormat format=ref.getFormat();
int offsetFrames=(int)(format.getFrameRate()*SampleOffset.getOffset(ref)/1000.0f);
refStream.skip(offsetFrames*format.getFrameSize());
int minLength=(int)((refStream.getFrameLength()-offsetFrames)/format.getFrameRate());
AudioInputStream[] sampleStreams=new AudioInputStream[samples.length];
for(int i=0;i<samples.length;i++) {
sampleStreams[i]=samples[i].getStream();
format=samples[i].getFormat();
offsetFrames=(int)(format.getFrameRate()*SampleOffset.getOffset(samples[i])/1000.0f);
sampleStreams[i].skip(offsetFrames*format.getFrameSize());
minLength=Math.min(minLength,(int)((sampleStreams[i].getFrameLength()-offsetFrames)/format.getFrameRate()));
}
double[] maxGain=new double[samples.length+1];
double refRMS=calcRMS(refStream,minLength);
refStream.close();
refStream=ref.getStream();
maxGain[0]=20*Math.log(32000.0d/(int)maxSampleValue(refStream))/Math.log(10);
refStream.close();
double[] sampleRMSs=new double[samples.length];
double minRMS=refRMS;
for(int i=0;i<samples.length;i++) {
sampleRMSs[i]=calcRMS(sampleStreams[i],minLength);
sampleStreams[i].close();
sampleStreams[i]=samples[i].getStream();
maxGain[i+1]=20*Math.log(32000.0d/(int)maxSampleValue(sampleStreams[i]))/Math.log(10);
sampleStreams[i].close();
minRMS=minRMS<sampleRMSs[i]?minRMS:sampleRMSs[i];
}
double[] relGain=new double[samples.length+1];
relGain[0]=20*Math.log(minRMS/refRMS)/Math.log(10);
for(int i=0;i<samples.length;i++) {
relGain[i+1]=20*Math.log(minRMS/sampleRMSs[i])/Math.log(10);
}
/*
System.out.println("maxGain: ");
for(int i=0;i<samples.length+1;i++) {
System.out.print(maxGain[i]+", ");
}
System.out.println();
System.out.println("relGain: ");
for(int i=0;i<samples.length+1;i++) {
System.out.print(relGain[i]+", ");
}
System.out.println();
*/
double minDiff=Double.MAX_VALUE;
int minDiffIndex=-1;
for(int i=0;i<samples.length+1;i++) {
if(maxGain[i]-relGain[i]<minDiff){minDiff=maxGain[i]-relGain[i];minDiffIndex=i;}
}
/*
System.out.println("minDiff = "+minDiff+", index = "+minDiffIndex);
for(int i=0;i<samples.length+1;i++) {
relGain[i]=minDiff+relGain[i];
}
System.out.println("relGain: ");
for(int i=0;i<samples.length+1;i++) {
System.out.print(relGain[i]+", ");
}
System.out.println();
*/
for(int i=0;i<samples.length;i++) {
SampleGain.setGain(samples[i],(float)relGain[i+1]);
}
SampleGain.setGain(ref,(float)relGain[0]);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -