commit 07/10/2025

This commit is contained in:
2025-10-07 15:57:23 +07:00
parent d04a8bedd1
commit e8695c7a6f
6 changed files with 583 additions and 558 deletions

View File

@@ -109,8 +109,12 @@ fun main() {
delay(1000) delay(1000)
// prioritas 1 , habisin queue paging // prioritas 1 , habisin queue paging
subcode01.Read_Queue_Paging() subcode01.Read_Queue_Paging()
// prioritas 2, habisin queue table // prioritas 2, habisin queue shalat
subcode01.Read_Queue_Table() subcode01.Read_Queue_Shalat()
// prioritas 3, habisin queue timer
subcode01.Read_Queue_Timer()
// prioritas 4, habisin queue soundbank
subcode01.Read_Queue_Soundbank()
} }
} }
// Coroutine untuk cek Schedulebank tiap menit saat detik 00 // Coroutine untuk cek Schedulebank tiap menit saat detik 00

View File

@@ -1,4 +1,5 @@
import audio.AudioFileInfo import audio.AudioFileInfo
import codes.Result_GetSoundbankFiles
import codes.Somecodes.Companion.Get_ANN_ID import codes.Somecodes.Companion.Get_ANN_ID
import codes.Somecodes.Companion.IsAlphabethic import codes.Somecodes.Companion.IsAlphabethic
import codes.Somecodes.Companion.IsNumber import codes.Somecodes.Companion.IsNumber
@@ -22,7 +23,6 @@ import java.time.DayOfWeek
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.LocalTime import java.time.LocalTime
import java.util.function.Consumer
/** /**
* MainExtension01 contains additional functions for the main application. * MainExtension01 contains additional functions for the main application.
@@ -208,27 +208,25 @@ class MainExtension01 {
* Find soundbank files from messagebank tags, filtered by VoiceType and Language * Find soundbank files from messagebank tags, filtered by VoiceType and Language
* @param mb Messagebank object * @param mb Messagebank object
* @param variables Map of variables to replace in tags. * @param variables Map of variables to replace in tags.
* @param cbOK Callback function if success, returns List of soundbank file names * @return Result_GetSoundbankFiles object containing success status, message, and list of soundbank file paths.
* @param cbFail Callback function if failed, returns error message
*/ */
fun Get_Soundbank_Files( fun Get_Soundbank_Files(
mb: Messagebank, mb: Messagebank,
variables: Map<String, String>, variables: Map<String, String>
cbOK: Consumer<List<String>>,
cbFail: Consumer<String> ) : Result_GetSoundbankFiles {
) {
val tags = mb.Message_TAGS.split(" ") val tags = mb.Message_TAGS.split(" ")
if (tags.isEmpty()) { if (tags.isEmpty()) {
cbFail.accept("No tags found in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No tags found in messagebank id ${mb.ANN_ID}")
return
} }
// dapatkan soundbank array berdasarkan VoiceType dan Language // dapatkan soundbank array berdasarkan VoiceType dan Language
val sb = db.soundDB.List val sb = db.soundDB.List
.filter { it.VoiceType.equals(mb.Voice_Type, true) } .filter { it.VoiceType.equals(mb.Voice_Type, true) }
.filter { it.Language.equals(mb.Language, true) } .filter { it.Language.equals(mb.Language, true) }
if (sb.isEmpty()) { if (sb.isEmpty()) {
cbFail.accept("No soundbank found for voice type ${mb.Voice_Type} and language ${mb.Language}") return Result_GetSoundbankFiles(false, "No soundbank found for voice type ${mb.Voice_Type} and language ${mb.Language}")
return
} }
val files = mutableListOf<String>() val files = mutableListOf<String>()
@@ -244,16 +242,16 @@ class MainExtension01 {
if (ValidFile(airplane.Path)) { if (ValidFile(airplane.Path)) {
files.add(airplane.Path) files.add(airplane.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${airplane.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${airplane.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No Airplane_Name found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No Airplane_Name found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("AIRPLANE_NAME variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "AIRPLANE_NAME variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -268,23 +266,23 @@ class MainExtension01 {
if (ValidFile(val1.Path)) { if (ValidFile(val1.Path)) {
files.add(val1.Path) files.add(val1.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${val1.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${val1.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No Airline_Code found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return return Result_GetSoundbankFiles(false, "No Airline_Code found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
} }
if (val2 != null && val2.isNotEmpty()) { if (val2 != null && val2.isNotEmpty()) {
files.addAll(val2) files.addAll(val2)
} else { } else {
cbFail.accept("No valid soundbank files found for FLIGHT_NUMBER value '$fncode' for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No valid soundbank files found for FLIGHT_NUMBER value '$fncode' for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("AIRPLANE_NAME or FLIGHT_NUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "FLIGHT_NUMBER or AIRLINE_CODE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -321,12 +319,12 @@ class MainExtension01 {
} }
} }
} else { } else {
cbFail.accept("PLATNOMOR variable has invalid format for value '$value' in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "PLATNOMOR variable has invalid format for value '$value' in messagebank id ${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("PLATNOMOR variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "PLATNOMOR variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -339,17 +337,17 @@ class MainExtension01 {
if (ValidFile(city.Path)) { if (ValidFile(city.Path)) {
files.add(city.Path) files.add(city.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} }
} else { } else {
cbFail.accept("CITY variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "CITY variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -361,16 +359,16 @@ class MainExtension01 {
if (ValidFile(places.Path)) { if (ValidFile(places.Path)) {
files.add(places.Path) files.add(places.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${places.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${places.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No Places found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No Places found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("PLACES variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "PLACES variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -390,19 +388,19 @@ class MainExtension01 {
files.add(hh) files.add(hh)
files.add(mm) files.add(mm)
} else { } else {
cbFail.accept("ETAD variable has invalid soundbank for hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "ETAD variable has invalid soundbank for hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("ETAD variable has invalid hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}, hour must be 0-23 and minute must be 0-59") return Result_GetSoundbankFiles(false, "ETAD variable has invalid hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}, hour must be 0-23 and minute must be 0-59")
return
} }
} }
} else { } else {
cbFail.accept("ETAD variable has invalid format for tag $_tag in messagebank id ${mb.ANN_ID}, expected format HH:MM") return Result_GetSoundbankFiles(false, "ETAD variable has invalid format for tag $_tag in messagebank id ${mb.ANN_ID}, expected format HH:MM")
return
} }
} }
@@ -415,16 +413,16 @@ class MainExtension01 {
if (ValidFile(shalat.Path)) { if (ValidFile(shalat.Path)) {
files.add(shalat.Path) files.add(shalat.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${shalat.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${shalat.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No Shalat found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No Shalat found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("SHALAT variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "SHALAT variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -437,8 +435,8 @@ class MainExtension01 {
if (path != null) { if (path != null) {
files.addAll(path) files.addAll(path)
} else { } else {
cbFail.accept("BCB variable is missing, empty, or doesn't have valid soundbank for value '$value' in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "BCB variable doesn't have valid soundbank for value '$value' in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -454,17 +452,17 @@ class MainExtension01 {
if (path != null) { if (path != null) {
files.addAll(path) files.addAll(path)
} else { } else {
cbFail.accept("GATENUMBER variable doesn't have valid soundbank for value '$vv' in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "GATENUMBER variable doesn't have valid soundbank for value '$vv' in messagebank id ${mb.ANN_ID}")
return
} }
} }
} else { } else {
cbFail.accept("GATENUMBER variable is empty after split for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "GATENUMBER variable is empty after split for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("GATENUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "GATENUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -477,16 +475,16 @@ class MainExtension01 {
if (ValidFile(reason.Path)) { if (ValidFile(reason.Path)) {
files.add(reason.Path) files.add(reason.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${reason.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${reason.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No Reason found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No Reason found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("REASON variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "REASON variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -499,16 +497,16 @@ class MainExtension01 {
if (ValidFile(procedure.Path)) { if (ValidFile(procedure.Path)) {
files.add(procedure.Path) files.add(procedure.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${procedure.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${procedure.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No Procedure found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No Procedure found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("PROCEDURE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return Result_GetSoundbankFiles(false,"PROCEDURE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
return
} }
} }
@@ -520,12 +518,12 @@ class MainExtension01 {
if (ValidFile(phrase.Path)) { if (ValidFile(phrase.Path)) {
files.add(phrase.Path) files.add(phrase.Path)
} else { } else {
cbFail.accept("Invalid soundbank file ${phrase.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${phrase.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} else { } else {
cbFail.accept("No Phrase found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No Phrase found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
return
} }
} }
@@ -533,51 +531,34 @@ class MainExtension01 {
} }
} }
// all tags processed, return files // all tags processed, return files
cbOK.accept(files) return Result_GetSoundbankFiles(true, "Success", files)
} }
/** fun Read_Queue_Paging() : Boolean{
* Read and process Queue_Paging table.
*/
fun Read_Queue_Paging(){
db.queuepagingDB.Get() db.queuepagingDB.Get()
for (qp in db.queuepagingDB.List) { val list = db.queuepagingDB.List
.filter { it.Source=="PAGING" }
list.forEach { qp ->
println("Processing $qp") println("Processing $qp")
if (qp.BroadcastZones.isNotBlank()) { if (qp.BroadcastZones.isNotBlank()){
val zz = qp.BroadcastZones.split(";") if (ValidFile(qp.Message)){
if (AllBroadcastZonesValid(zz)) { val zz = qp.BroadcastZones.split(";")
val ips = BroadcastZones_to_SoundChannel_IP(zz) if (AllBroadcastZonesValid(zz)){
if (AllStreamerOutputIdle(ips)) { val ips = BroadcastZones_to_SoundChannel_IP(zz)
if (qp.Source == "PAGING") { if (AllStreamerOutputIdle(ips)){
// nama file ada di Message val afi = audioPlayer.LoadAudioFile(qp.Message)
if (ValidFile(qp.Message)) { if (afi.isValid()){
// file ketemu di path // file bisa di load, kirim ke masing masing Streamer Output by IP address
val afi = audioPlayer.LoadAudioFile(qp.Message) ips.forEach { ip -> StreamerOutputs[ip]?.SendData(afi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) }
if (afi.isValid()){
// file bisa di load, kirim ke masing masing Streamer Output by IP address
ips.forEach {
ip ->
val br = StreamerOutputs[ip]
br?.SendData(afi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) })
}
val logmessage = val logmessage =
"Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}" "Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}"
Logger.info { logmessage } Logger.info { logmessage }
db.Add_Log("AAS", logmessage) db.Add_Log("AAS", logmessage)
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
return
} else {
// file tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log(
"AAS",
"Cancelled paging message with index ${qp.index} due to invalid audio file"
)
}
return true
} else { } else {
// file tidak valid, delete from queue paging // file tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
@@ -586,101 +567,18 @@ class MainExtension01 {
"Cancelled paging message with index ${qp.index} due to invalid audio file" "Cancelled paging message with index ${qp.index} due to invalid audio file"
) )
} }
} else if (qp.Source == "SHALAT") { } // kalau enggak semua streamer idle, skip dulu
val ann_id = Get_ANN_ID(qp.Message) } else {
if (ann_id > 0) { // ada broadcast zone yang tidak valid, delete from queue paging
// shalat, ambil messagebank berdasarkan ann_id dengan bahasa Indonesia saja db.queuepagingDB.DeleteByIndex(qp.index.toInt())
Get_MessageBank_by_id(ann_id, listOf(Language.INDONESIA.name)).let { mblist -> db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to invalid broadcast zone")
if (mblist.isNotEmpty()) {
Get_Soundbank_Files(
mblist[0],
emptyMap(),
{
// dapat list dari files dan sudah dicek valid path
listfile ->
val listafi = mutableListOf<AudioFileInfo>()
listfile.forEach { filenya ->
val afi = contentCache.getAudioFile(filenya)
if (afi!=null && afi.isValid()){
listafi.add(afi)
} else {
val afi = audioPlayer.LoadAudioFile(filenya)
if (afi.isValid()) {
listafi.add(afi)
contentCache.addAudioFile(filenya, afi)
}
}
}
val targetfile = SoundbankResult_directory.resolve(
Make_WAV_FileName(
"Shalat",
""
)
).toString()
audioPlayer.WavWriter(
listafi,
targetfile, true,
) { success, message ->
db.Add_Log("AAS", message)
if (success) {
// file siap broadcast
val targetafi = audioPlayer.LoadAudioFile(targetfile)
if (targetafi.isValid()) {
zz.forEach { z1 ->
StreamerOutputs.values.find { it.channel == z1 }
?.SendData(
targetafi.bytes,
{ db.Add_Log("AAS", it) },
{ db.Add_Log("AAS", it) })
}
val logmsg =
"Broadcast started SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}"
Logger.info { logmsg }
db.Add_Log("AAS", logmsg)
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
} else {
db.Add_Log(
"AAS",
"Failed to load generated Shalat WAV file $targetfile"
)
}
}
}
},
{ err ->
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log("AAS", err)
}
)
} else {
// tidak ada messagebank dengan ann_id ini, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log(
"AAS",
"Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank"
)
}
}
} else {
// invalid ann_id, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log(
"AAS",
"Cancelled Shalat message with index ${qp.index} due to invalid ANN_ID"
)
}
}
} }
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue paging // file tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled paging message with index ${qp.index} due to invalid broadcast zone" "Cancelled paging message with index ${qp.index} due to invalid audio file"
) )
} }
} else { } else {
@@ -689,66 +587,129 @@ class MainExtension01 {
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone") db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone")
} }
} }
return false
} }
/** fun Read_Queue_Shalat() : Boolean{
* Read and process Queue_Table table. db.queuepagingDB.Get()
*/ val list = db.queuepagingDB.List
fun Read_Queue_Table(){ .filter { it.Source=="SHALAT" }
db.queuetableDB.Get() list.forEach { qp ->
db.queuetableDB.List.forEach { qa -> println("Processing $qp")
println("Processing $qa") if (qp.BroadcastZones.isNotBlank()){
if (qa.BroadcastZones.isNotEmpty()) { val zz = qp.BroadcastZones.split(";")
val zz = qa.BroadcastZones.split(";") if (AllBroadcastZonesValid(zz)){
val ann_id = Get_ANN_ID(qp.Message)
if (ann_id>0){
val ips = BroadcastZones_to_SoundChannel_IP(zz)
if (AllStreamerOutputIdle(ips)){
val mblist = Get_MessageBank_by_id(ann_id, listOf(Language.INDONESIA.name))
if (mblist.isNotEmpty()) {
mblist.forEach { mb ->
val result = Get_Soundbank_Files(mb, emptyMap())
if (result.success){
val listafi = mutableListOf<AudioFileInfo>()
result.files.forEach { filenya ->
val afi = contentCache.getAudioFile(filenya)
if (afi!=null && afi.isValid()){
listafi.add(afi)
} else {
val afi = audioPlayer.LoadAudioFile(filenya)
if (afi.isValid()) {
listafi.add(afi)
contentCache.addAudioFile(filenya, afi)
}
}
if (AllBroadcastZonesValid(zz)) {
val ips = BroadcastZones_to_SoundChannel_IP(zz) }
if (AllStreamerOutputIdle(ips)) { val targetfile = SoundbankResult_directory.resolve(
if (qa.Type == "SOUNDBANK") { Make_WAV_FileName(
val variables = Get_Soundbank_Data(qa.SB_TAGS) "Shalat",
val languages = qa.Language.split(";") ""
// cek apakah ANN_ID ada di SB_TAGS )
val ann_id = variables?.get("ANN_ID")?.toIntOrNull() ?: 0 ).toString()
if (ann_id==0){
// not available from variables, try to get from Message column val result =audioPlayer.WavWriter(
// ada ini, karena protokol FIS dulu tidak ada ANN_ID tapi pake Remark listafi,
val remark = variables?.get("REMARK").orEmpty() targetfile, true,
when(remark){ )
"GOP" -> { db.Add_Log("AAS", result.message)
//TODO Combobox First_Call_Message_Chooser if (result.success) {
} // file siap broadcast
"GBD" ->{ val targetafi = audioPlayer.LoadAudioFile(targetfile)
// TODO Combobox Second_Call_Message_Chooser if (targetafi.isValid()) {
} ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, {db.Add_Log("AAS", it) }) }
"GFC" ->{
// TODO Combobox Final_Call_Message_Chooser val logmsg =
} "Broadcast started SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}"
"FLD" ->{ Logger.info { logmsg }
// TODO Combobox Landed_Message_Chooser db.Add_Log("AAS", logmsg)
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
return true
} else {
db.Add_Log(
"AAS",
"Failed to load generated Shalat WAV file $targetfile"
)
}
}
} else{
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log("AAS", result.message)
} }
} }
}
// recheck again
if (ann_id == 0) { } else {
// tidak ada messagebank dengan ann_id ini, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled SOUNDBANK message with index ${qa.index} due to missing or invalid ANN_ID in SB_TAGS" "Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank"
) )
db.queuetableDB.DeleteByIndex(qa.index.toInt())
return@forEach
} }
// sampe sini punya ann_id valid } // kalau enggak semua streamer idle, skip dulu
val mblist = Get_MessageBank_by_id(ann_id, languages) } else {
// invalid ann_id, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid ANN_ID")
}
} else {
// ada broadcast zone yang tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid broadcast zone")
}
} else {
// invalid broadcast zone, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to empty broadcast zone")
}
}
return false
}
fun Read_Queue_Timer() : Boolean{
db.queuetableDB.Get()
val list = db.queuetableDB.List.filter { it.Type=="TIMER" }
list.forEach { qa ->
println("Processing $qa")
if (qa.BroadcastZones.isNotEmpty()){
val zz = qa.BroadcastZones.split(";")
if (AllBroadcastZonesValid(zz)){
val ips = BroadcastZones_to_SoundChannel_IP(zz)
val ann_id = Get_ANN_ID(qa.Message)
if (ann_id>0){
if (AllStreamerOutputIdle(ips)){
val mblist = Get_MessageBank_by_id(ann_id, qa.Language.split(";"))
if (mblist.isNotEmpty()) { if (mblist.isNotEmpty()) {
val listafi = mutableListOf<AudioFileInfo>() mblist.forEach {
mb ->
mblist.forEach { mb -> val result = Get_Soundbank_Files(mb, emptyMap())
Get_Soundbank_Files(mb, variables ?: emptyMap(), { if (result.success){
listfile -> val listafi = mutableListOf<AudioFileInfo>()
listfile.forEach { filenya -> result.files.forEach { filenya ->
val afi = contentCache.getAudioFile(filenya) val afi = contentCache.getAudioFile(filenya)
if (afi!=null && afi.isValid()){ if (afi!=null && afi.isValid()){
listafi.add(afi) listafi.add(afi)
@@ -761,144 +722,190 @@ class MainExtension01 {
} }
} }
}, val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Timer","")).toString()
{ val result = audioPlayer.WavWriter(listafi, targetfile, true)
err -> db.Add_Log("AAS", result.message)
db.Add_Log("AAS", err) if (result.success){
db.queuetableDB.DeleteByIndex(qa.index.toInt())
})
}
if (listafi.isNotEmpty()){
db.queuetableDB.DeleteByIndex(qa.index.toInt())
val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Soundbank","")).toString()
println("Writing to target WAV file: $targetfile")
audioPlayer.WavWriter(listafi, targetfile, true,
) { success, message ->
if (success) {
// file siap broadcast // file siap broadcast
println("Successfully wrote WAV file: $targetfile")
val targetafi = audioPlayer.LoadAudioFile(targetfile) val targetafi = audioPlayer.LoadAudioFile(targetfile)
if (targetafi.isValid()) { if (targetafi.isValid()) {
ips.forEach { ip -> ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) }
StreamerOutputs[ip].let{ sc ->
sc?.SendData(targetafi.bytes,
{ db.Add_Log("AAS", it) },
{ db.Add_Log("AAS", it) } )
}
}
val logmsg = val logmsg =
"Broadcast started SOUNDBANK message with generated file '$targetfile' to zones: ${qa.BroadcastZones}" "Broadcast started TIMER message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
Logger.info { logmsg } Logger.info { logmsg }
db.Add_Log("AAS", logmsg) db.Add_Log("AAS", logmsg)
db.queuetableDB.DeleteByIndex(qa.index.toInt())
return true
} }
} else { } else{
println("Failed to write WAV file: $message")
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log("AAS", message) db.Add_Log("AAS", result.message)
} }
} else {
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log("AAS", result.message)
} }
} }
} else { } else {
// tidak ada messagebank dengan ann_id ini, delete from queue table // tidak ada messagebank dengan ann_id ini, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank" "Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
) )
} }
} else if (qa.Type == "TIMER") { } // kalau enggak semua streamer idle, skip dulu
val ann_id = Get_ANN_ID(qa.SB_TAGS)
if (ann_id > 0) {
val mblist = Get_MessageBank_by_id(ann_id, qa.Language.split(";"))
if (mblist.isNotEmpty()) {
mblist.forEach {
mb ->
Get_Soundbank_Files(mb, emptyMap(), {
listfile ->
val listafi = mutableListOf<AudioFileInfo>()
listfile.forEach { filenya ->
val afi = contentCache.getAudioFile(filenya)
if (afi!=null && afi.isValid()){
listafi.add(afi)
} else {
val afi = audioPlayer.LoadAudioFile(filenya)
if (afi.isValid()) {
listafi.add(afi)
contentCache.addAudioFile(filenya, afi)
}
}
} } else {
val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Timer","")).toString() // invalid ann_id, delete from queue table
audioPlayer.WavWriter(listafi, targetfile, true, db.queuetableDB.DeleteByIndex(qa.index.toInt())
) { success, message -> db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid ANN_ID")
if (success) {
// file siap broadcast
val targetafi = audioPlayer.LoadAudioFile(targetfile)
if (targetafi.isValid()) {
zz.forEach { z1 ->
StreamerOutputs.values.find { it.channel == z1 }
?.SendData(
targetafi.bytes,
{ db.Add_Log("AAS", it) },
{ db.Add_Log("AAS", it) })
}
val logmsg =
"Broadcast started TIMER message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
Logger.info { logmsg }
db.Add_Log("AAS", logmsg)
db.queuetableDB.DeleteByIndex(qa.index.toInt())
}
}
db.Add_Log("AAS", message)
}
},
{
err ->
db.Add_Log("AAS", err)
db.queuetableDB.DeleteByIndex(qa.index.toInt())
})
}
} else {
// tidak ada messagebank dengan ann_id ini, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log(
"AAS",
"Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
)
}
} else {
// invalid ann_id, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log(
"AAS",
"Cancelled TIMER with index ${qa.index} due to invalid ANN_ID"
)
}
}
} }
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue table // ada broadcast zone yang tidak valid, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log( db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid broadcast zone")
"AAS",
"Cancelled table message with index ${qa.index} due to invalid broadcast zone"
)
} }
} else { } else {
// invalid broadcast zone, delete from queue table // invalid broadcast zone, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log("AAS", "Cancelled table message with index ${qa.index} due to empty broadcast zone") db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to empty broadcast zone")
} }
} }
return false
} }
fun Read_Queue_Soundbank() : Boolean{
db.queuetableDB.Get()
val list = db.queuetableDB.List.filter { it.Type=="SOUNDBANK" }
list.forEach { qa ->
println("Processing $qa")
if (qa.BroadcastZones.isNotEmpty()){
val zz = qa.BroadcastZones.split(";")
if (AllBroadcastZonesValid(zz)){
val ips = BroadcastZones_to_SoundChannel_IP(zz)
if (AllStreamerOutputIdle(ips)) {
val variables = Get_Soundbank_Data(qa.SB_TAGS)
val languages = qa.Language.split(";")
// cek apakah ANN_ID ada di SB_TAGS
val ann_id = variables?.get("ANN_ID")?.toIntOrNull() ?: 0
if (ann_id==0){
// not available from variables, try to get from Message column
// ada ini, karena protokol FIS dulu tidak ada ANN_ID tapi pake Remark
val remark = variables?.get("REMARK").orEmpty()
when(remark){
"GOP" -> {
//TODO Combobox First_Call_Message_Chooser
}
"GBD" ->{
// TODO Combobox Second_Call_Message_Chooser
}
"GFC" ->{
// TODO Combobox Final_Call_Message_Chooser
}
"FLD" ->{
// TODO Combobox Landed_Message_Chooser
}
}
}
// recheck again
if (ann_id == 0) {
db.Add_Log(
"AAS",
"Cancelled SOUNDBANK message with index ${qa.index} due to missing or invalid ANN_ID in SB_TAGS"
)
db.queuetableDB.DeleteByIndex(qa.index.toInt())
return@forEach
}
// sampe sini punya ann_id valid
val mblist = Get_MessageBank_by_id(ann_id, languages)
if (mblist.isNotEmpty()) {
val listafi = mutableListOf<AudioFileInfo>()
mblist.forEach { mb ->
val result = Get_Soundbank_Files(mb, variables ?: emptyMap())
if (result.success){
result.files.forEach { filenya ->
val afi = contentCache.getAudioFile(filenya)
if (afi!=null && afi.isValid()){
listafi.add(afi)
} else {
val afi = audioPlayer.LoadAudioFile(filenya)
if (afi.isValid()) {
listafi.add(afi)
contentCache.addAudioFile(filenya, afi)
}
}
}
} else {
db.Add_Log("AAS", result.message)
db.queuetableDB.DeleteByIndex(qa.index.toInt())
}
}
if (listafi.isNotEmpty()){
db.queuetableDB.DeleteByIndex(qa.index.toInt())
val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Soundbank","")).toString()
println("Writing to target WAV file: $targetfile")
val result = audioPlayer.WavWriter(listafi, targetfile, true)
if (result.success){
// file siap broadcast
println("Successfully wrote WAV file: $targetfile")
val targetafi = audioPlayer.LoadAudioFile(targetfile)
if (targetafi.isValid()) {
ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) }
val logmsg =
"Broadcast started SOUNDBANK message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
Logger.info { logmsg }
db.Add_Log("AAS", logmsg)
return true
}
} else {
println("Failed to write WAV file: ${result.message}")
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log("AAS", result.message)
}
}
} else {
// tidak ada messagebank dengan ann_id ini, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log(
"AAS",
"Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
)
}
}
} else {
// ada broadcast zone yang tidak valid, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to invalid broadcast zone")
}
} else {
// invalid broadcast zone, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to empty broadcast zone")
}
}
return false
}
/** /**
* Read and process Schedule_Table table. * Read and process Schedule_Table table.
* This function is called every minute when second=00. * This function is called every minute when second=00.

View File

@@ -6,17 +6,14 @@ import audio.Bass.BASS_POS_BYTE
import audio.Bass.BASS_STREAM_DECODE import audio.Bass.BASS_STREAM_DECODE
import audio.Bass.BASS_SAMPLE_MONO import audio.Bass.BASS_SAMPLE_MONO
import audio.BassEnc.BASS_ENCODE_PCM import audio.BassEnc.BASS_ENCODE_PCM
import codes.Result_Boolean_String
import codes.Somecodes.Companion.ValidFile import codes.Somecodes.Companion.ValidFile
import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.ValidString
import com.sun.jna.Memory import com.sun.jna.Memory
import com.sun.jna.Pointer import com.sun.jna.Pointer
import contentCache import contentCache
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.tinylog.Logger import org.tinylog.Logger
import java.util.function.BiConsumer
@Suppress("unused") @Suppress("unused")
class AudioPlayer (var samplingrate: Int) { class AudioPlayer (var samplingrate: Int) {
@@ -131,84 +128,87 @@ class AudioPlayer (var samplingrate: Int) {
* @param data The byte array containing the audio data. * @param data The byte array containing the audio data.
* @param target The target file name for the WAV file. * @param target The target file name for the WAV file.
* @param withChime If true, adds a chime sound at the beginning and end of the audio. * @param withChime If true, adds a chime sound at the beginning and end of the audio.
* @param callback A BiConsumer that accepts a Boolean indicating success or failure and a String message. * @return A Result_Boolean_String indicating success or failure and a message.
*/ */
fun WavWriter(data: ByteArray, target: String, withChime: Boolean = true, callback: BiConsumer<Boolean, String>) { fun WavWriter(data: ByteArray, target: String, withChime: Boolean = true) : Result_Boolean_String {
CoroutineScope(Dispatchers.IO).launch { bass.BASS_SetDevice(0) // Set to No Sound device for writing
bass.BASS_SetDevice(0) // Set to No Sound device for writing val streamhandle = bass.BASS_StreamCreate(samplingrate, 1, BASS_STREAM_DECODE, Pointer(-1), null)
val streamhandle = bass.BASS_StreamCreate(samplingrate, 1, BASS_STREAM_DECODE, Pointer(-1), null)
if (streamhandle!=0){ if (streamhandle!=0){
val encodehandle = bassenc.BASS_Encode_Start(streamhandle, target, BASS_ENCODE_PCM, null, null) val encodehandle = bassenc.BASS_Encode_Start(streamhandle, target, BASS_ENCODE_PCM, null, null)
if (encodehandle!=0){ if (encodehandle!=0){
fun pushData(data: ByteArray): Boolean { fun pushData(data: ByteArray): Boolean {
val mem = Memory(data.size.toLong()) val mem = Memory(data.size.toLong())
mem.write(0, data, 0, data.size) mem.write(0, data, 0, data.size)
val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size) val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size)
if (pushresult==-1){ if (pushresult==-1){
val errcode = bass.BASS_ErrorGetCode() val errcode = bass.BASS_ErrorGetCode()
println("BASS_StreamPutData failed: $errcode") println("BASS_StreamPutData failed: $errcode")
}
return pushresult != -1
}
var all_success = true
if (withChime){
val chup = contentCache.getAudioFile("chimeup")
if (chup!=null && chup.isValid()){
if (pushData(chup.bytes)){
println("Chime up pushed")
} else {
all_success = false
println("Chime up failed")
} }
return pushresult != -1 } else println("Chime Up not valid")
} }
var all_success = true if (pushData(data)){
println("Data pushed")
} else {
all_success = false
println("Data push failed")
}
if (withChime){ if (withChime){
val chup = contentCache.getAudioFile("chimeup") val chdn = contentCache.getAudioFile("chimedown")
if (chup!=null && chup.isValid()){ if (chdn!=null && chdn.isValid()){
if (pushData(chup.bytes)){ if (pushData(chdn.bytes)){
println("Chime up pushed") println("Chime down pushed")
} else { } else {
all_success = false all_success = false
println("Chime up failed") println("Chime down failed")
}
} else println("Chime Up not valid")
}
if (pushData(data)){
println("Data pushed")
} else {
all_success = false
println("Data push failed")
}
if (withChime){
val chdn = contentCache.getAudioFile("chimedown")
if (chdn!=null && chdn.isValid()){
if (pushData(chdn.bytes)){
println("Chime down pushed")
} else {
all_success = false
println("Chime down failed")
}
} else println("Chime Down not valid")
}
val readsize: Long = 1024 * 1024 // read 1 MB at a time
var totalread: Long = 0
do{
val p = Memory(readsize)
val read = bass.BASS_ChannelGetData(streamhandle, p, 4096)
if (read > 0) {
totalread += read
} }
} while (read > 0) } else println("Chime Down not valid")
println("Finished reading stream data, total $totalread bytes read") }
if (all_success){ val readsize: Long = 1024 * 1024 // read 1 MB at a time
callback.accept(true, "WAV file written successfully: $target") var totalread: Long = 0
} else { do{
callback.accept(false, "Failed to write some data to WAV file: $target") val p = Memory(readsize)
val read = bass.BASS_ChannelGetData(streamhandle, p, 4096)
if (read > 0) {
totalread += read
} }
} while (read > 0)
bassenc.BASS_Encode_Stop(encodehandle) println("Finished reading stream data, total $totalread bytes read")
} else callback.accept(false, "Failed to start encoding: ${bass.BASS_ErrorGetCode()}") bassenc.BASS_Encode_Stop(encodehandle)
bass.BASS_StreamFree(streamhandle) bass.BASS_StreamFree(streamhandle)
return if (all_success){
Result_Boolean_String(true, "WAV file written successfully: $target")
} else {
Result_Boolean_String(false, "Failed to write some data to WAV file: $target")
}
} else { } else {
callback.accept(false, "Failed to create stream: ${bass.BASS_ErrorGetCode()}") bass.BASS_StreamFree(streamhandle)
return Result_Boolean_String(false, "Failed to start encoding: ${bass.BASS_ErrorGetCode()}")
} }
} else {
return Result_Boolean_String(false, "Failed to create stream: ${bass.BASS_ErrorGetCode()}")
} }
@@ -220,107 +220,102 @@ class AudioPlayer (var samplingrate: Int) {
* @param sources List of AudioFileInfo objects containing the audio data to write. * @param sources List of AudioFileInfo objects containing the audio data to write.
* @param target The target file name for the WAV file. * @param target The target file name for the WAV file.
* @param withChime If true, adds a chime sound at the beginning and end of the audio. * @param withChime If true, adds a chime sound at the beginning and end of the audio.
* @param callback A BiConsumer that accepts a Boolean indicating success or failure and a String message. * @return A Result_Boolean_String indicating success or failure and a message.
*/ */
fun WavWriter(sources: List<AudioFileInfo>, target: String, withChime: Boolean = true, callback: BiConsumer<Boolean, String>) { fun WavWriter(sources: List<AudioFileInfo>, target: String, withChime: Boolean = true) : Result_Boolean_String {
if (sources.isEmpty()) { if (sources.isEmpty()) {
callback.accept(false, " Invalid sources") return Result_Boolean_String(false,"Invalid Source")
return
} }
if (!ValidString(target)) { if (!ValidString(target)) {
callback.accept(false, " Invalid target file name") return Result_Boolean_String(false, " Invalid target file name")
return }
bass.BASS_SetDevice(0) // Set to No Sound device for writing
val streamhandle = bass.BASS_StreamCreate(samplingrate, 1, BASS_STREAM_DECODE, Pointer(-1), null)
if (streamhandle==0){
return Result_Boolean_String(false, "Failed to create stream: ${bass.BASS_ErrorGetCode()}")
}
val encodehandle = bassenc.BASS_Encode_Start(streamhandle, target, BASS_ENCODE_PCM, null, null)
if (encodehandle==0){
bass.BASS_StreamFree(streamhandle)
return Result_Boolean_String(false, "Failed to start encoding: ${bass.BASS_ErrorGetCode()}")
}
fun pushData(data: ByteArray): Boolean {
val mem = Memory(data.size.toLong())
mem.write(0, data, 0, data.size)
val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size)
if (pushresult==-1){
val errcode = bass.BASS_ErrorGetCode()
println("BASS_StreamPutData failed: $errcode")
}
return pushresult != -1
} }
CoroutineScope(Dispatchers.IO).launch(CoroutineName("WavWriter $target")) {
bass.BASS_SetDevice(0) // Set to No Sound device for writing
val streamhandle = bass.BASS_StreamCreate(samplingrate, 1, BASS_STREAM_DECODE, Pointer(-1), null)
if (streamhandle==0){
callback.accept(false, "Failed to create stream: ${bass.BASS_ErrorGetCode()}")
return@launch
}
val encodehandle = bassenc.BASS_Encode_Start(streamhandle, target, BASS_ENCODE_PCM, null, null)
if (encodehandle==0){
bass.BASS_StreamFree(streamhandle)
callback.accept(false, "Failed to start encoding: ${bass.BASS_ErrorGetCode()}")
return@launch
}
fun pushData(data: ByteArray): Boolean { var allsuccess = true
val mem = Memory(data.size.toLong()) if (withChime){
mem.write(0, data, 0, data.size) val chup = contentCache.getAudioFile("chimeup")
val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size) if (chup!=null && chup.isValid()){
if (pushresult==-1){ if (pushData(chup.bytes)){
val errcode = bass.BASS_ErrorGetCode() println("Chime up pushed")
println("BASS_StreamPutData failed: $errcode")
}
return pushresult != -1
}
var allsuccess = true
if (withChime){
val chup = contentCache.getAudioFile("chimeup")
if (chup!=null && chup.isValid()){
if (pushData(chup.bytes)){
println("Chime up pushed")
} else {
allsuccess = false
println("Chime up failed")
}
} else println("Chime Up not valid")
}
sources.forEach { source ->
if (source.isValid()) {
// write the bytes to the stream
if (pushData(source.bytes)){
println("Source ${source.fileName} pushed")
} else {
allsuccess = false
println("Source ${source.fileName} push failed")
}
} else { } else {
allsuccess = false allsuccess = false
println("Source ${source.fileName} is not valid") println("Chime up failed")
} }
} else println("Chime Up not valid")
}
if (withChime){
val chdn = contentCache.getAudioFile("chimedown")
if (chdn!=null && chdn.isValid()){
if (pushData(chdn.bytes)){
println("Chime down pushed")
} else {
allsuccess = false
println("Chime down failed")
}
} else println("Chime Down not valid")
}
val readsize: Long = 1024 * 1024 // read 1 MB at a time
var totalread: Long = 0
do{
val p = Memory(readsize)
val read = bass.BASS_ChannelGetData(streamhandle, p, 4096)
if (read > 0) {
totalread += read
}
} while (read > 0)
println("Finished reading stream data, total $totalread bytes read")
// close the encoding handle
bassenc.BASS_Encode_Stop(encodehandle)
bass.BASS_ChannelFree(streamhandle)
if (allsuccess){
callback.accept(true, "WAV file written successfully: $target")
} else {
callback.accept(false, "Failed to write some data to WAV file: $target")
}
} }
sources.forEach { source ->
if (source.isValid()) {
// write the bytes to the stream
if (pushData(source.bytes)){
println("Source ${source.fileName} pushed")
} else {
allsuccess = false
println("Source ${source.fileName} push failed")
}
} else {
allsuccess = false
println("Source ${source.fileName} is not valid")
}
}
if (withChime){
val chdn = contentCache.getAudioFile("chimedown")
if (chdn!=null && chdn.isValid()){
if (pushData(chdn.bytes)){
println("Chime down pushed")
} else {
allsuccess = false
println("Chime down failed")
}
} else println("Chime Down not valid")
}
val readsize: Long = 1024 * 1024 // read 1 MB at a time
var totalread: Long = 0
do{
val p = Memory(readsize)
val read = bass.BASS_ChannelGetData(streamhandle, p, 4096)
if (read > 0) {
totalread += read
}
} while (read > 0)
println("Finished reading stream data, total $totalread bytes read")
// close the encoding handle
bassenc.BASS_Encode_Stop(encodehandle)
bass.BASS_ChannelFree(streamhandle)
return if (allsuccess){
Result_Boolean_String(true, "WAV file written successfully: $target")
} else {
Result_Boolean_String(false, "Failed to write some data to WAV file: $target")
}
} }

View File

@@ -0,0 +1,4 @@
package codes
class Result_Boolean_String(val success: Boolean, val message: String) {
}

View File

@@ -0,0 +1,3 @@
package codes
class Result_GetSoundbankFiles(val success: Boolean, val message: String , val files: List<String> = emptyList())

View File

@@ -60,25 +60,32 @@ class TCP_Android_Command_Server {
Logger.info { "Start communicating with IPMT/IPM with IP $key" } Logger.info { "Start communicating with IPMT/IPM with IP $key" }
val din = socket.getInputStream() val din = socket.getInputStream()
val dout = socket.getOutputStream() val dout = socket.getOutputStream()
while (isActive) { try{
if (din.available() > 0) { while (isActive) {
val bb = ByteArray(din.available()) if (din.available() > 0) {
din.read(bb) val bb = ByteArray(din.available())
// B4A format, 4 bytes di depan adalah size din.read(bb)
val str = String(bb, 4, bb.size - 4) // B4A format, 4 bytes di depan adalah size
str.split("@").map { it.trim() }.filter { ValidString(it) } val str = String(bb, 4, bb.size - 4)
.forEach { println("Received command from $key : $str")
process_command(key,it) { reply -> str.split("@").map { it.trim() }.filter { ValidString(it) }
try { .forEach {
dout.write(String_to_Byte_Android(reply)) process_command(key,it) { reply ->
} catch (e: Exception) { try {
logcb.accept("Failed to send reply to $key, Message : $e") dout.write(String_to_Byte_Android(reply))
} catch (e: Exception) {
logcb.accept("Failed to send reply to $key, Message : $e")
}
} }
} }
} }
} }
} catch (e : Exception){
logcb.accept("Exception in communication with $key, Message : ${e.message}")
} }
logcb.accept("Finished communicatiing with $key") logcb.accept("Finished communicatiing with $key")
CloseSocket(socket)
socketMap.remove(key) socketMap.remove(key)
} }
@@ -98,6 +105,14 @@ class TCP_Android_Command_Server {
return false return false
} }
private fun CloseSocket(socket : Socket) {
try {
socket.close()
} catch (e: Exception) {
Logger.error { "Failed to close socket, Message : ${e.message}" }
}
}
/** /**
* Convert a String to ByteArray in prefix AsyncStream format in B4X * Convert a String to ByteArray in prefix AsyncStream format in B4X
* @param str The input string * @param str The input string
@@ -184,33 +199,30 @@ class TCP_Android_Command_Server {
val data = pj.GetData() val data = pj.GetData()
pj.Close() pj.Close()
Logger.info{"Paging job closed from Android $key, total bytes received ${data.size}, writing to file ${pj.filePath.absolutePathString()}"} Logger.info{"Paging job closed from Android $key, total bytes received ${data.size}, writing to file ${pj.filePath.absolutePathString()}"}
audioPlayer.WavWriter(data, pj.filePath.absolutePathString(), true){ val result = audioPlayer.WavWriter(data, pj.filePath.absolutePathString(), true)
success, message -> if (result.success){
if (success){ val qp = QueuePaging(
// insert to paging queue 0u,
LocalDateTime.now().format(datetimeformat1),
val qp = QueuePaging( "PAGING",
0u, "NORMAL",
LocalDateTime.now().format(datetimeformat1), pj.filePath.absolutePathString(),
"PAGING", pj.broadcastzones
"NORMAL", )
pj.filePath.absolutePathString(), if (db.queuepagingDB.Add(qp)){
pj.broadcastzones logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
) cb.accept(parts[0]+";OK@")
if (db.queuepagingDB.Add(qp)){
logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
cb.accept(parts[0]+";OK@")
} else {
logcb.accept("Failed to insert paging audio to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
cb.accept(parts[0]+";NG@")
}
} else { } else {
logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : $message") logcb.accept("Failed to insert paging audio to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
cb.accept(parts[0]+";NG@") cb.accept(parts[0]+";NG@")
} }
} else {
logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : ${result.message}")
cb.accept(parts[0]+";NG@")
return
} }
} else { } else {
logcb.accept("Paging stop from Android $key failed, no ongoing paging") logcb.accept("Paging stop from Android $key failed, no ongoing paging")
cb.accept(parts[0]+";NG@") cb.accept(parts[0]+";NG@")