代码格式整理

This commit is contained in:
loliball 2024-08-31 23:04:04 +08:00
parent 7d20b9c841
commit 25805cd556
5 changed files with 149 additions and 240 deletions

View File

@ -1,7 +1,9 @@
import io.github.jaredmdobson.concentus.*
import io.github.jaredmdobson.concentus.OpusApplication
import io.github.jaredmdobson.concentus.OpusDecoder
import io.github.jaredmdobson.concentus.OpusEncoder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
@ -11,57 +13,48 @@ import java.net.Socket
import javax.sound.sampled.AudioSystem
import kotlin.system.measureTimeMillis
val SERVER_PORT = 7860
val ECHO_CANCELLATION = true
val PACKET_SAMPLES = 2880
val SAMPLE_RATE = 48000
val CHANNELS = 1
val BUFFER_SIZE = PACKET_SAMPLES * CHANNELS
val AUDIO_FORMAT = AudioConvert.getAudioFormat(SAMPLE_RATE, CHANNELS)
object Client {
class Client {
companion object {
@JvmStatic
fun main(args: Array<String>) {
mainClient()
Client().mainClient()
}
}
}
data class DataPack(
val timestamp: Long,
val opus: ByteArray,
) : Serializable
val bufferOut = ByteArray(BUFFER_SIZE)
val targetDataLine = AudioSystem.getTargetDataLine(AUDIO_FORMAT).also {
it.open(AUDIO_FORMAT, bufferOut.size)
it.start()
}
fun mainClient() = runBlocking {
val bufferIn = ByteArray(BUFFER_SIZE)
val sourceDataLine = AudioSystem.getSourceDataLine(AUDIO_FORMAT).also {
it.open(AUDIO_FORMAT, bufferIn.size)
it.start()
}
val decoder = OpusDecoder(SAMPLE_RATE, CHANNELS)
val encoder = OpusEncoder(SAMPLE_RATE, CHANNELS, OpusApplication.OPUS_APPLICATION_AUDIO).also {
it.bitrate = OPUS_BITRATE
it.forceMode = OPUS_MODE
it.signalType = OPUS_SIGNAL_TYPE
it.complexity = OPUS_COMPLEXITY
}
val scope = CoroutineScope(Dispatchers.IO)
fun mainClient() {
val socket = Socket()
val t = measureTimeMillis {
socket.connect(InetSocketAddress("zedo.top", SERVER_PORT))
// socket.connect(InetSocketAddress("127.0.0.1", SERVER_PORT))
socket.connect(InetSocketAddress(SERVER_HOST, SERVER_PORT))
}
println("connect time $t")
"connect time $t".loge()
val out = ObjectOutputStream(socket.getOutputStream())
val inp = ObjectInputStream(socket.getInputStream())
val bufferOut = ByteArray(BUFFER_SIZE * 2)
val targetDataLine = AudioSystem.getTargetDataLine(AUDIO_FORMAT)
targetDataLine.open(AUDIO_FORMAT, bufferOut.size)
targetDataLine.start()
val bufferIn = ByteArray(BUFFER_SIZE * 2)
val sourceDataLine = AudioSystem.getSourceDataLine(AUDIO_FORMAT)
sourceDataLine.open(AUDIO_FORMAT, bufferIn.size)
sourceDataLine.start()
val decoder = OpusDecoder(SAMPLE_RATE, CHANNELS)
val encoder = OpusEncoder(SAMPLE_RATE, CHANNELS, OpusApplication.OPUS_APPLICATION_AUDIO).also {
it.bitrate = 96000
it.forceMode = OpusMode.MODE_CELT_ONLY
it.signalType = OpusSignal.OPUS_SIGNAL_MUSIC
it.complexity = 0
}
launch(Dispatchers.IO) {
scope.launch {
while (true) {
targetDataLine.read(bufferOut, 0, bufferOut.size)
try {
@ -74,7 +67,7 @@ fun mainClient() = runBlocking {
}
}
launch(Dispatchers.IO) {
scope.launch {
while (true) {
try {
// if (`in`!!.available() > bufferIn.size * 2) {
@ -91,5 +84,5 @@ fun mainClient() = runBlocking {
}
}
}
}
}

10
src/DataPack.kt Normal file
View File

@ -0,0 +1,10 @@
import java.io.Serializable
/**
* Created by LoliBall on 2024/8/31 23:02.
* https://github.com/WhichWho
*/
data class DataPack(
val timestamp: Long,
val opus: ByteArray,
) : Serializable

View File

@ -1,30 +1,50 @@
import io.github.jaredmdobson.concentus.OpusDecoder
import io.github.jaredmdobson.concentus.OpusEncoder
import io.github.jaredmdobson.concentus.*
import kotlinx.coroutines.*
/**
* Created by LoliBall on 2024/8/31 5:26.
* https://github.com/WhichWho
*/
//val SERVER_HOST = "zedo.top"
val SERVER_HOST = "127.0.0.1"
val SERVER_PORT = 7860
val ECHO_CANCELLATION = true // 回声消除
// 2.5, 5, 10, 20, 40, 60 ms
// 120, 240, 480, 960, 1920, 2880
val PACKET_SAMPLES = 2880 // 48000Hz * 60ms = 2880
val SAMPLE_RATE = 48000 // 8000、12000、16000、24000、48000
val CHANNELS = 1
val BUFFER_SIZE = PACKET_SAMPLES * CHANNELS * 2 // * 2是short | 48000Hz * 2(short) * 60ms = 5760B
val AUDIO_FORMAT = AudioConvert.getAudioFormat(SAMPLE_RATE, CHANNELS)
val OPUS_BITRATE = OpusConstants.OPUS_AUTO // 500 - 300000
val OPUS_COMPLEXITY = 10 // 0: 快,质量差 10: 慢,质量好
val OPUS_SIGNAL_TYPE = OpusSignal.OPUS_SIGNAL_VOICE
val OPUS_MODE = OpusMode.MODE_HYBRID
fun main() = runBlocking {
CoroutineScope(Dispatchers.IO).launch {
Server.main(emptyArray())
}
delay(1000)
Client.main(emptyArray())
delay(Long.MAX_VALUE)
}
fun OpusEncoder.encode(
pcm: ByteArray,
opusBuffer: ByteArray = ByteArray(pcm.size)
): DataPack {
val encodeOpusBuffer = mutableMapOf<OpusEncoder, ByteArray>()
// 1275是opus单帧包最大值
fun OpusEncoder.encode(pcm: ByteArray): DataPack {
val opusBuffer = encodeOpusBuffer[this] ?: ByteArray(1275).also { encodeOpusBuffer[this] = it }
val bytesEncoded = encode(pcm, 0, PACKET_SAMPLES, opusBuffer, 0, opusBuffer.size)
"compress rate: %.2f%%".format(bytesEncoded / pcm.size.toFloat() * 100).loge()
return DataPack(System.currentTimeMillis(), opusBuffer.sliceArray(0..<bytesEncoded))
}
fun OpusDecoder.decode(
pack: DataPack,
output: ByteArray,
) {
decode(pack.opus, 0, pack.opus.size, output, 0, PACKET_SAMPLES, false)
fun OpusDecoder.decode(pack: DataPack, pcm: ByteArray) {
decode(pack.opus, 0, pack.opus.size, pcm, 0, PACKET_SAMPLES, false)
}
fun <T> T.loge() = also { println(this) }

View File

@ -1,118 +0,0 @@
import io.github.jaredmdobson.concentus.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
* @author lostromb
*/
public class Program {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
test();
}
public static void test() {
try {
FileInputStream fileIn = new FileInputStream("a.pcm");
OpusEncoder encoder = new OpusEncoder(48000, 1, OpusApplication.OPUS_APPLICATION_AUDIO);
encoder.setBitrate(96000);
encoder.setForceMode(OpusMode.MODE_CELT_ONLY);
encoder.setSignalType(OpusSignal.OPUS_SIGNAL_MUSIC);
encoder.setComplexity(0);
OpusDecoder decoder = new OpusDecoder(48000, 1);
FileOutputStream fileOut = new FileOutputStream("b.pcm");
int packetSamples = 960;
byte[] inBuf = new byte[packetSamples * 2 * 2];
byte[] data_packet = new byte[1275];
long start = System.currentTimeMillis();
long size = 0;
while (fileIn.available() >= inBuf.length) {
int bytesRead = fileIn.read(inBuf, 0, inBuf.length);
short[] pcm = BytesToShorts(inBuf, 0, inBuf.length);
int bytesEncoded = encoder.encode(pcm, 0, packetSamples, data_packet, 0, 1275);
//System.out.println(bytesEncoded + " bytes encoded");
size += bytesEncoded;
int samplesDecoded = decoder.decode(data_packet, 0, bytesEncoded, pcm, 0, packetSamples, false);
//System.out.println(samplesDecoded + " samples decoded");
byte[] bytesOut = ShortsToBytes(pcm);
fileOut.write(bytesOut, 0, bytesOut.length);
}
long end = System.currentTimeMillis();
System.out.println("Time was " + (end - start) + "ms");
fileIn.close();
fileOut.close();
System.out.println("Done!");
} catch (IOException e) {
System.out.println(e.getMessage());
} catch (OpusException e) {
System.out.println(e.getMessage());
}
}
/// <summary>
/// Converts interleaved byte samples (such as what you get from a capture device)
/// into linear short samples (that are much easier to work with)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static short[] BytesToShorts(byte[] input) {
return BytesToShorts(input, 0, input.length);
}
/// <summary>
/// Converts interleaved byte samples (such as what you get from a capture device)
/// into linear short samples (that are much easier to work with)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static short[] BytesToShorts(byte[] input, int offset, int length) {
short[] processedValues = new short[length / 2];
for (int c = 0; c < processedValues.length; c++) {
short a = (short) (((int) input[(c * 2) + offset]) & 0xFF);
short b = (short) (((int) input[(c * 2) + 1 + offset]) << 8);
processedValues[c] = (short) (a | b);
}
return processedValues;
}
/// <summary>
/// Converts linear short samples into interleaved byte samples, for writing to a file, waveout device, etc.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] ShortsToBytes(short[] input) {
return ShortsToBytes(input, 0, input.length);
}
/// <summary>
/// Converts linear short samples into interleaved byte samples, for writing to a file, waveout device, etc.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] ShortsToBytes(short[] input, int offset, int length) {
byte[] processedValues = new byte[length * 2];
for (int c = 0; c < length; c++) {
processedValues[c * 2] = (byte) (input[c + offset] & 0xFF);
processedValues[c * 2 + 1] = (byte) ((input[c + offset] >> 8) & 0xFF);
}
return processedValues;
}
}

View File

@ -1,7 +1,9 @@
import io.github.jaredmdobson.concentus.*
import io.github.jaredmdobson.concentus.OpusApplication
import io.github.jaredmdobson.concentus.OpusDecoder
import io.github.jaredmdobson.concentus.OpusEncoder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
@ -13,15 +15,15 @@ import java.util.concurrent.CopyOnWriteArrayList
val clientAudios = CopyOnWriteArrayList<ClientAudio>()
object Server {
@JvmStatic
fun main(args: Array<String>) {
mainServer()
}
}
fun mainServer() = runBlocking {
//混音
launch(Dispatchers.IO) {
fun mainServer() {
CoroutineScope(Dispatchers.IO).launch {
while (true) {
for (client in clientAudios) {
if (!client.isConnected) {
@ -32,7 +34,7 @@ fun mainServer() = runBlocking {
}
for (client in clientAudios) {
launch(Dispatchers.IO) {
launch {
for (audio in clientAudios) {
if (audio != client || !ECHO_CANCELLATION) {
client.mix(audio.sample)
@ -50,23 +52,25 @@ fun mainServer() = runBlocking {
println("Accepted connection from " + socket.remoteSocketAddress)
clientAudios += ClientAudio(socket)
}
}
}
class ClientAudio(val socket: Socket) {
val inp = ObjectInputStream(socket.getInputStream())
val out = ObjectOutputStream(socket.getOutputStream())
val buffer = ByteArray(BUFFER_SIZE * 2)
val sample = FloatArray(BUFFER_SIZE)
val mixSample = FloatArray(BUFFER_SIZE)
val mixBuffer = ByteArray(BUFFER_SIZE * 2)
val buffer = ByteArray(BUFFER_SIZE)
val sample = FloatArray(PACKET_SAMPLES)
val mixSample = FloatArray(PACKET_SAMPLES)
val mixBuffer = ByteArray(BUFFER_SIZE)
val decoder = OpusDecoder(48000, 1)
val encoder = OpusEncoder(48000, 1, OpusApplication.OPUS_APPLICATION_AUDIO).also {
it.bitrate = 96000
it.forceMode = OpusMode.MODE_CELT_ONLY
it.signalType = OpusSignal.OPUS_SIGNAL_MUSIC
it.complexity = 0
val decoder = OpusDecoder(SAMPLE_RATE, CHANNELS)
val encoder = OpusEncoder(SAMPLE_RATE, CHANNELS, OpusApplication.OPUS_APPLICATION_AUDIO).also {
it.bitrate = OPUS_BITRATE
it.forceMode = OPUS_MODE
it.signalType = OPUS_SIGNAL_TYPE
it.complexity = OPUS_COMPLEXITY
}
var isConnected = true