126 lines
4.4 KiB
Kotlin
126 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 Mp3Encoder(val samplingrate: Int=44100, val channels: Int=1) {
|
|
var push_handle: Int = 0
|
|
var mp3_handle: Int = 0
|
|
private val bass = audioPlayer.bass
|
|
private val bassencmp3 = audioPlayer.bassencmp3
|
|
var callback : Consumer<ByteArray>? = null
|
|
|
|
/**
|
|
* Check if the encoder is started
|
|
* @return true if started, false otherwise
|
|
*/
|
|
fun isStarted() : Boolean {
|
|
return push_handle!=0 && mp3_handle!=0
|
|
}
|
|
|
|
val proc = BassEnc.ENCODEPROCEX{
|
|
enchandle, sourcehandle, buffer, length, offset, user ->
|
|
if (enchandle == mp3_handle) {
|
|
if (sourcehandle == push_handle) {
|
|
val data = ByteArray(length)
|
|
buffer.read(0, data, 0, length)
|
|
callback?.accept(data)
|
|
} else Logger.error { "MP3 Encoder callback called with unknown source handle: $sourcehandle" }
|
|
} else Logger.error { "MP3 Encoder callback called with unknown encoder handle: $enchandle" }
|
|
}
|
|
|
|
/**
|
|
* Start the MP3 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{"MP3 Encoder initialized with sampling rate $samplingrate Hz and $channels channel(s)" }
|
|
val options = "lame -b 128 --cbr --write-xing 0 --nohist --noid3v2 - -"
|
|
//val flag = BassEnc.BASS_ENCODE_AUTOFREE or BassEnc.BASS_ENCODE_NOHEAD
|
|
val flag = BassEnc.BASS_ENCODE_AUTOFREE
|
|
mp3_handle = bassencmp3.BASS_Encode_MP3_Start(push_handle, options, flag,proc, null)
|
|
|
|
if (mp3_handle!=0){
|
|
callback = cb
|
|
|
|
CoroutineScope(Dispatchers.Default).launch {
|
|
val readsize = 4*1024 // 4 K buffer
|
|
Logger.info{"MP3 Encoder started successfully." }
|
|
while(isActive && push_handle!=0){
|
|
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 MP3 Encoder stream. BASS error code: $err" }
|
|
break
|
|
}
|
|
delay(2)
|
|
}
|
|
Logger.info{"MP3 Encoder finished successfully." }
|
|
}
|
|
|
|
} else {
|
|
val err = bass.BASS_ErrorGetCode()
|
|
Logger.error{"Failed to start MP3 Encoder. BASS error code: $err"}
|
|
}
|
|
} else {
|
|
val err = bass.BASS_ErrorGetCode()
|
|
Logger.error{"Failed to initialize MP3 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{"MP3 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 MP3 Encoder. BASS error code: $err" }
|
|
return 0
|
|
}
|
|
return written
|
|
}
|
|
|
|
/**
|
|
* Stop the MP3 encoder and free resources
|
|
*/
|
|
fun Stop(){
|
|
|
|
if (push_handle!=0){
|
|
val res = bass.BASS_StreamFree(push_handle)
|
|
if (res){
|
|
Logger.info{"MP3 Encoder stream freed successfully." }
|
|
} else {
|
|
val err = bass.BASS_ErrorGetCode()
|
|
Logger.error{"Failed to free MP3 Encoder stream. BASS error code: $err"}
|
|
}
|
|
push_handle = 0
|
|
}
|
|
// auto close by BASS_ENCODE_AUTOFREE
|
|
mp3_handle = 0
|
|
callback = null
|
|
}
|
|
} |