commit 06/11/2025
This commit is contained in:
127
src/audio/OpusEncoder.kt
Normal file
127
src/audio/OpusEncoder.kt
Normal 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.")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user