diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordConfig.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordConfig.java new file mode 100644 index 0000000000..30661b4b4e --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordConfig.java @@ -0,0 +1,179 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record; + +import android.media.AudioFormat; + +import java.io.Serializable; +import java.util.Locale; + + +public class RecordConfig implements Serializable { + /** + * 录音格式 默认WAV格式 + */ + private RecordFormat format = RecordFormat.WAV; + /** + * 通道数:默认双通道 + */ + private int channelConfig = AudioFormat.CHANNEL_IN_STEREO; + + /** + * 位宽 + */ + private int encodingConfig = AudioFormat.ENCODING_PCM_16BIT; + + /** + * 采样率 + */ + private int sampleRate = 16000; + + + + public RecordConfig() { + } + + public RecordConfig(RecordFormat format) { + this.format = format; + } + + /** + * @param format 录音文件的格式 + * @param channelConfig 声道配置 + * 单声道:See {@link AudioFormat#CHANNEL_IN_MONO} + * 双声道:See {@link AudioFormat#CHANNEL_IN_STEREO} + * @param encodingConfig 位宽配置 + * 8Bit: See {@link AudioFormat#ENCODING_PCM_8BIT} + * 16Bit: See {@link AudioFormat#ENCODING_PCM_16BIT}, + * @param sampleRate 采样率 hz: 8000/16000/44100 + */ + public RecordConfig(RecordFormat format, int channelConfig, int encodingConfig, int sampleRate) { + this.format = format; + this.channelConfig = channelConfig; + this.encodingConfig = encodingConfig; + this.sampleRate = sampleRate; + } + + + + + + /** + * 获取当前录音的采样位宽 单位bit + * + * @return 采样位宽 0: error + */ + public int getEncoding() { + if (format == RecordFormat.MP3) {//mp3后期转换 + return 16; + } + + if (encodingConfig == AudioFormat.ENCODING_PCM_8BIT) { + return 8; + } else if (encodingConfig == AudioFormat.ENCODING_PCM_16BIT) { + return 16; + } else { + return 0; + } + } + + /** + * 获取当前录音的采样位宽 单位bit + * + * @return 采样位宽 0: error + */ + public int getRealEncoding() { + if (encodingConfig == AudioFormat.ENCODING_PCM_8BIT) { + return 8; + } else if (encodingConfig == AudioFormat.ENCODING_PCM_16BIT) { + return 16; + } else { + return 0; + } + } + + /** + * 当前的声道数 + * + * @return 声道数: 0:error + */ + public int getChannelCount() { + if (channelConfig == AudioFormat.CHANNEL_IN_MONO) { + return 1; + } else if (channelConfig == AudioFormat.CHANNEL_IN_STEREO) { + return 2; + } else { + return 0; + } + } + + //get&set + + public RecordFormat getFormat() { + return format; + } + + public RecordConfig setFormat(RecordFormat format) { + this.format = format; + return this; + } + + public int getChannelConfig() { + return channelConfig; + } + + public RecordConfig setChannelConfig(int channelConfig) { + this.channelConfig = channelConfig; + return this; + } + + public int getEncodingConfig() { + if (format == RecordFormat.MP3) {//mp3后期转换 + return AudioFormat.ENCODING_PCM_16BIT; + } + return encodingConfig; + } + + public RecordConfig setEncodingConfig(int encodingConfig) { + this.encodingConfig = encodingConfig; + return this; + } + + public int getSampleRate() { + return sampleRate; + } + + public RecordConfig setSampleRate(int sampleRate) { + this.sampleRate = sampleRate; + return this; + } + + + @Override + public String toString() { + return String.format(Locale.getDefault(), "录制格式: %s,采样率:%sHz,位宽:%s bit,声道数:%s", format, sampleRate, getEncoding(), getChannelCount()); + } + + public enum RecordFormat { + /** + * mp3格式 + */ + MP3(".mp3"), + /** + * wav格式 + */ + WAV(".wav"), + /** + * pcm格式 + */ + PCM(".pcm"); + + private String extension; + + public String getExtension() { + return extension; + } + + RecordFormat(String extension) { + this.extension = extension; + } + } +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordHelper.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordHelper.java new file mode 100644 index 0000000000..bea8b5f739 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordHelper.java @@ -0,0 +1,423 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record; + +import android.media.AudioRecord; +import android.media.MediaRecorder; +import android.os.Environment; +import android.util.Log; + +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger; +import com.mogo.eagle.core.utilcode.mogo.logger.Logger; +import com.mogo.eagle.core.utilcode.util.FileUtils; +import com.zhjt.mogo_core_function_devatools.badcase.record.fft.FftFactory; +import com.zhjt.mogo_core_function_devatools.badcase.record.listener.RecordListener; +import com.zhjt.mogo_core_function_devatools.badcase.record.mp3.Mp3EncodeThread; +import com.zhjt.mogo_core_function_devatools.badcase.record.utils.ByteUtils; +import com.zhjt.mogo_core_function_devatools.badcase.record.utils.WavUtils; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + + +public class RecordHelper { + private static final String TAG = RecordHelper.class.getSimpleName(); + private volatile RecordState state = RecordState.IDLE; + private static final int RECORD_AUDIO_BUFFER_TIMES = 1; + /* + * 录音文件存放路径 + */ + public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Mogo" + File.separator + "DataCollection" + File.separator;//程序外部存储跟目录 + private static final String TEMP_PATH = ROOT_PATH + "temp" + File.separator; + private RecordListener listener; + private final RecordConfig currentConfig; + private AudioRecordThread audioRecordThread; + + private File resultFile = null; + private File tmpFile = null; + private List files = new ArrayList<>(); + private Mp3EncodeThread mp3EncodeThread; + + public RecordHelper(RecordConfig config) { + this.currentConfig = config; + } + + + public RecordState getState() { + return state; + } + + + public void registerRecordListener(RecordListener listener) { + this.listener = listener; + } + + public void unregisterRecordListener() { + this.listener = null; + } + + public void start(String fileName) { + + if (state != RecordState.IDLE && state != RecordState.STOP) { + return; + } + String path = getFilePath(fileName); + resultFile = new File(path); + String tempFilePath = getTempFilePath(); + tmpFile = new File(tempFilePath); + audioRecordThread = new AudioRecordThread(); + audioRecordThread.start(); + } + + public void stop() { + if (state == RecordState.IDLE) { + return; + } + + if (state == RecordState.PAUSE) { + makeFile(); + state = RecordState.IDLE; + notifyState(); + stopMp3Encoded(); + } else { + state = RecordState.STOP; + notifyState(); + } + } + + public void pause() { + if (state != RecordState.RECORDING) { + return; + } + state = RecordState.PAUSE; + notifyState(); + } + + public void resume() { + if (state != RecordState.PAUSE) { + return; + } + String tempFilePath = getTempFilePath(); + tmpFile = new File(tempFilePath); + audioRecordThread = new AudioRecordThread(); + audioRecordThread.start(); + } + + private void notifyState() { + if (listener == null) { + return; + } + listener.onStateChange(state); + if (state == RecordState.STOP || state == RecordState.PAUSE) { + listener.onSoundSize(0); + } + } + + private void notifyFinish() { + if (listener != null) { + listener.onStateChange(RecordState.FINISH); + listener.onResult(resultFile); + } + + } + + private void notifyError(final String error) { + if (listener != null) { + listener.onError(error); + } + } + + private FftFactory fftFactory = new FftFactory(FftFactory.Level.Original); + + private void notifyData(final byte[] data) { + if (listener != null) { + listener.onData(data); + byte[] fftData = fftFactory.makeFftData(data); + if (fftData != null) { + listener.onSoundSize(getDb(fftData)); + listener.onFftData(fftData); + } + } + + } + + private int getDb(byte[] data) { + double sum = 0; + double ave; + int length = Math.min(data.length, 128); + int offsetStart = 8; + for (int i = offsetStart; i < length; i++) { + sum += data[i]; + } + ave = (sum / (length - offsetStart)) * 65536 / 128f; + int i = (int) (Math.log10(ave) * 20); + return i < 0 ? 27 : i; + } + + private void initMp3EncoderThread(int bufferSize) { + try { + mp3EncodeThread = new Mp3EncodeThread(resultFile, bufferSize, currentConfig); + mp3EncodeThread.start(); + } catch (Exception e) { +// Log.e(e, TAG, e.getMessage()); + CallerLogger.INSTANCE.d("$M_DEVA$TAG", e.getMessage()); + } + } + + private class AudioRecordThread extends Thread { + private final AudioRecord audioRecord; + private int bufferSize; + + AudioRecordThread() { + bufferSize = AudioRecord.getMinBufferSize(currentConfig.getSampleRate(), + currentConfig.getChannelConfig(), currentConfig.getEncodingConfig()) * RECORD_AUDIO_BUFFER_TIMES; + Logger.d(TAG, "record buffer size = %s", bufferSize); + audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, currentConfig.getSampleRate(), + currentConfig.getChannelConfig(), currentConfig.getEncodingConfig(), bufferSize); + if (currentConfig.getFormat() == RecordConfig.RecordFormat.MP3) { + if (mp3EncodeThread == null) { + initMp3EncoderThread(bufferSize); + } else { + Logger.e(TAG, "mp3EncodeThread != null, 请检查代码"); + } + } + } + + @Override + public void run() { + super.run(); + + switch (currentConfig.getFormat()) { + case MP3: + startMp3Recorder(); + break; + default: + startPcmRecorder(); + break; + } + } + + private void startPcmRecorder() { + state = RecordState.RECORDING; + notifyState(); + Logger.d(TAG, "开始录制 Pcm"); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(tmpFile); + audioRecord.startRecording(); + byte[] byteBuffer = new byte[bufferSize]; + + while (state == RecordState.RECORDING) { + int end = audioRecord.read(byteBuffer, 0, byteBuffer.length); + notifyData(byteBuffer); + fos.write(byteBuffer, 0, end); + fos.flush(); + } + audioRecord.stop(); + files.add(tmpFile); + if (state == RecordState.STOP) { + makeFile(); + } else { + Logger.i(TAG, "暂停!"); + } + } catch (Exception e) { + notifyError("录音失败"); + } finally { + try { + if (fos != null) { + fos.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + if (state != RecordState.PAUSE) { + state = RecordState.IDLE; + notifyState(); + Logger.d(TAG, "录音结束"); + } + } + + private void startMp3Recorder() { + state = RecordState.RECORDING; + notifyState(); + + try { + audioRecord.startRecording(); + short[] byteBuffer = new short[bufferSize]; + + while (state == RecordState.RECORDING) { + int end = audioRecord.read(byteBuffer, 0, byteBuffer.length); + if (mp3EncodeThread != null) { + mp3EncodeThread.addChangeBuffer(new Mp3EncodeThread.ChangeBuffer(byteBuffer, end)); + } + notifyData(ByteUtils.toBytes(byteBuffer)); + } + audioRecord.stop(); + } catch (Exception e) { + notifyError("录音失败"); + } + if (state != RecordState.PAUSE) { + state = RecordState.IDLE; + notifyState(); + stopMp3Encoded(); + } else { + Logger.d(TAG, "暂停"); + } + } + } + + private void stopMp3Encoded() { + if (mp3EncodeThread != null) { + mp3EncodeThread.stopSafe(new Mp3EncodeThread.EncordFinishListener() { + @Override + public void onFinish() { + notifyFinish(); + mp3EncodeThread = null; + } + }); + } else { + Logger.e(TAG, "mp3EncodeThread is null, 代码业务流程有误,请检查!! "); + } + } + + private void makeFile() { + switch (currentConfig.getFormat()) { + case MP3: + return; + case WAV: + mergePcmFile(); + makeWav(); + break; + case PCM: + mergePcmFile(); + break; + default: + break; + } + notifyFinish(); + Logger.i(TAG, "录音完成! path: %s ; 大小:%s", resultFile.getAbsoluteFile(), resultFile.length()); + } + + /** + * 添加Wav头文件 + */ + private void makeWav() { + if (!FileUtils.isFile(resultFile) || resultFile.length() == 0) { + return; + } + byte[] header = WavUtils.generateWavFileHeader((int) resultFile.length(), currentConfig.getSampleRate(), currentConfig.getChannelCount(), currentConfig.getEncoding()); + WavUtils.writeHeader(resultFile, header); + } + + /** + * 合并文件 + */ + private void mergePcmFile() { + boolean mergeSuccess = mergePcmFiles(resultFile, files); + if (!mergeSuccess) { + notifyError("合并失败"); + } + } + + /** + * 合并Pcm文件 + * + * @param recordFile 输出文件 + * @param files 多个文件源 + * @return 是否成功 + */ + private boolean mergePcmFiles(File recordFile, List files) { + if (recordFile == null || files == null || files.size() <= 0) { + return false; + } + + FileOutputStream fos = null; + BufferedOutputStream outputStream = null; + byte[] buffer = new byte[1024]; + try { + fos = new FileOutputStream(recordFile); + outputStream = new BufferedOutputStream(fos); + + for (int i = 0; i < files.size(); i++) { + BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(files.get(i))); + int readCount; + while ((readCount = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, readCount); + } + inputStream.close(); + } + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + return false; + } finally { + try { + if (outputStream != null) { + outputStream.close(); + } + if (fos != null) { + fos.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + for (int i = 0; i < files.size(); i++) { + files.get(i).delete(); + } + files.clear(); + return true; + } + + private String getFilePath(String fileName) { + if (!FileUtils.createOrExistsDir(ROOT_PATH)) { + Logger.w(TAG, "文件夹创建失败:%s", ROOT_PATH); + return null; + } + + String format = currentConfig.getFormat().getExtension(); + String filePath = String.format(Locale.getDefault(), "%s%s%s", ROOT_PATH, fileName, format); + return filePath; + } + + private String getTempFilePath() { + if (!FileUtils.createOrExistsDir(TEMP_PATH)) { + Logger.e(TAG, "文件夹创建失败:%s", TEMP_PATH); + } + String fileName = String.format(Locale.getDefault(), "tmp_%s", FileUtils.getNowString(new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.SIMPLIFIED_CHINESE))); + return String.format(Locale.getDefault(), "%s%s.pcm", TEMP_PATH, fileName); + } + + /** + * 表示当前状态 + */ + public enum RecordState { + /** + * 空闲状态 + */ + IDLE, + /** + * 录音中 + */ + RECORDING, + /** + * 暂停中 + */ + PAUSE, + /** + * 正在停止 + */ + STOP, + /** + * 录音流程结束(转换结束) + */ + FINISH + } + +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordManager.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordManager.java new file mode 100644 index 0000000000..a8a167bb27 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/RecordManager.java @@ -0,0 +1,106 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record; + + +import android.annotation.SuppressLint; + +import com.zhjt.mogo_core_function_devatools.badcase.record.listener.RecordListener; + + +public class RecordManager { + private static final String TAG = RecordManager.class.getSimpleName(); + @SuppressLint("StaticFieldLeak") + private volatile static RecordManager instance; + private final RecordHelper recordHelper; + /** + * 录音配置 + */ + private final RecordConfig currentConfig = new RecordConfig(); + + private RecordManager() { + recordHelper = new RecordHelper(currentConfig); + } + + public static RecordManager getInstance() { + if (instance == null) { + synchronized (RecordManager.class) { + if (instance == null) { + instance = new RecordManager(); + } + } + } + return instance; + } + +// /** +// * @param showLog 是否开启日志 +// */ +// public void setISDebug(boolean showLog) { +// Logger.IsDebug = showLog; +// } + + public void start(String fileName) { + recordHelper.start(fileName); + } + + public void stop() { + recordHelper.stop(); + } + + public void resume() { + recordHelper.resume(); + } + + public void pause() { + recordHelper.pause(); + } + + + public RecordConfig getRecordConfig() { + return currentConfig; + } + + + /** + * 录音数据监听回调 + */ + public void registerRecordListener(RecordListener listener) { + recordHelper.registerRecordListener(listener); + } + public void unregisterRecordListener() { + recordHelper.unregisterRecordListener(); + } + + public boolean changeFormat(RecordConfig.RecordFormat recordFormat) { + if (getState() == RecordHelper.RecordState.IDLE) { + currentConfig.setFormat(recordFormat); + return true; + } + return false; + } + + public boolean changeSampleRate(int sampleRate) { + if (getState() == RecordHelper.RecordState.IDLE) { + currentConfig.setSampleRate(sampleRate); + return true; + } + return false; + } + + public boolean changeEncodingConfig(int encodingConfig) { + if (getState() == RecordHelper.RecordState.IDLE) { + currentConfig.setEncodingConfig(encodingConfig); + return true; + } + return false; + } + + /** + * 获取当前的录音状态 + * + * @return 状态 + */ + public RecordHelper.RecordState getState() { + return recordHelper.getState(); + } + +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/Complex.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/Complex.java new file mode 100644 index 0000000000..251011f8aa --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/Complex.java @@ -0,0 +1,139 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.fft; + +import java.util.Objects; + +/** + * 复数 + * + * @author test + */ +public class Complex { + + /** + * 实数部分 + */ + private final double real; + + /** + * 虚数部分 imaginary + */ + private final double im; + + public Complex(double real, double imag) { + this.real = real; + im = imag; + } + + @Override + public String toString() { + return String.format("hypot: %s, complex: %s+%si", hypot(), real, im); + } + + public double hypot() { + return Math.hypot(real, im); + } + + public double phase() { + return Math.atan2(im, real); + } + + /** + * 复数求和 + */ + public Complex plus(Complex b) { + double real = this.real + b.real; + double imag = this.im + b.im; + return new Complex(real, imag); + } + + // return a new Complex object whose value is (this - b) + public Complex minus(Complex b) { + double real = this.real - b.real; + double imag = this.im - b.im; + return new Complex(real, imag); + } + + // return a new Complex object whose value is (this * b) + public Complex times(Complex b) { + Complex a = this; + double real = a.real * b.real - a.im * b.im; + double imag = a.real * b.im + a.im * b.real; + return new Complex(real, imag); + } + + // return a new object whose value is (this * alpha) + public Complex scale(double alpha) { + return new Complex(alpha * real, alpha * im); + } + + // return a new Complex object whose value is the conjugate of this + public Complex conjugate() { + return new Complex(real, -im); + } + + // return a new Complex object whose value is the reciprocal of this + public Complex reciprocal() { + double scale = real * real + im * im; + return new Complex(real / scale, -im / scale); + } + + // return the real or imaginary part + public double re() { + return real; + } + + public double im() { + return im; + } + + // return a / b + public Complex divides(Complex b) { + Complex a = this; + return a.times(b.reciprocal()); + } + + // return a new Complex object whose value is the complex exponential of this + public Complex exp() { + return new Complex(Math.exp(real) * Math.cos(im), Math.exp(real) * Math.sin(im)); + } + + // return a new Complex object whose value is the complex sine of this + public Complex sin() { + return new Complex(Math.sin(real) * Math.cosh(im), Math.cos(real) * Math.sinh(im)); + } + + // return a new Complex object whose value is the complex cosine of this + public Complex cos() { + return new Complex(Math.cos(real) * Math.cosh(im), -Math.sin(real) * Math.sinh(im)); + } + + // return a new Complex object whose value is the complex tangent of this + public Complex tan() { + return sin().divides(cos()); + } + + + // a static version of plus + public static Complex plus(Complex a, Complex b) { + double real = a.real + b.real; + double imag = a.im + b.im; + Complex sum = new Complex(real, imag); + return sum; + } + + // See Section 3.3. + @Override + public boolean equals(Object x) { + if (x == null) return false; + if (this.getClass() != x.getClass()) return false; + Complex that = (Complex) x; + return (this.real == that.real) && (this.im == that.im); + } + + // See Section 3.3. + @Override + public int hashCode() { + return Objects.hash(real, im); + } + +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/FFT.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/FFT.java new file mode 100644 index 0000000000..90437212aa --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/FFT.java @@ -0,0 +1,165 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.fft; + + +public class FFT { + + // compute the FFT of x[], assuming its length is a power of 2 + public static Complex[] fft(Complex[] x) { + int n = x.length; + + // base case + if (n == 1) return new Complex[]{x[0]}; + + // radix 2 Cooley-Tukey FFT + if (n % 2 != 0) { + throw new IllegalArgumentException("n is not a power of 2"); + } + + // fft of even terms + Complex[] even = new Complex[n / 2]; + for (int k = 0; k < n / 2; k++) { + even[k] = x[2 * k]; + } + Complex[] q = fft(even); + + // fft of odd terms + for (int k = 0; k < n / 2; k++) { + even[k] = x[2 * k + 1]; + } + Complex[] r = fft(even); + + // combine + Complex[] y = new Complex[n]; + for (int k = 0; k < n / 2; k++) { + double kth = -2 * k * Math.PI / n; + Complex wk = new Complex(Math.cos(kth), Math.sin(kth)); + y[k] = q[k].plus(wk.times(r[k])); + y[k + n / 2] = q[k].minus(wk.times(r[k])); + } + return y; + } + + public static double[] fft(double[] x, int sc) { + int len = x.length; + if (len == 1) { + return x; + } + Complex[] cs = new Complex[len]; + double[] ds = new double[len / 2]; + for (int i = 0; i < len; i++) { + cs[i] = new Complex(x[i], 0); + } + Complex[] ffts = fft(cs); + + for (int i = 0; i < ds.length; i++) { + ds[i] = Math.sqrt(Math.pow(ffts[i].re(), 2) + Math.pow(ffts[i].im(), 2)) / x.length; + } + return ds; + } + + // compute the inverse FFT of x[], assuming its length is a power of 2 + public static Complex[] ifft(Complex[] x) { + int n = x.length; + Complex[] y = new Complex[n]; + + // take conjugate + for (int i = 0; i < n; i++) { + y[i] = x[i].conjugate(); + } + + // compute forward FFT + y = fft(y); + + // take conjugate again + for (int i = 0; i < n; i++) { + y[i] = y[i].conjugate(); + } + + // divide by n + for (int i = 0; i < n; i++) { + y[i] = y[i].scale(1.0 / n); + } + + return y; + + } + + // compute the circular convolution of x and y + public static Complex[] cconvolve(Complex[] x, Complex[] y) { + + // should probably pad x and y with 0s so that they have same length + // and are powers of 2 + if (x.length != y.length) { + throw new IllegalArgumentException("Dimensions don't agree"); + } + + int n = x.length; + + // compute FFT of each sequence + Complex[] a = fft(x); + Complex[] b = fft(y); + + // point-wise multiply + Complex[] c = new Complex[n]; + for (int i = 0; i < n; i++) { + c[i] = a[i].times(b[i]); + } + + // compute inverse FFT + return ifft(c); + } + + + // compute the linear convolution of x and y + public static Complex[] convolve(Complex[] x, Complex[] y) { + Complex ZERO = new Complex(0, 0); + + Complex[] a = new Complex[2 * x.length]; + for (int i = 0; i < x.length; i++) a[i] = x[i]; + for (int i = x.length; i < 2 * x.length; i++) a[i] = ZERO; + + Complex[] b = new Complex[2 * y.length]; + for (int i = 0; i < y.length; i++) b[i] = y[i]; + for (int i = y.length; i < 2 * y.length; i++) b[i] = ZERO; + + return cconvolve(a, b); + } + + // display an array of Complex numbers to standard output + public static void show(Complex[] x, String title) { + System.out.println(title); + System.out.println("-------------------"); + for (int i = 0; i < SIZE; i++) { + System.out.println(x[i]); + } + System.out.println(); + } + + private static final int SIZE = 16384 / 4; + + public static double fun(int x) { + return Math.sin(15f * x);//f= 3.142 + } + + public static double getY(double[] d) { + double y = 0; + int x = 0; + for (int i = 0; i < d.length; i++) { + if (d[i] > y) { + y = d[i]; + x = i; + } + } + x++; + log(String.format("x: %s , y: %s", x, y)); + log(String.format("频率: %sHz", (float) x / SIZE)); + log(String.format("频率2: %sHz", (float) (SIZE - x) / SIZE)); + log(String.format("振幅: %s", y)); + return y; + } + + public static void log(String s) { + System.out.println(s); + } + +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/FftFactory.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/FftFactory.java new file mode 100644 index 0000000000..c8230a9929 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/fft/FftFactory.java @@ -0,0 +1,98 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.fft; + +import com.zhjt.mogo_core_function_devatools.badcase.record.utils.ByteUtils; + +/** + * FFT 数据处理工厂 + */ +public class FftFactory { + private static final String TAG = FftFactory.class.getSimpleName(); + private Level level = Level.Original; + + public FftFactory(Level level) { +// this.level = level; + } + + public byte[] makeFftData(byte[] pcmData) { +// Logger.d(TAG, "pcmData length: %s", pcmData.length); + if (pcmData.length < 1024) { + return null; + } + + double[] doubles = ByteUtils.toHardDouble(ByteUtils.toShorts(pcmData)); + double[] fft = FFT.fft(doubles, 0); + + switch (level) { + case Original: + return ByteUtils.toSoftBytes(fft); + case Maximal: +// return doFftMaximal(fft); + default: + return ByteUtils.toHardBytes(fft); + } + } + + + private byte[] doFftMaximal(double[] fft) { + byte[] bytes = ByteUtils.toSoftBytes(fft); + byte[] result = new byte[bytes.length]; + + for (int i = 0; i < bytes.length; i++) { + + if (isSimpleData(bytes, i)) { + result[i] = bytes[i]; + } else { + result[Math.max(i - 1, 0)] = (byte) (bytes[i] / 2); + result[Math.min(i + 1, result.length - 1)] = (byte) (bytes[i] / 2); + } + } + + return result; + } + + private boolean isSimpleData(byte[] data, int i) { + + int start = Math.max(0, i - 5); + int end = Math.min(data.length, i + 5); + + byte max = 0, min = 127; + for (int j = start; j < end; j++) { + if (data[j] > max) { + max = data[j]; + } + if (data[j] < min) { + min = data[j]; + } + } + + return data[i] == min || data[i] == max; + } + + + /** + * FFT 处理等级 + */ + public enum Level { + + /** + * 原始数据,不做任何优化 + */ + Original, + + /** + * 对音乐进行优化 + */ + Music, + + /** + * 对人声进行优化 + */ + People, + + /** + * 极限优化 + */ + Maximal + } + +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/listener/RecordListener.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/listener/RecordListener.java new file mode 100644 index 0000000000..739556398a --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/listener/RecordListener.java @@ -0,0 +1,50 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.listener; + +import com.zhjt.mogo_core_function_devatools.badcase.record.RecordHelper; + +import java.io.File; + +public interface RecordListener { + /** + * 实时返回音量大小 + * + * @param soundSize 当前音量大小 + */ + void onSoundSize(int soundSize); + + /** + * @param data 录音可视化数据,即傅里叶转换后的数据:fftData + */ + void onFftData(byte[] data); + + /** + * 当前的录音状态发生变化 + * + * @param data 当前音频数据 + */ + void onData(byte[] data); + + /** + * 录音完成回调 + * 录音文件 + * + * @param result 录音文件 + */ + void onResult(File result); + + + /** + * 当前的录音状态发生变化 + * + * @param state 当前状态 + */ + void onStateChange(RecordHelper.RecordState state); + + /** + * 录音错误 + * + * @param error 错误 + */ + void onError(String error); + +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3EncodeThread.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3EncodeThread.java new file mode 100644 index 0000000000..cb003dc341 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3EncodeThread.java @@ -0,0 +1,148 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.mp3; + +import com.zhjt.mogo_core_function_devatools.badcase.record.RecordConfig; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + + +public class Mp3EncodeThread extends Thread { + private static final String TAG = Mp3EncodeThread.class.getSimpleName(); + private List cacheBufferList = Collections.synchronizedList(new LinkedList()); + private File file; + private FileOutputStream os; + private byte[] mp3Buffer; + private EncordFinishListener encordFinishListener; + + /** + * 是否已停止录音 + */ + private volatile boolean isOver = false; + + /** + * 是否继续轮询数据队列 + */ + private volatile boolean start = true; + + public Mp3EncodeThread(File file, int bufferSize, RecordConfig currentConfig) { + this.file = file; + mp3Buffer = new byte[(int) (7200 + (bufferSize * 2 * 1.25))]; + int sampleRate = currentConfig.getSampleRate(); + Mp3Encoder.init(sampleRate, currentConfig.getChannelCount(), sampleRate, currentConfig.getRealEncoding()); + } + + @Override + public void run() { + try { + this.os = new FileOutputStream(file); + } catch (FileNotFoundException e) { + return; + } + + while (start) { + ChangeBuffer next = next(); + lameData(next); + } + } + + public void addChangeBuffer(ChangeBuffer changeBuffer) { + if (changeBuffer != null) { + cacheBufferList.add(changeBuffer); + synchronized (this) { + notify(); + } + } + } + + public void stopSafe(EncordFinishListener encordFinishListener) { + this.encordFinishListener = encordFinishListener; + isOver = true; + synchronized (this) { + notify(); + } + } + + private ChangeBuffer next() { + for (; ; ) { + if (cacheBufferList == null || cacheBufferList.size() == 0) { + try { + if (isOver) { + finish(); + } + synchronized (this) { + wait(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + return cacheBufferList.remove(0); + } + } + } + + private void lameData(ChangeBuffer changeBuffer) { + if (changeBuffer == null) { + return; + } + short[] buffer = changeBuffer.getData(); + int readSize = changeBuffer.getReadSize(); + if (readSize > 0) { + int encodedSize = Mp3Encoder.encode(buffer, buffer, readSize, mp3Buffer); +// if (encodedSize < 0) { +// Logger.e(TAG, "Lame encoded size: " + encodedSize); +// } + try { + os.write(mp3Buffer, 0, encodedSize); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void finish() { + start = false; + final int flushResult = Mp3Encoder.flush(mp3Buffer); + if (flushResult > 0) { + try { + os.write(mp3Buffer, 0, flushResult); + os.close(); + } catch (final IOException e) { + e.printStackTrace(); + } + } + if (encordFinishListener != null) { + encordFinishListener.onFinish(); + } + } + + public static class ChangeBuffer { + private short[] rawData; + private int readSize; + + public ChangeBuffer(short[] rawData, int readSize) { + this.rawData = rawData.clone(); + this.readSize = readSize; + } + + short[] getData() { + return rawData; + } + + int getReadSize() { + return readSize; + } + } + + public interface EncordFinishListener { + /** + * 格式转换完毕 + */ + void onFinish(); + } +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3Encoder.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3Encoder.java new file mode 100644 index 0000000000..899395b4b5 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3Encoder.java @@ -0,0 +1,28 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.mp3; + + +public class Mp3Encoder { + + static { + System.loadLibrary("lamemp3"); + } + + /** + * 获取lame版本号 + * + * @return + */ + public static native String getVersion(); + + public native static void close(); + + public native static int encode(short[] buffer_l, short[] buffer_r, int samples, byte[] mp3buf); + + public native static int flush(byte[] mp3buf); + + public native static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate, int quality); + + public static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate) { + init(inSampleRate, outChannel, outSampleRate, outBitrate, 7); + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3Utils.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3Utils.java new file mode 100644 index 0000000000..5a8f84e0fd --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/mp3/Mp3Utils.java @@ -0,0 +1,45 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.mp3; + +import android.media.MediaExtractor; +import android.media.MediaFormat; +import android.util.Log; + +import com.mogo.eagle.core.utilcode.util.FileUtils; +import com.zhjt.mogo_core_function_devatools.badcase.record.RecordConfig; + +import java.io.IOException; + + +public class Mp3Utils { + private static final String TAG = Mp3Utils.class.getSimpleName(); + + /** + * 获取mp3音频的总时长 单位:ms + * + * @param mp3FilePath MP3文件路径 + * @return 时长 + */ + public static long getDuration(String mp3FilePath) { + if (!FileUtils.isFileExists(mp3FilePath)) { + return 0; + } + if (!mp3FilePath.endsWith(RecordConfig.RecordFormat.MP3.getExtension())) { + return 0; + } + MediaExtractor mex = null; + try { + mex = new MediaExtractor(); + mex.setDataSource(mp3FilePath); + MediaFormat mf = mex.getTrackFormat(0); + long duration = mf.getLong(MediaFormat.KEY_DURATION) / 1000L; + return duration; + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + } finally { + if (mex != null) { + mex.release(); + } + } + return 0; + } +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/utils/ByteUtils.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/utils/ByteUtils.java new file mode 100644 index 0000000000..f23617e3cb --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/utils/ByteUtils.java @@ -0,0 +1,152 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.utils; + +import java.nio.ByteBuffer; +import java.util.Arrays; + + +public class ByteUtils { + public static double[] toHardDouble(short[] shorts) { + int length = 512; + double[] ds = new double[length]; + for (int i = 0; i < length; i++) { + ds[i] = shorts[i]; + } + return ds; + } + + public static byte[] toHardBytes(double[] doubles) { + byte[] bytes = new byte[doubles.length]; + for (int i = 0; i < doubles.length; i++) { + double item = doubles[i]; + bytes[i] = (byte) (item > 127 ? 127 : item); + } + return bytes; + } + + + public static byte[] toSoftBytes(double[] doubles) { + double max = getMax(doubles); + + double sc = 1f; + if (max > 127) { + sc = (max / 128f); + } + + byte[] bytes = new byte[doubles.length]; + for (int i = 0; i < doubles.length; i++) { + double item = doubles[i] / sc; + bytes[i] = (byte) (item > 127 ? 127 : item); + } + return bytes; + } + + + public static double getMax(double[] data) { + double max = 0; + for (double datum : data) { + if (datum > max) { + max = datum; + } + } + return max; + } + + /** + * short[] 转 byte[] + */ + public static byte[] toBytes(short[] src) { + int count = src.length; + byte[] dest = new byte[count << 1]; + for (int i = 0; i < count; i++) { + dest[i * 2] = (byte) (src[i]); + dest[i * 2 + 1] = (byte) (src[i] >> 8); + } + + return dest; + } + + + /** + * short[] 转 byte[] + */ + public static byte[] toBytes(short src) { + byte[] dest = new byte[2]; + dest[0] = (byte) (src); + dest[1] = (byte) (src >> 8); + + return dest; + } + + /** + * int 转 byte[] + */ + public static byte[] toBytes(int i) { + byte[] b = new byte[4]; + b[0] = (byte) (i & 0xff); + b[1] = (byte) ((i >> 8) & 0xff); + b[2] = (byte) ((i >> 16) & 0xff); + b[3] = (byte) ((i >> 24) & 0xff); + return b; + } + + + /** + * String 转 byte[] + */ + public static byte[] toBytes(String str) { + return str.getBytes(); + } + + /** + * long类型转成byte数组 + */ + public static byte[] toBytes(long number) { + ByteBuffer buffer = ByteBuffer.allocate(8); + buffer.putLong(0, number); + return buffer.array(); + } + + public static int toInt(byte[] src, int offset) { + return ((src[offset] & 0xFF) + | ((src[offset + 1] & 0xFF) << 8) + | ((src[offset + 2] & 0xFF) << 16) + | ((src[offset + 3] & 0xFF) << 24)); + } + + public static int toInt(byte[] src) { + return toInt(src, 0); + } + + /** + * 字节数组到long的转换. + */ + public static long toLong(byte[] b) { + ByteBuffer buffer = ByteBuffer.allocate(8); + buffer.put(b, 0, b.length); + return buffer.getLong(); + } + + /** + * byte[] 转 short[] + * short: 2字节 + */ + public static short[] toShorts(byte[] src) { + int count = src.length >> 1; + short[] dest = new short[count]; + for (int i = 0; i < count; i++) { + dest[i] = (short) ((src[i * 2] & 0xff) | ((src[2 * i + 1] & 0xff) << 8)); + } + return dest; + } + + public static byte[] merger(byte[] bt1, byte[] bt2) { + byte[] bt3 = new byte[bt1.length + bt2.length]; + System.arraycopy(bt1, 0, bt3, 0, bt1.length); + System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length); + return bt3; + } + + public static String toString(byte[] b) { + return Arrays.toString(b); + } +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/utils/WavUtils.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/utils/WavUtils.java new file mode 100644 index 0000000000..09e8d3e0a1 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/badcase/record/utils/WavUtils.java @@ -0,0 +1,257 @@ +package com.zhjt.mogo_core_function_devatools.badcase.record.utils; + +import android.util.Log; + +import com.mogo.eagle.core.utilcode.util.FileUtils; +import com.zhjt.mogo_core_function_devatools.badcase.record.RecordConfig; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * @author zhaolewei on 2018/7/3. + * pcm 转 wav 工具类 + * http://soundfile.sapp.org/doc/WaveFormat/ + */ +public class WavUtils { + private static final String TAG = WavUtils.class.getSimpleName(); + + /** + * 生成wav格式的Header + * wave是RIFF文件结构,每一部分为一个chunk,其中有RIFF WAVE chunk, + * FMT Chunk,Fact chunk(可选),Data chunk + * + * @param totalAudioLen 不包括header的音频数据总长度 + * @param sampleRate 采样率,也就是录制时使用的频率 + * @param channels audioRecord的频道数量 + * @param sampleBits 位宽 + */ + public static byte[] generateWavFileHeader(int totalAudioLen, int sampleRate, int channels, int sampleBits) { + WavHeader wavHeader = new WavHeader(totalAudioLen, sampleRate, (short) channels, (short) sampleBits); + return wavHeader.getHeader(); + } + + /** + * 将header写入到pcm文件中 不修改文件名 + * + * @param file 写入的pcm文件 + * @param header wav头数据 + */ + public static void writeHeader(File file, byte[] header) { + if (!FileUtils.isFile(file)) { + return; + } + + RandomAccessFile wavRaf = null; + try { + wavRaf = new RandomAccessFile(file, "rw"); + wavRaf.seek(0); + wavRaf.write(header); + wavRaf.close(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (wavRaf != null) { + wavRaf.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + /** + * Pcm 转 WAV 文件 + * + * @param pcmFile File + * @param header wavHeader + * @throws IOException Exception + */ + public static void pcmToWav(File pcmFile, byte[] header) throws IOException { + if (!FileUtils.isFile(pcmFile)) { + return; + } + String pcmPath = pcmFile.getAbsolutePath(); + String wavPath = pcmPath.substring(0, pcmPath.length() - 4) + ".wav"; + writeHeader(new File(wavPath), header); + } + + /** + * 获取WAV文件的头信息 + * + * @param wavFilePath 文件地址 + * @return header + */ + private static byte[] getHeader(String wavFilePath) { + if (!new File(wavFilePath).isFile()) { + return null; + } + byte[] buffer = null; + File file = new File(wavFilePath); + final int size = 44; + FileInputStream fis = null; + ByteArrayOutputStream bos = null; + try { + fis = new FileInputStream(file); + bos = new ByteArrayOutputStream(size); + byte[] b = new byte[size]; + int len; + if ((len = fis.read(b)) != size) { + return null; + } + bos.write(b, 0, len); + buffer = bos.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (fis != null) { + fis.close(); + fis = null; + } + if (bos != null) { + bos.close(); + bos = null; + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return buffer; + } + + /** + * 获取wav音频时长 ms + * + * @param filePath wav文件路径 + * @return 时长 -1: 获取失败 + */ + public static long getWavDuration(String filePath) { + if (!filePath.endsWith(RecordConfig.RecordFormat.WAV.getExtension())) { + return -1; + } + byte[] header = getHeader(filePath); + return getWavDuration(header); + } + + /** + * 获取wav音频时长 ms + * + * @param header wav音频文件字节数组 + * @return 时长 -1: 获取失败 + */ + public static long getWavDuration(byte[] header) { + if (header == null || header.length < 44) { + Log.e(TAG, "header size有误"); + return -1; + } + int byteRate = ByteUtils.toInt(header, 28);//28-31 + int waveSize = ByteUtils.toInt(header, 40);//40-43 + return waveSize * 1000L / byteRate; + } + + public static String headerToString(byte[] header) { + if (header == null || header.length < 44) { + return null; + } + StringBuilder stringBuilder = new StringBuilder(); + + for (int i = 0; i < 4; i++) { + stringBuilder.append((char) header[i]); + } + stringBuilder.append(","); + + stringBuilder.append(ByteUtils.toInt(header, 4)); + stringBuilder.append(","); + + for (int i = 8; i < 16; i++) { + stringBuilder.append((char) header[i]); + } + stringBuilder.append(","); + + for (int i = 16; i < 24; i++) { + stringBuilder.append(header[i]); + } + stringBuilder.append(","); + + stringBuilder.append(ByteUtils.toInt(header, 24)); + stringBuilder.append(","); + + stringBuilder.append(ByteUtils.toInt(header, 28)); + stringBuilder.append(","); + + for (int i = 32; i < 36; i++) { + stringBuilder.append(header[i]); + } + stringBuilder.append(","); + + for (int i = 36; i < 40; i++) { + stringBuilder.append((char) header[i]); + } + stringBuilder.append(","); + + stringBuilder.append(ByteUtils.toInt(header, 40)); + + return stringBuilder.toString(); + } + + public static class WavHeader { + /** + * RIFF数据块 + */ + final String riffChunkId = "RIFF"; + int riffChunkSize; + final String riffType = "WAVE"; + + /** + * FORMAT 数据块 + */ + final String formatChunkId = "fmt "; + final int formatChunkSize = 16; + final short audioFormat = 1; + short channels; + int sampleRate; + int byteRate; + short blockAlign; + short sampleBits; + + /** + * FORMAT 数据块 + */ + final String dataChunkId = "data"; + int dataChunkSize; + + WavHeader(int totalAudioLen, int sampleRate, short channels, short sampleBits) { + this.riffChunkSize = totalAudioLen; + this.channels = channels; + this.sampleRate = sampleRate; + this.byteRate = sampleRate * sampleBits / 8 * channels; + this.blockAlign = (short) (channels * sampleBits / 8); + this.sampleBits = sampleBits; + this.dataChunkSize = totalAudioLen - 44; + } + + public byte[] getHeader() { + byte[] result; + result = ByteUtils.merger(ByteUtils.toBytes(riffChunkId), ByteUtils.toBytes(riffChunkSize)); + result = ByteUtils.merger(result, ByteUtils.toBytes(riffType)); + result = ByteUtils.merger(result, ByteUtils.toBytes(formatChunkId)); + result = ByteUtils.merger(result, ByteUtils.toBytes(formatChunkSize)); + result = ByteUtils.merger(result, ByteUtils.toBytes(audioFormat)); + result = ByteUtils.merger(result, ByteUtils.toBytes(channels)); + result = ByteUtils.merger(result, ByteUtils.toBytes(sampleRate)); + result = ByteUtils.merger(result, ByteUtils.toBytes(byteRate)); + result = ByteUtils.merger(result, ByteUtils.toBytes(blockAlign)); + result = ByteUtils.merger(result, ByteUtils.toBytes(sampleBits)); + result = ByteUtils.merger(result, ByteUtils.toBytes(dataChunkId)); + result = ByteUtils.merger(result, ByteUtils.toBytes(dataChunkSize)); + return result; + } + } + +} diff --git a/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/FileUtils.java b/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/FileUtils.java index d621788d9c..a9b26d8fce 100644 --- a/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/FileUtils.java +++ b/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/FileUtils.java @@ -30,6 +30,7 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.List; import javax.net.ssl.HttpsURLConnection; @@ -77,6 +78,21 @@ public final class FileUtils { return isFileExists(file.getAbsolutePath()); } + public static String getNowString(final java.text.DateFormat format) { + return millis2String(System.currentTimeMillis(), format); + } + + /** + * Milliseconds to the formatted time string. + * + * @param millis The milliseconds. + * @param format The format. + * @return the formatted time string + */ + public static String millis2String(final long millis, final java.text.DateFormat format) { + return format.format(new Date(millis)); + } + /** * Return whether the file exists. *