From e8695c7a6f0ec031ff4e6c53d8724ec2d1776926 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Tue, 7 Oct 2025 15:57:23 +0700 Subject: [PATCH] commit 07/10/2025 --- src/Main.kt | 8 +- src/MainExtension01.kt | 727 +++++++++--------- src/audio/AudioPlayer.kt | 315 ++++---- src/codes/Result_Boolean_String.kt | 4 + src/codes/Result_GetSoundbankFiles.kt | 3 + .../TCP_Android_Command_Server.kt | 84 +- 6 files changed, 583 insertions(+), 558 deletions(-) create mode 100644 src/codes/Result_Boolean_String.kt create mode 100644 src/codes/Result_GetSoundbankFiles.kt diff --git a/src/Main.kt b/src/Main.kt index c2ee700..42e6596 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -109,8 +109,12 @@ fun main() { delay(1000) // prioritas 1 , habisin queue paging subcode01.Read_Queue_Paging() - // prioritas 2, habisin queue table - subcode01.Read_Queue_Table() + // prioritas 2, habisin queue shalat + 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 diff --git a/src/MainExtension01.kt b/src/MainExtension01.kt index 14a627a..8599bbe 100644 --- a/src/MainExtension01.kt +++ b/src/MainExtension01.kt @@ -1,4 +1,5 @@ import audio.AudioFileInfo +import codes.Result_GetSoundbankFiles import codes.Somecodes.Companion.Get_ANN_ID import codes.Somecodes.Companion.IsAlphabethic import codes.Somecodes.Companion.IsNumber @@ -22,7 +23,6 @@ import java.time.DayOfWeek import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime -import java.util.function.Consumer /** * 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 * @param mb Messagebank object * @param variables Map of variables to replace in tags. - * @param cbOK Callback function if success, returns List of soundbank file names - * @param cbFail Callback function if failed, returns error message + * @return Result_GetSoundbankFiles object containing success status, message, and list of soundbank file paths. */ fun Get_Soundbank_Files( mb: Messagebank, - variables: Map, - cbOK: Consumer>, - cbFail: Consumer - ) { + variables: Map + + ) : Result_GetSoundbankFiles { val tags = mb.Message_TAGS.split(" ") if (tags.isEmpty()) { - cbFail.accept("No tags found in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No tags found in messagebank id ${mb.ANN_ID}") + } // dapatkan soundbank array berdasarkan VoiceType dan Language val sb = db.soundDB.List .filter { it.VoiceType.equals(mb.Voice_Type, true) } .filter { it.Language.equals(mb.Language, true) } if (sb.isEmpty()) { - cbFail.accept("No soundbank found for voice type ${mb.Voice_Type} and language ${mb.Language}") - return + return Result_GetSoundbankFiles(false, "No soundbank found for voice type ${mb.Voice_Type} and language ${mb.Language}") + } val files = mutableListOf() @@ -244,16 +242,16 @@ class MainExtension01 { if (ValidFile(airplane.Path)) { files.add(airplane.Path) } else { - cbFail.accept("Invalid soundbank file ${airplane.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${airplane.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("No Airplane_Name found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No Airplane_Name found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("AIRPLANE_NAME variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "AIRPLANE_NAME variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -268,23 +266,23 @@ class MainExtension01 { if (ValidFile(val1.Path)) { files.add(val1.Path) } else { - cbFail.accept("Invalid soundbank file ${val1.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${val1.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } 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()) { files.addAll(val2) } 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 + 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}") + } } else { - cbFail.accept("AIRPLANE_NAME or FLIGHT_NUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "FLIGHT_NUMBER or AIRLINE_CODE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -321,12 +319,12 @@ class MainExtension01 { } } } else { - cbFail.accept("PLATNOMOR variable has invalid format for value '$value' in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "PLATNOMOR variable has invalid format for value '$value' in messagebank id ${mb.ANN_ID}") + } } else { - cbFail.accept("PLATNOMOR variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "PLATNOMOR variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -339,17 +337,17 @@ class MainExtension01 { if (ValidFile(city.Path)) { files.add(city.Path) } else { - cbFail.accept("Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } } else { - cbFail.accept("CITY variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "CITY variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -361,16 +359,16 @@ class MainExtension01 { if (ValidFile(places.Path)) { files.add(places.Path) } else { - cbFail.accept("Invalid soundbank file ${places.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${places.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("No Places found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No Places found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("PLACES variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "PLACES variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -390,19 +388,19 @@ class MainExtension01 { files.add(hh) files.add(mm) } else { - cbFail.accept("ETAD variable has invalid soundbank for hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "ETAD variable has invalid soundbank for hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}") + } } 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 + 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") + } } } else { - cbFail.accept("ETAD variable has invalid format for tag $_tag in messagebank id ${mb.ANN_ID}, expected format HH:MM") - return + return Result_GetSoundbankFiles(false, "ETAD variable has invalid format for tag $_tag in messagebank id ${mb.ANN_ID}, expected format HH:MM") + } } @@ -415,16 +413,16 @@ class MainExtension01 { if (ValidFile(shalat.Path)) { files.add(shalat.Path) } else { - cbFail.accept("Invalid soundbank file ${shalat.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${shalat.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("No Shalat found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No Shalat found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("SHALAT variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "SHALAT variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -437,8 +435,8 @@ class MainExtension01 { if (path != null) { files.addAll(path) } else { - cbFail.accept("BCB variable is missing, empty, or doesn't have valid soundbank for value '$value' in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "BCB variable doesn't have valid soundbank for value '$value' in messagebank id ${mb.ANN_ID}") + } } @@ -454,17 +452,17 @@ class MainExtension01 { if (path != null) { files.addAll(path) } else { - cbFail.accept("GATENUMBER variable doesn't have valid soundbank for value '$vv' in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "GATENUMBER variable doesn't have valid soundbank for value '$vv' in messagebank id ${mb.ANN_ID}") + } } } else { - cbFail.accept("GATENUMBER variable is empty after split for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "GATENUMBER variable is empty after split for tag $_tag in messagebank id ${mb.ANN_ID}") + } } else { - cbFail.accept("GATENUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "GATENUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -477,16 +475,16 @@ class MainExtension01 { if (ValidFile(reason.Path)) { files.add(reason.Path) } else { - cbFail.accept("Invalid soundbank file ${reason.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${reason.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("No Reason found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No Reason found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("REASON variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "REASON variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -499,16 +497,16 @@ class MainExtension01 { if (ValidFile(procedure.Path)) { files.add(procedure.Path) } else { - cbFail.accept("Invalid soundbank file ${procedure.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${procedure.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("No Procedure found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No Procedure found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("PROCEDURE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false,"PROCEDURE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") + } } @@ -520,12 +518,12 @@ class MainExtension01 { if (ValidFile(phrase.Path)) { files.add(phrase.Path) } else { - cbFail.accept("Invalid soundbank file ${phrase.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "Invalid soundbank file ${phrase.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } else { - cbFail.accept("No Phrase found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") - return + return Result_GetSoundbankFiles(false, "No Phrase found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") + } } @@ -533,51 +531,34 @@ class MainExtension01 { } } // all tags processed, return files - cbOK.accept(files) - + return Result_GetSoundbankFiles(true, "Success", files) } - /** - * Read and process Queue_Paging table. - */ - fun Read_Queue_Paging(){ + fun Read_Queue_Paging() : Boolean{ db.queuepagingDB.Get() - for (qp in db.queuepagingDB.List) { + val list = db.queuepagingDB.List + .filter { it.Source=="PAGING" } + + list.forEach { qp -> println("Processing $qp") - if (qp.BroadcastZones.isNotBlank()) { - val zz = qp.BroadcastZones.split(";") - if (AllBroadcastZonesValid(zz)) { - val ips = BroadcastZones_to_SoundChannel_IP(zz) - if (AllStreamerOutputIdle(ips)) { - if (qp.Source == "PAGING") { - // nama file ada di Message - if (ValidFile(qp.Message)) { - // file ketemu di path - val afi = audioPlayer.LoadAudioFile(qp.Message) - 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) }) - } + if (qp.BroadcastZones.isNotBlank()){ + if (ValidFile(qp.Message)){ + val zz = qp.BroadcastZones.split(";") + if (AllBroadcastZonesValid(zz)){ + val ips = BroadcastZones_to_SoundChannel_IP(zz) + if (AllStreamerOutputIdle(ips)){ + val afi = audioPlayer.LoadAudioFile(qp.Message) + if (afi.isValid()){ + // file bisa di load, kirim ke masing masing Streamer Output by IP address + ips.forEach { ip -> StreamerOutputs[ip]?.SendData(afi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } - val logmessage = - "Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}" - Logger.info { logmessage } - db.Add_Log("AAS", logmessage) - 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" - ) - } + val logmessage = + "Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}" + Logger.info { logmessage } + db.Add_Log("AAS", logmessage) + db.queuepagingDB.DeleteByIndex(qp.index.toInt()) + return true } else { // file tidak valid, delete from queue paging db.queuepagingDB.DeleteByIndex(qp.index.toInt()) @@ -586,101 +567,18 @@ class MainExtension01 { "Cancelled paging message with index ${qp.index} due to invalid audio file" ) } - } else if (qp.Source == "SHALAT") { - val ann_id = Get_ANN_ID(qp.Message) - if (ann_id > 0) { - // shalat, ambil messagebank berdasarkan ann_id dengan bahasa Indonesia saja - Get_MessageBank_by_id(ann_id, listOf(Language.INDONESIA.name)).let { mblist -> - if (mblist.isNotEmpty()) { - Get_Soundbank_Files( - mblist[0], - emptyMap(), - { - // dapat list dari files dan sudah dicek valid path - listfile -> - val listafi = mutableListOf() - 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" - ) - } - } + } // kalau enggak semua streamer idle, skip dulu + } else { + // ada broadcast zone yang 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 broadcast zone") } } 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.Add_Log( "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 { @@ -689,66 +587,129 @@ class MainExtension01 { db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone") } } - + return false } - /** - * Read and process Queue_Table table. - */ - fun Read_Queue_Table(){ - db.queuetableDB.Get() - db.queuetableDB.List.forEach { qa -> - println("Processing $qa") - if (qa.BroadcastZones.isNotEmpty()) { - val zz = qa.BroadcastZones.split(";") + fun Read_Queue_Shalat() : Boolean{ + db.queuepagingDB.Get() + val list = db.queuepagingDB.List + .filter { it.Source=="SHALAT" } + list.forEach { qp -> + println("Processing $qp") + if (qp.BroadcastZones.isNotBlank()){ + val zz = qp.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() + 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)) { - if (qa.Type == "SOUNDBANK") { - 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 + + } + val targetfile = SoundbankResult_directory.resolve( + Make_WAV_FileName( + "Shalat", + "" + ) + ).toString() + + val result =audioPlayer.WavWriter( + listafi, + targetfile, true, + ) + db.Add_Log("AAS", result.message) + if (result.success) { + // file siap broadcast + 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 SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}" + Logger.info { logmsg } + 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( "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()) { - val listafi = mutableListOf() - - mblist.forEach { mb -> - Get_Soundbank_Files(mb, variables ?: emptyMap(), { - listfile -> - listfile.forEach { filenya -> + mblist.forEach { + mb -> + val result = Get_Soundbank_Files(mb, emptyMap()) + if (result.success){ + val listafi = mutableListOf() + result.files.forEach { filenya -> val afi = contentCache.getAudioFile(filenya) if (afi!=null && afi.isValid()){ listafi.add(afi) @@ -761,144 +722,190 @@ class MainExtension01 { } } - }, - { - err -> - db.Add_Log("AAS", err) - 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) { + val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Timer","")).toString() + val result = audioPlayer.WavWriter(listafi, targetfile, true) + db.Add_Log("AAS", result.message) + 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].let{ sc -> - sc?.SendData(targetafi.bytes, - { db.Add_Log("AAS", it) }, - { db.Add_Log("AAS", it) } ) - } - } + 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}" + "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()) + return true } - } else { - println("Failed to write WAV file: $message") + } else{ 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 { // 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" + "Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank" ) } - } else if (qa.Type == "TIMER") { - 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() - 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) - } - } + } // kalau enggak semua streamer idle, skip dulu - } - val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Timer","")).toString() - audioPlayer.WavWriter(listafi, targetfile, true, - ) { success, 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 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 { + // invalid ann_id, delete from queue table + db.queuetableDB.DeleteByIndex(qa.index.toInt()) + db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid ANN_ID") } } else { // ada broadcast zone yang tidak valid, delete from queue table db.queuetableDB.DeleteByIndex(qa.index.toInt()) - db.Add_Log( - "AAS", - "Cancelled table message with index ${qa.index} due to invalid broadcast zone" - ) + db.Add_Log("AAS", "Cancelled TIMER 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 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() + + 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. * This function is called every minute when second=00. diff --git a/src/audio/AudioPlayer.kt b/src/audio/AudioPlayer.kt index e6ea5ff..fa00e90 100644 --- a/src/audio/AudioPlayer.kt +++ b/src/audio/AudioPlayer.kt @@ -6,17 +6,14 @@ import audio.Bass.BASS_POS_BYTE import audio.Bass.BASS_STREAM_DECODE import audio.Bass.BASS_SAMPLE_MONO import audio.BassEnc.BASS_ENCODE_PCM +import codes.Result_Boolean_String import codes.Somecodes.Companion.ValidFile import codes.Somecodes.Companion.ValidString import com.sun.jna.Memory import com.sun.jna.Pointer import contentCache -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch + import org.tinylog.Logger -import java.util.function.BiConsumer @Suppress("unused") class AudioPlayer (var samplingrate: Int) { @@ -131,84 +128,87 @@ class AudioPlayer (var samplingrate: Int) { * @param data The byte array containing the audio data. * @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 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) { - CoroutineScope(Dispatchers.IO).launch { - bass.BASS_SetDevice(0) // Set to No Sound device for writing - val streamhandle = bass.BASS_StreamCreate(samplingrate, 1, BASS_STREAM_DECODE, Pointer(-1), null) + fun WavWriter(data: ByteArray, target: String, withChime: Boolean = true) : Result_Boolean_String { + 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){ - val encodehandle = bassenc.BASS_Encode_Start(streamhandle, target, BASS_ENCODE_PCM, null, null) - if (encodehandle!=0){ + if (streamhandle!=0){ + val encodehandle = bassenc.BASS_Encode_Start(streamhandle, target, BASS_ENCODE_PCM, null, null) + if (encodehandle!=0){ - 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") + 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 + } + + 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){ - 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") - } - } 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 + 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") } - } while (read > 0) - println("Finished reading stream data, total $totalread bytes read") + } else println("Chime Down not valid") + } - if (all_success){ - callback.accept(true, "WAV file written successfully: $target") - } else { - callback.accept(false, "Failed to write some data to WAV file: $target") + 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 } - - bassenc.BASS_Encode_Stop(encodehandle) - } else callback.accept(false, "Failed to start encoding: ${bass.BASS_ErrorGetCode()}") + } while (read > 0) + println("Finished reading stream data, total $totalread bytes read") + bassenc.BASS_Encode_Stop(encodehandle) 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 { - 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 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 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, target: String, withChime: Boolean = true, callback: BiConsumer) { + fun WavWriter(sources: List, target: String, withChime: Boolean = true) : Result_Boolean_String { if (sources.isEmpty()) { - callback.accept(false, " Invalid sources") - return + return Result_Boolean_String(false,"Invalid Source") } if (!ValidString(target)) { - callback.accept(false, " Invalid target file name") - return + return Result_Boolean_String(false, " Invalid target file name") + } + + 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 { - 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 - } - - - - 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") - } + 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("Source ${source.fileName} is not valid") + println("Chime up failed") } - - } - 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") - } + } 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 { + 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") + } + + } diff --git a/src/codes/Result_Boolean_String.kt b/src/codes/Result_Boolean_String.kt new file mode 100644 index 0000000..fc1b61d --- /dev/null +++ b/src/codes/Result_Boolean_String.kt @@ -0,0 +1,4 @@ +package codes + +class Result_Boolean_String(val success: Boolean, val message: String) { +} \ No newline at end of file diff --git a/src/codes/Result_GetSoundbankFiles.kt b/src/codes/Result_GetSoundbankFiles.kt new file mode 100644 index 0000000..5f5a90a --- /dev/null +++ b/src/codes/Result_GetSoundbankFiles.kt @@ -0,0 +1,3 @@ +package codes + +class Result_GetSoundbankFiles(val success: Boolean, val message: String , val files: List = emptyList()) \ No newline at end of file diff --git a/src/commandServer/TCP_Android_Command_Server.kt b/src/commandServer/TCP_Android_Command_Server.kt index b8df24b..6216ef7 100644 --- a/src/commandServer/TCP_Android_Command_Server.kt +++ b/src/commandServer/TCP_Android_Command_Server.kt @@ -60,25 +60,32 @@ class TCP_Android_Command_Server { Logger.info { "Start communicating with IPMT/IPM with IP $key" } val din = socket.getInputStream() val dout = socket.getOutputStream() - while (isActive) { - if (din.available() > 0) { - val bb = ByteArray(din.available()) - din.read(bb) - // B4A format, 4 bytes di depan adalah size - val str = String(bb, 4, bb.size - 4) - str.split("@").map { it.trim() }.filter { ValidString(it) } - .forEach { - process_command(key,it) { reply -> - try { - dout.write(String_to_Byte_Android(reply)) - } catch (e: Exception) { - logcb.accept("Failed to send reply to $key, Message : $e") + try{ + while (isActive) { + if (din.available() > 0) { + val bb = ByteArray(din.available()) + din.read(bb) + // B4A format, 4 bytes di depan adalah size + val str = String(bb, 4, bb.size - 4) + println("Received command from $key : $str") + str.split("@").map { it.trim() }.filter { ValidString(it) } + .forEach { + process_command(key,it) { reply -> + try { + 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") + CloseSocket(socket) socketMap.remove(key) } @@ -98,6 +105,14 @@ class TCP_Android_Command_Server { 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 * @param str The input string @@ -184,33 +199,30 @@ class TCP_Android_Command_Server { val data = pj.GetData() pj.Close() 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){ - success, message -> - if (success){ - // insert to paging queue - - val qp = QueuePaging( - 0u, - LocalDateTime.now().format(datetimeformat1), - "PAGING", - "NORMAL", - pj.filePath.absolutePathString(), - pj.broadcastzones - ) - 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@") - } - + val result = audioPlayer.WavWriter(data, pj.filePath.absolutePathString(), true) + if (result.success){ + val qp = QueuePaging( + 0u, + LocalDateTime.now().format(datetimeformat1), + "PAGING", + "NORMAL", + pj.filePath.absolutePathString(), + pj.broadcastzones + ) + 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 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@") } + } else { + logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : ${result.message}") + cb.accept(parts[0]+";NG@") + return } + } else { logcb.accept("Paging stop from Android $key failed, no ongoing paging") cb.accept(parts[0]+";NG@")