代码地址:https://download.youkuaiyun.com/download/qq_14931305/10803169
Bass库官网:http://www.un4seen.com/
Bass库参考文档:http://www.un4seen.com/doc/#bass/bass.html
1.Bass库集成
集成请参考我之前的博客:https://blog.youkuaiyun.com/qq_14931305/article/details/84374301
2.录音完整代码
代码地址:https://download.youkuaiyun.com/download/qq_14931305/10810927
3.录音实现
- BASSRecord类
package com.lin.bassrecorddemo; import android.util.Log; import com.un4seen.bass.BASS; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; /** * Created by jiedoushi on 2018/11/27. */ public class BASSRecord implements BASS.RECORDPROC{ private static final String pathRecordRaw ="/storage/emulated/0/BASSRecord/"+System.currentTimeMillis()+".raw"; private String pathRecordWav; private static final int DEFAULT_SAMPLING_RATE =44100; private static final int DEFAULT_WAV_CHANNEL =1;//通道 private BassDataEncodeThread mEncodeThread; private int chan; @Override public boolean RECORDPROC(int handle, ByteBuffer byteBuffer, int length, Object user) { //write raw file int byteSize = byteBuffer.capacity(); mEncodeThread.addTask(byteBuffer, byteSize); mEncodeThread.writeData(); byteBuffer.position(0); return true; } private void init(){ File file =new File(pathRecordRaw); if(!file.getParentFile().exists()){ file.getParentFile().mkdirs(); } if (!file.exists()){ } pathRecordWav =pathRecordRaw.replace(".raw",".wav"); mEncodeThread = new BassDataEncodeThread(pathRecordRaw); mEncodeThread.start(); // setup recording device (using default device) if (!BASS.BASS_RecordInit(-1)) { Error("Can't initialize recording device"); } } public void startRecord(){ init(); // start recording Log.e("BASSRecorder","录音信息sampl="+DEFAULT_SAMPLING_RATE+ " channels="+DEFAULT_WAV_CHANNEL); if (BASS.BASS_RecordStart(DEFAULT_SAMPLING_RATE, DEFAULT_WAV_CHANNEL, 0, this, 0) == 0) { Error("Couldn't start recording"); } } public void stopRecord(){ try { BASS.BASS_RecordFree(); mEncodeThread.stopWrite(); File wavFile =new File(pathRecordWav); if (wavFile.exists()){ wavFile.createNewFile(); } File fileRaw= new File(pathRecordRaw); WavPcmUtil.saveRawAsWavFile(fileRaw, 1, wavFile, null); } catch (IOException e) { e.printStackTrace(); } } public void playMusic(){ if (!BASS.BASS_Init(-1,44100,0)) { Error("Can't initialize device"); return; }else { chan=BASS.BASS_StreamCreateFile(pathRecordWav,0L,0L,BASS.BASS_SAMPLE_FLOAT); if (chan == 0){ Error("Can't initialize BASS_StreamCreateFile"); return; } if (!BASS.BASS_ChannelPlay(chan, true)) // play the recorded data Error("Cannot Play"); } } public void stopMusic(){ BASS.BASS_ChannelStop(chan); BASS.BASS_StreamFree(chan); BASS.BASS_Stop(); BASS.BASS_Free(); } // display error messages void Error(String es) { Log.e("BASSRecorder","PROCESS_ERROR "); BASS.BASS_RecordFree(); // get error code in current thread for display in UI thread String s=String.format("%s\n(error code: %d)", es, BASS.BASS_ErrorGetCode()); Log.e("BASSRecorder","BASS_ErrorGetCode= "+s); } }
-
BassDataEncodeThread类
package com.lin.bassrecorddemo; import android.os.HandlerThread; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Created by jiedoushi on 2018/11/27. */ public class BassDataEncodeThread extends HandlerThread { private String pathRecord; private FileChannel mFileChannel; public BassDataEncodeThread(String path) { super("BassDataEncodeThread"); pathRecord =path; File file =new File(path); try { this.mFileChannel = new FileOutputStream(file).getChannel(); } catch (FileNotFoundException e) { e.printStackTrace(); } } public void writeData(){ if (mTasks.size() > 0) { Task task = mTasks.remove(0); ByteBuffer byteBuffer = task.getData(); int readSize = task.getReadSize(); if (readSize > 0) { try { mFileChannel.write(byteBuffer); } catch (IOException e) { e.printStackTrace(); } } } } public void stopWrite(){ try { mFileChannel.close(); } catch (IOException e) { e.printStackTrace(); } getLooper().quit(); } private List<Task> mTasks = Collections.synchronizedList(new ArrayList<Task>()); void addTask(ByteBuffer rawData, int readSize) { mTasks.add(new Task(rawData, readSize)); } private class Task { private ByteBuffer rawData; private int readSize; Task(ByteBuffer rawData, int readSize) { this.rawData = rawData; this.readSize = readSize; } public ByteBuffer getData() { return rawData; } int getReadSize() { return readSize; } } }
-
MainActivity类
package com.lin.bassrecorddemo; import android.Manifest; import android.content.pm.PackageManager; import android.os.Build; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; public class MainActivity extends AppCompatActivity { private BASSRecord record; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestVibratePermission(); findViewById(R.id.btnStart).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (record == null) { record = new BASSRecord(); } record.startRecord(); } }); findViewById(R.id.btnStop).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { record.stopRecord(); } }); findViewById(R.id.btnPlay).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { record.playMusic(); } }); findViewById(R.id.btnStopMusic).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { record.stopMusic(); } }); } /** * 申请振动权限 */ public boolean requestVibratePermission (){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1001); Log.e("phonevibrate", "权限未申请"); return false; } else { Log.e("phonevibrate", "权限已经申请"); return true; } } return true; } }
-
AndroidManifest.xml权限添加
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
-
WavPcmUtil类,wav文件头定义
package com.lin.bassrecorddemo; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.List; /** * Created by jiedoushi on 2017/11/22. * public static void testVolume() { try { File a = new File("/sdcard/PerfectPiano/test/a.wav"); File c = new File("/sdcard/PerfectPiano/test/c.raw"); File d = new File("/sdcard/PerfectPiano/test/d.wav"); if (c.exists()) { c.delete(); } c.createNewFile(); if(d.exists()) { d.delete(); } d.createNewFile(); setWavFileVolume(a, 0.1, c,null); saveRawAsWavFile(c, 1, d, null); } catch (IOException e) { e.printStackTrace(); } } public static void test() { try { File a = new File("/sdcard/PerfectPiano/test/a.raw"); File b = new File("/sdcard/PerfectPiano/test/b.wav"); File c = new File("/sdcard/PerfectPiano/test/c.raw"); File d = new File("/sdcard/PerfectPiano/test/d.wav"); if (c.exists()) { c.delete(); } c.createNewFile(); if(d.exists()) { d.delete(); } d.createNewFile(); mergeWavToRawFile(a, b, c); saveRawAsWavFile(c, 1, d); }catch (IOException e) { e.printStackTrace(); } } public static void test2() { try { File a = new File("/sdcard/PerfectPiano/test/b.wav"); File b = new File("/sdcard/PerfectPiano/test/d.wav"); File c = new File("/sdcard/PerfectPiano/test/c.raw"); File d = new File("/sdcard/PerfectPiano/test/e.wav"); List<File> inputList = new ArrayList<>(); inputList.add(a); inputList.add(b); List<Integer> timelist = new ArrayList<>(); timelist.add(3000); timelist.add(120000); if (c.exists()) { c.delete(); } c.createNewFile(); if(d.exists()) { d.delete(); } d.createNewFile(); spliceWavFiles(inputList, timelist, c); saveRawAsWavFile(c, 1, d); }catch (IOException e) { e.printStackTrace(); } } public static void test3() { try { File a = new File("/sdcard/PerfectPiano/test/b.wav"); File b = new File("/sdcard/PerfectPiano/test/f.raw"); File c = new File("/sdcard/PerfectPiano/test/g.raw"); File d = new File("/sdcard/PerfectPiano/test/h.wav"); File e = new File("/sdcard/PerfectPiano/test/i.wav"); if (b.exists()) { b.delete(); } b.createNewFile(); if (c.exists()) { c.delete(); } c.createNewFile(); if(d.exists()) { d.delete(); } d.createNewFile(); if(e.exists()) { e.delete(); } e.createNewFile(); int channelCount = splitWavFile(a, 30000, b, c); saveRawAsWavFile(b, channelCount, d); saveRawAsWavFile(c, channelCount, e); }catch (IOException e) { e.printStackTrace(); } } */ public class WavPcmUtil { public static final int DEFAULT_SAMPLING_RATE = 44100; public interface WavOperationCallback { void wavOperationComplete(); void wavOperationError(); } /** * 将PCM RAW DATA存储为.wav文件 * @param rawInputFile * @param channels * @param outputWavFile * @throws IOException */ public static void saveRawAsWavFile(File rawInputFile, int channels, File outputWavFile, WavOperationCallback callback) throws IOException { if(rawInputFile == null || outputWavFile == null) { throw new IOException(); } FileInputStream fis = new FileInputStream(rawInputFile); FileOutputStream fos = new FileOutputStream(outputWavFile); Log.e("WavPcmUtil", "save start"); int fileLength = (int)rawInputFile.length(); createWavHeader(fos, fileLength, (short) channels, DEFAULT_SAMPLING_RATE, (short) 16); byte[] data = new byte[512]; int length = 0; while ((length = fis.read(data)) != -1) { fos.write(data, 0, length); } Log.e("WavPcmUtil", "save end"); fos.flush(); fos.close(); fis.close(); if (callback!=null) { callback.wavOperationComplete(); } } /** * 创建.wav文件头,并写入output stream. * @param fos: 写入的文件 * @param rawLength: raw文件数据长度 * @param channel: 声道数量 * @param sampleRate: 采样率 * @param bitPerSample: 比特率 * @throws IOException */ public static void createWavHeader(FileOutputStream fos, int rawLength, short channel, int sampleRate, short bitPerSample) throws IOException { byte[] data = new byte[1]; // RIFF data[0] = 'R'; fos.write(data); data[0] = 'I'; fos.write(data); data[0] = 'F'; fos.write(data); data[0] = 'F'; fos.write(data); int fileLength = rawLength + (44 - 8); fos.write(intToByteArray(fileLength)); // WAVE data[0] = 'W'; fos.write(data); data[0] = 'A'; fos.write(data); data[0] = 'V'; fos.write(data); data[0] = 'E'; fos.write(data); // fmt data[0] = 'f'; fos.write(data); data[0] = 'm'; fos.write(data); data[0] = 't'; fos.write(data); data[0] = ' '; fos.write(data); int size = 16; fos.write(intToByteArray(size)); short fmtTag = 1; fos.write(shortToByteArray(fmtTag)); fos.write(shortToByteArray(channel)); fos.write(intToByteArray(sampleRate)); short blockAlign = (short)(channel * bitPerSample / 8); int bytePerSec = blockAlign * sampleRate; fos.write(intToByteArray(bytePerSec)); fos.write(shortToByteArray(blockAlign)); fos.write(shortToByteArray(bitPerSample)); // data data[0] = 'd'; fos.write(data); data[0] = 'a'; fos.write(data); data[0] = 't'; fos.write(data); data[0] = 'a'; fos.write(data); // data size fos.write(intToByteArray(rawLength)); } /** * 将int转化为byte[] */ private static byte[] intToByteArray(int data) { return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(data).array(); } /** * 将short转化为byte[] */ private static byte[] shortToByteArray(short data) { return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(data).array(); } }
代码已经全部上传,希望能帮到你们!