127 lines
4.4 KiB
Kotlin
127 lines
4.4 KiB
Kotlin
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.")
|
|
}
|
|
} |