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? = 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){ 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.") } }