commit 06/11/2025

This commit is contained in:
2025-11-06 11:31:02 +07:00
parent 72c509feec
commit b24c153615
34 changed files with 719 additions and 174 deletions

127
src/audio/OpusEncoder.kt Normal file
View File

@@ -0,0 +1,127 @@
package audio
import audioPlayer
import com.sun.jna.Memory
import com.sun.jna.Pointer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.tinylog.Logger
import java.util.function.Consumer
@Suppress("unused")
class OpusEncoder(val samplingrate: Int=44100, val channels: Int=1) {
var push_handle: Int = 0
var opus_handle: Int = 0
private val bass = audioPlayer.bass
private val bassencopus = audioPlayer.bassencopus
var callback : Consumer<ByteArray>? = null
/**
* Check if the encoder is started
* @return true if started, false otherwise
*/
fun isStarted() : Boolean {
return push_handle!=0 && opus_handle!=0
}
val proc = BassEnc.ENCODEPROC{
enchandle, sourcehandle, buffer, length, user ->
if (enchandle == opus_handle) {
if (sourcehandle == push_handle) {
val data = ByteArray(length)
buffer.read(0, data, 0, length)
callback?.accept(data)
//println("MP3 Encoder callback: Sent $length bytes")
} else Logger.error { "Opus Encoder callback called with unknown source handle: $sourcehandle" }
} else Logger.error { "Opus Encoder callback called with unknown encoder handle: $enchandle" }
}
/**
* Start the Opus encoder
* @param cb Function to receive encoded MP3 data
*/
fun Start(cb: Consumer<ByteArray>){
callback = null
push_handle = bass.BASS_StreamCreate(samplingrate, channels, Bass.BASS_STREAM_DECODE, Pointer(-1), null)
if (push_handle!=0){
Logger.info{"Opus Encoder initialized with sampling rate $samplingrate Hz and $channels channel(s)" }
opus_handle = bassencopus.BASS_Encode_OPUS_Start(push_handle, null, BassEnc.BASS_ENCODE_AUTOFREE, proc, null)
if (opus_handle!=0){
callback = cb
CoroutineScope(Dispatchers.Default).launch {
val readsize = 8 * 1024 * 1024 // 8 MB buffer
Logger.info{"Opus Encoder started successfully." }
while(isActive && push_handle!=0){
delay(2)
val p = Memory(readsize.toLong())
val read = bass.BASS_ChannelGetData(push_handle, p, readsize)
if (read==-1){
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to read data from Opus Encoder stream. BASS error code: $err" }
break
}
}
Logger.info{"Opus Encoder finished successfully." }
}
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to start Opus Encoder. BASS error code: $err"}
}
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to initialize Opus Encoder. BASS error code: $err" }
}
}
/**
* Push PCM data to be encoded
* @param data PCM data in ByteArray
* @return Number of bytes written, or 0 if failed
*/
fun PushData(data: ByteArray): Int {
if (push_handle==0){
Logger.error{"Opus Encoder is not started." }
return 0
}
val mem = Memory(data.size.toLong())
mem.write(0, data, 0, data.size)
val written = bass.BASS_StreamPutData(push_handle, mem, data.size)
if (written==-1){
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to push data to Opus Encoder. BASS error code: $err" }
return 0
}
//println("Opus Encoder: Pushed $written bytes of PCM data.")
return written
}
/**
* Stop the Opus encoder and free resources
*/
fun Stop(){
if (push_handle!=0){
val res = bass.BASS_StreamFree(push_handle)
if (res){
Logger.info{"Opus Encoder stream freed successfully." }
} else {
val err = bass.BASS_ErrorGetCode()
Logger.error{"Failed to free Opus Encoder stream. BASS error code: $err"}
}
push_handle = 0
}
// auto close by BASS_ENCODE_AUTOFREE
opus_handle = 0
callback = null
println("Opus Encoder: Stopped.")
}
}