Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1.3.1 beta #12

Merged
merged 7 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
### Sonic扩展功能
> 1. 获取app列表,包括app图标
> 2. 远程音频采集
> 3. PCM格式音频进行ACC转码
> 3. PCM格式音频进行WAV转码(doing)
> 4. 横竖屏实时监听
> 5. 剪切板管理优化(doing)
> 6. 其他扩展

### 开源协议

Expand Down
36 changes: 18 additions & 18 deletions app/src/main/java/org/cloud/sonic/android/AudioService.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,30 +266,30 @@ private static AudioRecord createAudioRecord(MediaProjection mediaProjection) {
// packet[6] = (byte) 0xFC;
// }

//try wav

//record audio
private void startRecording() {
final AudioRecord recorder = createAudioRecord(mediaProjection);

recorderThread = new Thread(new Runnable() {
@Override
public void run() {
try (LocalSocket socket = connect()) {
handler.sendEmptyMessage(MSG_CONNECTION_ESTABLISHED);

recorder.startRecording();
int BUFFER_MS = 15; // do not buffer more than BUFFER_MS milliseconds
byte[] buf = new byte[SAMPLE_RATE * CHANNELS * BUFFER_MS / 1000];
while (true) {
int r = recorder.read(buf, 0, buf.length);
//try not thread
recorderThread = new Thread(() -> {
try (LocalSocket socket = connect()) {
handler.sendEmptyMessage(MSG_CONNECTION_ESTABLISHED);

recorder.startRecording();
int BUFFER_MS = 15; // do not buffer more than BUFFER_MS milliseconds
byte[] buf = new byte[SAMPLE_RATE * CHANNELS * BUFFER_MS / 1000];
while (true) {
int r = recorder.read(buf, 0, buf.length);
// encodePCMToAAC(buf,socket.getOutputStream());
socket.getOutputStream().write(buf, 0, r);
}
} catch (IOException e) {
// ignore
} finally {
recorder.stop();
stopSelf();
socket.getOutputStream().write(buf, 0, r);
}
} catch (IOException e) {
// ignore
} finally {
recorder.stop();
stopSelf();
}
});
recorderThread.start();
Expand Down
60 changes: 60 additions & 0 deletions app/src/main/java/org/cloud/sonic/android/models/WavHeader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.cloud.sonic.android.models;

import org.cloud.sonic.android.util.ByteUtils;

/**
*
*/
public 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;

public 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;
}
}
105 changes: 105 additions & 0 deletions app/src/main/java/org/cloud/sonic/android/util/ByteUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.cloud.sonic.android.util;

import java.nio.ByteBuffer;
import java.util.Arrays;

public class ByteUtils {
/**
* 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);
}
}
63 changes: 63 additions & 0 deletions app/src/main/java/org/cloud/sonic/android/util/WavUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.cloud.sonic.android.util;

import android.os.FileUtils;
import android.util.Log;

import org.cloud.sonic.android.models.WavHeader;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.logging.Logger;

/**
* 生成头字节
*/
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 (!file.exists()) {
return;
}

RandomAccessFile wavRaf = null;
try {
wavRaf = new RandomAccessFile(file, "rw");
wavRaf.seek(0);
wavRaf.write(header);
wavRaf.close();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
} finally {
try {
if (wavRaf != null) {
wavRaf.close();
}
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
}
}