0
1
mirror of https://github.com/radio95-rnt/rds95.git synced 2026-02-26 12:32:05 +01:00

massive lua api refactor

This commit is contained in:
2026-01-20 19:50:04 +01:00
parent 97f9b3069c
commit 53c9169330
15 changed files with 976 additions and 776 deletions

View File

@@ -30,5 +30,8 @@
"ffi": "disable",
"package": "disable"
},
"C_Cpp.dimInactiveRegions": false
"C_Cpp.dimInactiveRegions": false,
"Lua.diagnostics.disable": [
"duplicate-set-field"
]
}

View File

@@ -3,48 +3,62 @@
-- Global Variables
---@type string
core_version = ""
---@type integer
eon_count = 0
---@type integer
max_programs = 0
---@type integer
user_data_len = 0
--#region Functions implemented or used in C
---Executes a CRC-16 CCIIT
dp = {}
---@type integer
dp.max_programs = 0
---Executes a CRC-16 CCIIT on given data
---@param data string
---@return integer
function crc16(data) end
function dp.crc16(data) end
---Starts the initialization sequence, also calls the on_init function
---@return nil
function set_rds_program_defaults() end
function dp.set_rds_program_defaults() end
---Saves, loads and resets the state of the data, you might as well restart the whole program
---@return nil
function reset_rds() end
function dp.reset_rds() end
---Forces encoder and modulator data to be saved to disc
---@return nil
function force_save() end
function dp.force_save() end
---@param program_idx integer 0 to (max_programs - 1)
function dp.set_rds_program(program_idx) end
---@return integer
function dp.get_rds_program() end
---This function is called by the C core after we reset data, or have no data in general
---It should be defined by the user in the script.
---@return nil
function on_init() end
on_inits = {}
---This function is called by the C core after we initialize the encoder (always, every start)
---It should be defined by the user in the script.
---@return nil
function on_start() end
on_starts = {}
---This function is called every time when the state resets, register your odas here
---It should be defined by the user in the script.
---@return nil
function on_state() end
on_states = {}
---This function is called every second
---It should be defined by the user in the script.
---@return nil
function tick() end
ticks = {}
-- Every function with with the s suffixed table means that other versions of that function can be in that list-table, and each one will be called
---This function is called in order to handle UDP data
---It should be defined by the user in the script.
---@param data string
@@ -70,72 +84,77 @@ function group(group) end
---@return integer d
function rds2_group(stream) end
rds = {}
---@type integer
rds.eon_count = 0
---@param pi integer
function set_rds_pi(pi) end
function rds.set_pi(pi) end
---@return integer
function get_rds_pi() end
function rds.get_pi() end
---@param pty integer
function set_rds_pty(pty) end
function rds.set_pty(pty) end
---@return integer
function get_rds_pty() end
function rds.get_pty() end
---@param ecc integer
function set_rds_ecc(ecc) end
function rds.set_ecc(ecc) end
---@return integer
function get_rds_ecc() end
function rds.get_ecc() end
---@param slc_data integer
function set_rds_slc_data(slc_data) end
function rds.set_slc_data(slc_data) end
---@return integer
function get_rds_slc_data() end
function rds.get_slc_data() end
---@param ct boolean
function set_rds_ct(ct) end
function rds.set_ct(ct) end
---@return boolean
function get_rds_ct() end
function rds.get_ct() end
---@param dpty boolean
function set_rds_dpty(dpty) end
function rds.set_dpty(dpty) end
---@return boolean
function get_rds_dpty() end
function rds.get_dpty() end
---@param tp boolean
function set_rds_tp(tp) end
function rds.set_tp(tp) end
---@return boolean
function get_rds_tp() end
function rds.get_tp() end
---@param ta boolean
function set_rds_ta(ta) end
function rds.set_ta(ta) end
---@return boolean
function get_rds_ta() end
function rds.get_ta() end
-- Feature Flags
---@param enabled boolean
function set_rds_rt1_enabled(enabled) end
function rds.set_rt1_enabled(enabled) end
---@return boolean
function get_rds_rt1_enabled() end
function rds.get_rt1_enabled() end
---@param enabled boolean
function set_rds_rt2_enabled(enabled) end
function rds.set_rt2_enabled(enabled) end
---@return boolean
function get_rds_rt2_enabled() end
function rds.get_rt2_enabled() end
---@param enabled boolean
function set_rds_ptyn_enabled(enabled) end
function rds.set_ptyn_enabled(enabled) end
---@return boolean
function get_rds_ptyn_enabled() end
function rds.get_ptyn_enabled() end
---@param rt_type integer
function set_rds_rt_type(rt_type) end
function rds.set_rt_type(rt_type) end
---@return integer
function get_rds_rt_type() end
function rds.get_rt_type() end
-- Modulation & Generation
---@param mode integer
function set_rds2_mode(mode) end
function rds.set_rds2_mode(mode) end
---@return integer
function get_rds2_mode() end
function rds.get_rds2_mode() end
---@param streams integer
function set_rds_streams(streams) end
@@ -152,72 +171,67 @@ function get_rds_level() end
-- Program & Linking
---@param linkage boolean
function set_rds_link(linkage) end
function rds.set_link(linkage) end
---@return boolean
function get_rds_link() end
---@param program_idx integer 0 to (max_programs - 1)
function set_rds_program(program_idx) end
---@return integer
function get_rds_program() end
function rds.get_link() end
-- Timeouts and Periods
---@param period integer in seconds
function set_rds_rt_switching_period(period) end
function rds.set_rt_switching_period(period) end
---@return integer
function get_rds_rt_switching_period() end
function rds.get_rt_switching_period() end
---For a RT1, this sets the timeout period before setting RT1 into "Default RT" (not literally)
---@param timeout integer in seconds
function set_rds_rt_text_timeout(timeout) end
function rds.set_rt_text_timeout(timeout) end
---@return integer
function get_rds_rt_text_timeout() end
function rds.get_rt_text_timeout() end
-- String Setters (Charset converted)
---@param ptyn string Program Type Name (max 8 chars)
function set_rds_ptyn(ptyn) end
function rds.set_ptyn(ptyn) end
---@param ps string Program Service (8 chars)
function set_rds_ps(ps) end
function rds.set_ps(ps) end
---@param tps string Traffic PS
function set_rds_tps(tps) end
function rds.set_tps(tps) end
---@param rt1 string Radio Text 1 (max 64 chars)
function set_rds_rt1(rt1) end
function rds.set_rt1(rt1) end
---@param rt2 string Radio Text 2 (max 64 chars)
function set_rds_rt2(rt2) end
function rds.set_rt2(rt2) end
---@param rt string Default radio text - max 64 characters
function set_rds_default_rt(rt) end
function rds.set_default_rt(rt) end
---@param lps string
function set_rds_lps(lps) end
function rds.set_lps(lps) end
---@return string
function get_rds_lps() end
function rds.get_lps() end
---@param grpseq string
function set_rds_grpseq(grpseq) end
function rds.set_grpseq(grpseq) end
---@return string
function get_rds_grpseq() end
function rds.get_grpseq() end
---@param grpseq2 string
function set_rds_grpseq2(grpseq2) end
function rds.set_grpseq2(grpseq2) end
---@return string
function get_rds_grpseq2() end
function rds.get_grpseq2() end
---Puts in a RDS1 group in the buffer, note that block A is filled in always
---@param b integer
---@param c integer
---@param d integer
function put_rds_custom_group(b, c, d) end
function rds.put_custom_group(b, c, d) end
---Puts in a RDS2 group in the buffer
---@param a integer
---@param b integer
---@param c integer
---@param d integer
function put_rds2_custom_group(a, b, c, d) end
function rds.put_rds2_custom_group(a, b, c, d) end
---Sets the AFs included in group 0
---@param afs table
function set_rds_af_group0(afs) end
function rds.set_af(afs) end
---Sets data about the EON
---@param eon integer Index of the EON we are setting
@@ -229,7 +243,7 @@ function set_rds_af_group0(afs) end
---@param ps string
---@param afs table
---@param data integer
function set_rds_eon(eon, enabled, pi, tp, ta, pty, ps, afs, data) end
function rds.set_eon(eon, enabled, pi, tp, ta, pty, ps, afs, data) end
---Gets the same data set_rds_eon sets, yes this returns 8 arguments
---@param eon integer
@@ -241,37 +255,45 @@ function set_rds_eon(eon, enabled, pi, tp, ta, pty, ps, afs, data) end
---@return string ps
---@return table _ this is empty, getting afs is not supported yet
---@return integer data
function get_rds_eon(eon) end
function rds.get_eon(eon) end
---Sets the X/Y of the UDG
---@param xy boolean
---@param groups table Table of tables, this should be up to 8 tables containing 3 integers
function set_rds_udg(xy, groups) end
function rds.set_udg(xy, groups) end
---Sets the X/Y of the UDG for RDS2
---@param xy boolean
---@param groups table Table of tables, this should be up to 8 tables containing 4 integers
function set_rds_udg2(xy, groups) end
function rds.set_udg2(xy, groups) end
userdata = {}
---@type integer
userdata.len = 0
---Data is allocated in each program's data for lua data (per program, diffrent program, diffrent data), note that this overwrites existing data over the whole userdata string
---@param data string
function set_userdata(data) end
function userdata.set(data) end
---Writes to the userdata at the offset, size does not have to match the length of the string, if the string is less than size then the rest of the string will be padded with zeroes until it is size
---@param offset integer
---@param size integer
---@param data string
function set_userdata_offset(offset, size, data) end
function userdata.set_offset(offset, size, data) end
---Returns all of the data saved as user data
---@return string
function get_userdata() end
function userdata.get() end
---Gets data from userdata but at the specified offset
---@param offset integer
---@param size integer
---@return string
function get_userdata_offset(offset, size) end
function userdata.get_offset(offset, size) end
--#endregion
ext = {}
rds.ext = {}
-- RT Plus Tags
---Sets RT+ tags: type1, start1, len1, type2, start2, len2
---@param ertp boolean
@@ -281,29 +303,29 @@ function get_userdata_offset(offset, size) end
---@param t2 integer
---@param s2 integer
---@param l2 integer
function set_rds_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2) end
function rds.ext.set_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2) end
---Gets RT+ tags: type1, start1, len1, type2, start2, len2
---@param ertp boolean
---@return integer type1, integer start1, integer len1, integer type2, integer start2, integer len2
function get_rds_rtplus_tags(ertp) end
function rds.ext.get_rtplus_tags(ertp) end
---Toggles RTP or ERTP's toggle switch
---@param ertp boolean
function toggle_rds_rtp(ertp) end
function rds.ext.toggle_rtp(ertp) end
---Sets the metadata of RTP or ERTP
---@param ertp boolean
---@param running boolean
function set_rds_rtp_meta(ertp, running) end
function rds.ext.set_rtp_meta(ertp, running) end
---Gets the metadata of RTP and ERTP
---@param ertp boolean
---@return boolean running
function get_rds_rtp_meta(ertp) end
function rds.ext.get_rtp_meta(ertp) end
---Sets the AFs included in the ODA
---@param afs table
function set_rds_af_oda(afs) end
function rds.ext.set_af_oda(afs) end
---Registers an ODA to be used in the O of the group sequence. ODAs are stored as state data, thus running reset_rds will clear it
---Groups 14, 15, 2, 0 cannot be registered either version, groups 10, 4, 1 can be only registered as B, any other is free to take
@@ -313,16 +335,16 @@ function set_rds_af_oda(afs) end
---@param aid integer
---@param data integer
---@return integer oda_id
function register_oda(group, group_version, aid, data) end
function ext.register_oda(group, group_version, aid, data) end
---Unregisters an ODA, this stops the handler or AID being called/sent
---@param oda_id integer
function unregister_oda(oda_id) end
function ext.unregister_oda(oda_id) end
---Sets the data for a existing ODA group
---@param oda_id integer
---@param data integer
function set_oda_id_data(oda_id, data) end
function ext.set_oda_id_data(oda_id, data) end
---The callback function for an ODA handler
---@alias ODAHandler fun(): (boolean, integer, integer, integer)
@@ -334,7 +356,7 @@ function set_oda_id_data(oda_id, data) end
---You are asked to set groups B last 5 bits, leave rest 0
---@param oda_id integer The ID returned by register_oda
---@param fun ODAHandler
function set_oda_handler(oda_id, fun) end
function ext.set_oda_handler(oda_id, fun) end
---The callback function for an ODA handler
---@alias RDS2_ODAHandler fun(integer): (boolean, integer, integer, integer, integer)
@@ -342,23 +364,23 @@ function set_oda_handler(oda_id, fun) end
---You are asked to not fill in the channel id in block A, however you are asked to fill in the function number (if you do not know what is that, just OR block A with (1 << 14))
---@param oda_id integer
---@param func RDS2_ODAHandler
function set_oda_handler_rds2(oda_id, func) end
function ext.set_oda_handler_rds2(oda_id, func) end
---@param oda_id integer
---@param data integer
function set_oda_id_data_rds2(oda_id, data) end
function ext.set_oda_id_data_rds2(oda_id, data) end
---@param aid integer
---@param data integer
---@param file_related boolean
---@return integer oda_id
function register_oda_rds2(aid, data, file_related) end
function ext.register_oda_rds2(aid, data, file_related) end
---Unregisters an RDS 2 ODA, this stops the handler or AID being called/sent
---@param oda_id integer
function unregister_oda_rds2(oda_id) end
function ext.unregister_oda_rds2(oda_id) end
---@param ert string
function set_rds_ert(ert) end
function rds.ext.set_ert(ert) end
---@return string
function get_rds_ert() end
function rds.ext.get_ert() end

View File

@@ -17,7 +17,7 @@ local _RDS_ODA_pointer = 1
---@param aid integer
---@param data integer
---@return integer oda_id
function register_oda(group, group_version, aid, data)
function ext.register_oda(group, group_version, aid, data)
if group == 14 or group == 15 or group == 2 or group == 0 then error("Group is incorrect", 2) end
if (group == 10 or group == 4 or group == 1) and group_version then error("Group is incorrect", 2) end
local oda = _ODA.new(group, group_version, aid, data, false)
@@ -33,7 +33,7 @@ end
---Unregisters an ODA, this stops the handler or AID being called/sent
---@param oda_id integer
function unregister_oda(oda_id)
function ext.unregister_oda(oda_id)
if oda_id < 1 or oda_id > #_RDS_ODAs or _RDS_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
_RDS_ODAs[oda_id] = false
@@ -45,7 +45,7 @@ end
---Sets the id_data for a existing ODA group
---@param oda_id integer
---@param data integer
function set_oda_id_data(oda_id, data)
function ext.set_oda_id_data(oda_id, data)
if oda_id < 1 or oda_id > #_RDS_ODAs or _RDS_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
_RDS_ODAs[oda_id].data = data
end
@@ -57,7 +57,7 @@ end
---You are asked to set groups B last 5 bits, leave rest 0
---@param oda_id integer The ID returned by register_oda
---@param fun ODAHandler
function set_oda_handler(oda_id, fun)
function ext.set_oda_handler(oda_id, fun)
if oda_id < 1 or oda_id > #_RDS_ODAs or _RDS_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
if _RDS_ODAs[oda_id].group == 3 then error("3A ODAs cannot have handlers.", 2) end
_RDS_ODAs[oda_id].handler = fun
@@ -113,18 +113,13 @@ function group(group_type)
if #_RDS_ODAs == 0 then return false, 0, 0, 0 end
if _RDS_ODA_pointer > #_RDS_ODAs or _RDS_ODA_pointer < 1 then _RDS_ODA_pointer = 1 end
if group_type == "O" then
return get_aid()
elseif group_type == "K" then
return get_data()
end
if group_type == "O" then return get_aid()
elseif group_type == "K" then return get_data() end
end
return false, 0, 0, 0
end
local _old_on_state_oda = on_state
function on_state()
table.insert(on_states, function ()
_RDS_ODAs = {}
_RDS_ODA_pointer = 1
if type(_old_on_state_oda) == "function" then _old_on_state_oda() end
end
end)

View File

@@ -14,7 +14,7 @@ _RDS2_ODA_pointer = 1
---@param data integer
---@param file_related boolean
---@return integer oda_id
function register_oda_rds2(aid, data, file_related)
function ext.register_oda_rds2(aid, data, file_related)
local oda = _RDS2_ODA.new(aid, data, false, file_related)
for i = 1, #_RDS2_ODAs do
if _RDS2_ODAs[i] == false then
@@ -28,7 +28,7 @@ end
---Unregisters an RDS 2 ODA, this stops the handler or AID being called/sent
---@param oda_id integer
function unregister_oda_rds2(oda_id)
function ext.unregister_oda_rds2(oda_id)
if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
_RDS2_ODAs[oda_id] = false
@@ -39,14 +39,14 @@ end
---@param oda_id integer
---@param data integer
function set_oda_id_data_rds2(oda_id, data)
function ext.set_oda_id_data_rds2(oda_id, data)
if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
_RDS2_ODAs[oda_id].data = data
end
---@param oda_id integer
---@param func RDS2_ODAHandler
function set_oda_handler_rds2(oda_id, func)
function ext.set_oda_handler_rds2(oda_id, func)
if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
_RDS2_ODAs[oda_id].handler = func
end
@@ -125,10 +125,8 @@ function rds2_group(stream)
end
end
local _old_on_state_oda_rds2 = on_state
function on_state()
table.insert(on_states, function ()
_RDS2_ODAs = {}
_RDS2_ODA_aid = 0
_RDS2_ODA_pointer = 1
if type(_old_on_state_oda_rds2) == "function" then _old_on_state_oda_rds2() end
end
end)

View File

@@ -55,8 +55,8 @@ end
local function init_af_oda()
if _Af_Oda_id == nil then
_Af_Oda_id = register_oda(7, false, 0x6365, 0)
set_oda_handler(_Af_Oda_id, function()
_Af_Oda_id = ext.register_oda(7, false, 0x6365, 0)
ext.set_oda_handler(_Af_Oda_id, function()
local b, c, d = get_next_af_oda_group()
return true, b, c, d
end)
@@ -69,7 +69,7 @@ local function save_af_to_userdata(afs)
local payload = string.pack("B", count)
for i = 1, count do payload = payload .. string.pack("f", afs[i]) end
set_userdata_offset(USERDATA_ODA_OFFSET, #payload, payload)
userdata.set_offset(USERDATA_ODA_OFFSET, #payload, payload)
end
local function _process_af_list(afs)
@@ -86,13 +86,13 @@ local function _process_af_list(afs)
end
local function load_af_from_userdata()
local header = get_userdata_offset(USERDATA_ODA_OFFSET, 1)
local header = userdata.get_offset(USERDATA_ODA_OFFSET, 1)
if header == "" or header == nil then return end
local count = string.unpack("B", header)
if count == 0 or count > 25 then return end
local data = get_userdata_offset(USERDATA_ODA_OFFSET + 1, count * 4)
local data = userdata.get_offset(USERDATA_ODA_OFFSET + 1, count * 4)
if #data < (count * 4) then return end
local afs = {}
@@ -106,14 +106,12 @@ end
---Sets the AFs included in the ODA and saves them
---@param afs table List of numbers (e.g., {98.1, 102.5})
function set_rds_af_oda(afs)
function rds.ext.set_af_oda(afs)
_process_af_list(afs)
save_af_to_userdata(afs)
end
local _old_on_state_af = on_state
function on_state()
table.insert(on_states, function ()
load_af_from_userdata()
if _Af_Oda_len ~= 0 then init_af_oda() end
if type(_old_on_state_af) == "function" then _old_on_state_af() end
end
end)

View File

@@ -6,25 +6,25 @@ local USERDATA_ERT_OFFSET = 0
local function init_ert()
if _Ert_oda_id == nil then
_Ert_oda_id = register_oda(13, false, 0x6552, 1)
set_oda_handler(_Ert_oda_id, function ()
if string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+258, 1)) == 1 then
local new_data = get_userdata_offset(USERDATA_ERT_OFFSET, 128)
local new_segments = string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+128, 1))
set_userdata_offset(USERDATA_ERT_OFFSET+129, 128, new_data)
set_userdata_offset(USERDATA_ERT_OFFSET+257, 1, string.char(new_segments))
set_userdata_offset(USERDATA_ERT_OFFSET+258, 1, string.char(0))
_Ert_oda_id = ext.register_oda(13, false, 0x6552, 1)
ext.set_oda_handler(_Ert_oda_id, function ()
if string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+258, 1)) == 1 then
local new_data = userdata.get_offset(USERDATA_ERT_OFFSET, 128)
local new_segments = string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+128, 1))
userdata.set_offset(USERDATA_ERT_OFFSET+129, 128, new_data)
userdata.set_offset(USERDATA_ERT_OFFSET+257, 1, string.char(new_segments))
userdata.set_offset(USERDATA_ERT_OFFSET+258, 1, string.char(0))
_Ert_state = 0
end
local segments = string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+257, 1))
local segments = string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+257, 1))
if segments == 0 then return false, 0, 0, 0 end
if _Ert_state >= segments then _Ert_state = 0 end
local b = _Ert_state & 31
local chunk = get_userdata_offset(USERDATA_ERT_OFFSET + 129 + _Ert_state * 4, 4)
local chunk = userdata.get_offset(USERDATA_ERT_OFFSET + 129 + _Ert_state * 4, 4)
local c = (string.byte(chunk, 1) << 8) | string.byte(chunk, 2)
local d = (string.byte(chunk, 3) << 8) | string.byte(chunk, 4)
@@ -36,16 +36,16 @@ end
function unregister_ert()
if _Ert_oda_id ~= nil then
unregister_oda(_Ert_oda_id)
ext.unregister_oda(_Ert_oda_id)
_Ert_oda_id = nil
end
end
function set_rds_ert(ert)
function rds.ext.set_ert(ert)
if #ert == 0 then
set_userdata_offset(USERDATA_ERT_OFFSET, 128, "")
set_userdata_offset(USERDATA_ERT_OFFSET+128, 1, string.char(0))
set_userdata_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1))
userdata.set_offset(USERDATA_ERT_OFFSET, 128, "")
userdata.set_offset(USERDATA_ERT_OFFSET+128, 1, string.char(0))
userdata.set_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1))
return
end
@@ -55,33 +55,31 @@ function set_rds_ert(ert)
local padding = (4 - (#data % 4)) % 4
data = data .. string.rep("\0", padding)
set_userdata_offset(USERDATA_ERT_OFFSET, 128, data)
userdata.set_offset(USERDATA_ERT_OFFSET, 128, data)
local segments = #data // 4
if segments > 32 then segments = 32 end
set_userdata_offset(USERDATA_ERT_OFFSET+128, 1, string.char(segments))
userdata.set_offset(USERDATA_ERT_OFFSET+128, 1, string.char(segments))
if string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+257, 1)) == 0 then
if string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+257, 1)) == 0 then
init_ert()
set_userdata_offset(USERDATA_ERT_OFFSET+129, 128, data)
set_userdata_offset(USERDATA_ERT_OFFSET+257, 1, string.char(segments))
userdata.set_offset(USERDATA_ERT_OFFSET+129, 128, data)
userdata.set_offset(USERDATA_ERT_OFFSET+257, 1, string.char(segments))
_Ert_state = 0
else set_userdata_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1)) end
else userdata.set_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1)) end
if _Ert_oda_id == nil then init_ert() end
end
function get_rds_ert()
local segments = string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+128, 1))
function rds.ext.get_ert()
local segments = string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+128, 1))
if segments == 0 then return "" end
local data = get_userdata_offset(USERDATA_ERT_OFFSET, 128)
local data = userdata.get_offset(USERDATA_ERT_OFFSET, 128)
return data:match("^(.-)[\r%z]*") or ""
end
local _old_on_state_ert = on_state
function on_state()
if string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+257, 1)) ~= 0 then init_ert() end
if type(_old_on_state_ert) == "function" then _old_on_state_ert() end
end
table.insert(on_states, function ()
if string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+257, 1)) ~= 0 then init_ert() end
end)

View File

@@ -43,7 +43,7 @@ end
function RftInstance:stop()
if self.oda_id ~= nil and self.aid ~= 0 then
unregister_oda_rds2(self.oda_id)
ext.unregister_oda_rds2(self.oda_id)
self.oda_id = nil
end
@@ -59,9 +59,9 @@ end
---@private
function RftInstance:start()
if self.oda_id == nil and self.aid ~= 0 then
self.oda_id = register_oda_rds2(self.aid, 0, true)
self.oda_id = ext.register_oda_rds2(self.aid, 0, true)
set_oda_handler_rds2(self.oda_id, function(stream)
ext.set_oda_handler_rds2(self.oda_id, function(stream)
if #self.file_data == 0 or self.paused then
return false, 0, 0, 0, 0
end
@@ -138,7 +138,7 @@ function RftInstance:sendFile(aid, path, id, crc, once)
local f_size = #self.file_data
if crc == 0 then
self.crc_mode = 0
self.crc_full_file = crc16(self.file_data)
self.crc_full_file = dp.crc16(self.file_data)
elseif crc == true or crc == 7 then
if f_size <= 40960 then self.crc_mode = 1
elseif f_size > 40960 and f_size <= 81920 then self.crc_mode = 2
@@ -153,7 +153,7 @@ function RftInstance:sendFile(aid, path, id, crc, once)
local chunk_size = 5 * multiplier
for i = 1, f_size, chunk_size do
local chunk = string.sub(self.file_data, i, i + chunk_size - 1)
local v = crc16(chunk)
local v = dp.crc16(chunk)
self.crc_data = self.crc_data .. string.char(v >> 8, v & 0xff)
end
end
@@ -161,7 +161,7 @@ function RftInstance:sendFile(aid, path, id, crc, once)
if f_size > (0x3ffff * 5) then error("File too large") end
if self.oda_id == nil then self:start() end
set_oda_id_data_rds2(self.oda_id, f_size | (id & 63) << 18 | (self.version & 7) << 24 | (self.crc_enabled and 1 or 0) << 27)
ext.set_oda_id_data_rds2(self.oda_id, f_size | (id & 63) << 18 | (self.version & 7) << 24 | (self.crc_enabled and 1 or 0) << 27)
self.last_id = id
self.paused = false
return interrupted

View File

@@ -8,11 +8,11 @@ local USERDATA_RTP_OFFSET = 259
local function init_rtp()
if _Rtp_oda_id == nil then
_Rtp_oda_id = register_oda(11, false, 0x4BD7, 0)
set_oda_handler(_Rtp_oda_id, function ()
local b = (_Rtp_toggle and 1 or 0) << 4 | string.byte(get_userdata_offset(USERDATA_RTP_OFFSET, 1)) << 3
local data_0 = get_userdata_offset(USERDATA_RTP_OFFSET+1, 3)
local data_1 = get_userdata_offset(USERDATA_RTP_OFFSET+4, 3)
_Rtp_oda_id = ext.register_oda(11, false, 0x4BD7, 0)
ext.set_oda_handler(_Rtp_oda_id, function ()
local b = (_Rtp_toggle and 1 or 0) << 4 | string.byte(userdata.get_offset(USERDATA_RTP_OFFSET, 1)) << 3
local data_0 = userdata.get_offset(USERDATA_RTP_OFFSET+1, 3)
local data_1 = userdata.get_offset(USERDATA_RTP_OFFSET+4, 3)
b = b | (string.byte(data_0, 1) & 0xf8) >> 3
local c = (string.byte(data_0, 1) & 0x7) << 13
@@ -31,11 +31,11 @@ end
local function init_ertp()
if _Ertp_oda_id == nil then
_Ertp_oda_id = register_oda(12, false, 0x4BD8, 0)
set_oda_handler(_Ertp_oda_id, function ()
local b = (_Ertp_toggle and 1 or 0) << 4 | string.byte(get_userdata_offset(USERDATA_RTP_OFFSET+7, 1)) << 3
local data_0 = get_userdata_offset(USERDATA_RTP_OFFSET+8, 3)
local data_1 = get_userdata_offset(USERDATA_RTP_OFFSET+11, 3)
_Ertp_oda_id = ext.register_oda(12, false, 0x4BD8, 0)
ext.set_oda_handler(_Ertp_oda_id, function ()
local b = (_Ertp_toggle and 1 or 0) << 4 | string.byte(userdata.get_offset(USERDATA_RTP_OFFSET+7, 1)) << 3
local data_0 = userdata.get_offset(USERDATA_RTP_OFFSET+8, 3)
local data_1 = userdata.get_offset(USERDATA_RTP_OFFSET+11, 3)
b = b | (string.byte(data_0, 1) & 0xf8) >> 3
local c = (string.byte(data_0, 1) & 0x7) << 13
@@ -52,46 +52,44 @@ local function init_ertp()
end
end
function set_rds_rtp_meta(ertp, running)
function rds.ext.set_rtp_meta(ertp, running)
if ertp then
if running and _Ertp_oda_id == nil then init_ertp() end
set_userdata_offset(USERDATA_RTP_OFFSET+7, 1, string.char(running and 1 or 0))
userdata.set_offset(USERDATA_RTP_OFFSET+7, 1, string.char(running and 1 or 0))
else
if running and _Rtp_oda_id == nil then init_rtp() end
set_userdata_offset(USERDATA_RTP_OFFSET, 1, string.char(running and 1 or 0))
userdata.set_offset(USERDATA_RTP_OFFSET, 1, string.char(running and 1 or 0))
end
end
function get_rds_rtp_meta(ertp)
function rds.ext.get_rtp_meta(ertp)
local offset = ertp and (USERDATA_RTP_OFFSET+7) or USERDATA_RTP_OFFSET
return string.byte(get_userdata_offset(offset, 1)) ~= 0
return string.byte(userdata.get_offset(offset, 1)) ~= 0
end
function toggle_rds_rtp(ertp)
function rds.ext.toggle_rtp(ertp)
if ertp then _Ertp_toggle = not _Ertp_toggle
else _Rtp_toggle = not _Rtp_toggle end
end
function set_rds_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2)
set_rds_rtp_meta(ertp, true)
toggle_rds_rtp(ertp)
set_userdata_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6, string.char(t1, s1, l1, t2, s2, l2))
function rds.ext.set_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2)
rds.ext.set_rds_rtp_meta(ertp, true)
rds.ext.toggle_rds_rtp(ertp)
userdata.set_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6, string.char(t1, s1, l1, t2, s2, l2))
end
function get_rds_rtplus_tags(ertp)
return string.byte(get_userdata_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6), 1, 6)
function rds.ext.get_rtplus_tags(ertp)
return string.byte(userdata.get_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6), 1, 6)
end
function unregister_rtp(ertp)
if ertp and _Ertp_oda_id ~= nil then
unregister_oda(_Ertp_oda_id)
ext.unregister_oda(_Ertp_oda_id)
_Ertp_oda_id = nil
elseif _Rtp_oda_id ~= nil then
unregister_oda(_Rtp_oda_id)
ext.unregister_oda(_Rtp_oda_id)
_Rtp_oda_id = nil
end
end
local _old_on_state_rtp = on_state
function on_state()
if get_rds_rtp_meta(false) then init_rtp() end
if get_rds_rtp_meta(true) then init_ertp() end
if type(_old_on_state_rtp) == "function" then _old_on_state_rtp() end
end
table.insert(on_states, function ()
if rds.ext.get_rtp_meta(false) then init_rtp() end
if rds.ext.get_rtp_meta(true) then init_ertp() end
end)

View File

@@ -40,52 +40,52 @@ function data_handle(data)
data = data:lower()
if data == "ver" then return string.format("rds95 core v. %s - (C) 2025 radio95 - lua parser\r\n", core_version)
elseif data == "init" then
set_rds_program_defaults()
dp.set_rds_program_defaults()
return "+\r\n"
elseif data == "reset" then
reset_rds()
dp.reset_rds()
return "+\r\n"
elseif data == "pi" then return string.format("PI=%s\r\n", string.format("%x", get_rds_pi()))
elseif data == "pty" then return string.format("PTY=%s\r\n", string.format("%d", get_rds_pty()))
elseif data == "ecc" then return string.format("ECC=%s\r\n", string.format("%x", get_rds_ecc()))
elseif data == "slcd" then return string.format("SLCD=%s\r\n", string.format("%x", get_rds_slc_data()))
elseif data == "ct" then return string.format("CT=%s\r\n", string.format("%d", (get_rds_ct() and 1 or 0)))
elseif data == "dpty" then return string.format("DPTY=%s\r\n", string.format("%d", (get_rds_dpty() and 1 or 0)))
elseif data == "tp" then return string.format("TP=%s\r\n", string.format("%d", (get_rds_tp() and 1 or 0)))
elseif data == "ta" then return string.format("TA=%s\r\n", string.format("%d", (get_rds_ta() and 1 or 0)))
elseif data == "rt1en" then return string.format("RT1EN=%s\r\n", string.format("%d", (get_rds_rt1_enabled() and 1 or 0)))
elseif data == "rt2en" then return string.format("RT2EN=%s\r\n", string.format("%d", (get_rds_rt2_enabled() and 1 or 0)))
elseif data == "ptynen" then return string.format("PTYNEN=%s\r\n", string.format("%d", (get_rds_ptyn_enabled() and 1 or 0)))
elseif data == "rttype" then return string.format("RTTYPE=%s\r\n", string.format("%d", get_rds_rt_type()))
elseif data == "rds2mod" then return string.format("RDS2MOD=%s\r\n", string.format("%d", get_rds2_mode()))
elseif data == "pi" then return string.format("PI=%s\r\n", string.format("%x", rds.get_pi()))
elseif data == "pty" then return string.format("PTY=%s\r\n", string.format("%d", rds.get_pty()))
elseif data == "ecc" then return string.format("ECC=%s\r\n", string.format("%x", rds.get_ecc()))
elseif data == "slcd" then return string.format("SLCD=%s\r\n", string.format("%x", rds.get_slc_data()))
elseif data == "ct" then return string.format("CT=%s\r\n", string.format("%d", (rds.get_ct() and 1 or 0)))
elseif data == "dpty" then return string.format("DPTY=%s\r\n", string.format("%d", (rds.get_dpty() and 1 or 0)))
elseif data == "tp" then return string.format("TP=%s\r\n", string.format("%d", (rds.get_tp() and 1 or 0)))
elseif data == "ta" then return string.format("TA=%s\r\n", string.format("%d", (rds.get_ta() and 1 or 0)))
elseif data == "rt1en" then return string.format("RT1EN=%s\r\n", string.format("%d", (rds.get_rt1_enabled() and 1 or 0)))
elseif data == "rt2en" then return string.format("RT2EN=%s\r\n", string.format("%d", (rds.get_rt2_enabled() and 1 or 0)))
elseif data == "ptynen" then return string.format("PTYNEN=%s\r\n", string.format("%d", (rds.get_ptyn_enabled() and 1 or 0)))
elseif data == "rttype" then return string.format("RTTYPE=%s\r\n", string.format("%d", rds.get_rt_type()))
elseif data == "rds2mod" then return string.format("RDS2MOD=%s\r\n", string.format("%d", rds.get_rds2_mode()))
elseif data == "rdsgen" then return string.format("RDSGEN=%s\r\n", string.format("%d", get_rds_streams()))
elseif data == "level" then return string.format("LEVEL=%s\r\n", string.format("%d", get_rds_level() * 255))
elseif data == "link" then return string.format("LINK=%s\r\n", string.format("%d", (get_rds_link() and 1 or 0)))
elseif data == "link" then return string.format("LINK=%s\r\n", string.format("%d", (rds.get_link() and 1 or 0)))
elseif data == "rtp" then
local t1, s1, l1, t2, s2, l2 = get_rds_rtplus_tags(false)
local t1, s1, l1, t2, s2, l2 = rds.ext.get_rtplus_tags(false)
return string.format("RTP=%d,%d,%d,%d,%d,%d\r\n", t1, s1, l1, t2, s2, l2)
elseif data == "ertp" then
local t1, s1, l1, t2, s2, l2 = get_rds_rtplus_tags(true)
local t1, s1, l1, t2, s2, l2 = rds.ext.get_rtplus_tags(true)
return string.format("ERTP=%d,%d,%d,%d,%d,%d\r\n", t1, s1, l1, t2, s2, l2)
elseif data == "rtprun" then
local running = get_rds_rtp_meta(false)
local running = rds.ext.get_rtp_meta(false)
local f1 = 2 or (running and 1 or 0)
return string.format("RTPRUN=%d\r\n", f1)
elseif data == "ertprun" then
local running = get_rds_rtp_meta(true)
local running = rds.ext.get_rtp_meta(true)
local f1 = 2 or (running and 1 or 0)
return string.format("ERTPRUN=%d\r\n", f1)
elseif data == "lps" then return string.format("LPS=%s\r\n", get_rds_lps())
elseif data == "ert" then return string.format("ERT=%s\r\n", get_rds_ert())
elseif data == "grpseq" then return string.format("GRPSEQ=%s\r\n", get_rds_grpseq())
elseif data == "grpseq2" then return string.format("GRPSEQ2=%s\r\n", get_rds_grpseq2())
elseif data == "lps" then return string.format("LPS=%s\r\n", rds.get_lps())
elseif data == "ert" then return string.format("ERT=%s\r\n", rds.ext.get_ert())
elseif data == "grpseq" then return string.format("GRPSEQ=%s\r\n", rds.get_grpseq())
elseif data == "grpseq2" then return string.format("GRPSEQ2=%s\r\n", rds.get_grpseq2())
else
local eon_cmd, eon_num = data:match("^eon(%d+)([a-z]+)$")
if eon_cmd then
local eon_idx = tonumber(eon_cmd)
if not eon_idx or eon_idx < 1 or eon_idx > eon_count then return "?\r\n" end
if not eon_idx or eon_idx < 1 or eon_idx > rds.eon_count then return "?\r\n" end
eon_idx = eon_idx - 1
local enabled, pi, tp, ta, pty, ps, afs, data_val = get_rds_eon(eon_idx)
local enabled, pi, tp, ta, pty, ps, afs, data_val = rds.get_eon(eon_idx)
if eon_num == "en" then return string.format("EON%dEN=%d\r\n", eon_idx + 1, enabled and 1 or 0)
elseif eon_num == "pi" then return string.format("EON%dPI=%x\r\n", eon_idx + 1, pi)
@@ -106,48 +106,48 @@ function data_handle(data)
local eon_num, eon_type = cmd:match("^eon(%d+)([a-z]+)$")
if eon_num then
local eon_idx = tonumber(eon_num)
if not eon_idx or eon_idx < 1 or eon_idx > eon_count then return "?\r\n" end
if not eon_idx or eon_idx < 1 or eon_idx > rds.eon_count then return "?\r\n" end
eon_idx = eon_idx - 1
local enabled, pi, tp, ta, pty, ps, afs, data_val = get_rds_eon(eon_idx)
local enabled, pi, tp, ta, pty, ps, afs, data_val = rds.get_eon(eon_idx)
if eon_type == "en" then
local en_val = tonumber(value)
if not en_val then return "-\r\n" end
enabled = (en_val ~= 0)
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val)
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val)
return "+\r\n"
elseif eon_type == "pi" then
local pi_val = tonumber(value, 16)
if not pi_val then return "-\r\n" end
set_rds_eon(eon_idx, enabled, pi_val, tp, ta, pty, ps, afs, data_val)
rds.set_eon(eon_idx, enabled, pi_val, tp, ta, pty, ps, afs, data_val)
return "+\r\n"
elseif eon_type == "ps" then
local ps_val = value:sub(1, 24)
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps_val, afs, data_val)
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps_val, afs, data_val)
return "+\r\n"
elseif eon_type == "pty" then
local pty_val = tonumber(value)
if not pty_val then return "-\r\n" end
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty_val, ps, afs, data_val)
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty_val, ps, afs, data_val)
return "+\r\n"
elseif eon_type == "ta" then
if not enabled or not tp then return "-\r\n" end
local ta_val = tonumber(value)
if not ta_val then return "-\r\n" end
ta = (ta_val ~= 0)
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val)
if ta then set_rds_ta(true) end
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val)
if ta then rds.set_ta(true) end
return "+\r\n"
elseif eon_type == "tp" then
local tp_val = tonumber(value)
if not tp_val then return "-\r\n" end
tp = (tp_val ~= 0)
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val)
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val)
return "+\r\n"
elseif eon_type == "af" then
local af_table = {}
if value == "" or value == "0" then
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, {}, data_val)
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, {}, data_val)
return "+\r\n"
end
for freq_str in value:gmatch("([^,]+)") do
@@ -156,12 +156,12 @@ function data_handle(data)
else return "-\r\n" end
end
if #af_table > 25 then return "-\r\n" end
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, af_table, data_val)
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, af_table, data_val)
return "+\r\n"
elseif eon_type == "dt" then
local dt_val = tonumber(value, 16)
if not dt_val then return "-\r\n" end
set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, dt_val)
rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, dt_val)
return "+\r\n"
else return "?\r\n" end
end
@@ -179,7 +179,7 @@ function data_handle(data)
end
if #groups > 8 or #groups == 0 then return "-\r\n" end
set_rds_udg(xy, groups)
rds.set_udg(xy, groups)
return "+\r\n"
end
if udg2_num then
@@ -193,7 +193,7 @@ function data_handle(data)
end
if #groups > 8 or #groups == 0 then return "-" end
set_rds_udg2(xy, groups)
rds.set_udg2(xy, groups)
return "+\r\n"
end
@@ -201,67 +201,67 @@ function data_handle(data)
local pi = tonumber(value, 16)
if not pi then return "-\r\n" end
if (pi & 0xF000) == 0 then return "-\r\n" end
set_rds_pi(pi)
rds.set_pi(pi)
return "+\r\n"
elseif cmd == "ecc" then
local ecc = tonumber(value, 16)
if not ecc then return "-\r\n" end
set_rds_ecc(ecc)
rds.set_ecc(ecc)
return "+\r\n"
elseif cmd == "pty" then
local pty = tonumber(value)
if not pty then return "-\r\n" end
set_rds_pty(pty)
rds.set_pty(pty)
return "+\r\n"
elseif cmd == "slcd" then
local slc_data = tonumber(value, 16)
if not slc_data then return "-\r\n" end
set_rds_slc_data(slc_data)
rds.set_slc_data(slc_data)
return "+\r\n"
elseif cmd == "ct" then
local ct = tonumber(value)
if not ct then return "-\r\n" end
set_rds_ct(ct ~= 0)
rds.set_ct(ct ~= 0)
return "+\r\n"
elseif cmd == "dpty" then
local dpty = tonumber(value)
if not dpty then return "-\r\n" end
set_rds_dpty(dpty ~= 0)
rds.set_dpty(dpty ~= 0)
return "+\r\n"
elseif cmd == "tp" then
local tp = tonumber(value)
if not tp then return "-\r\n" end
set_rds_tp(tp ~= 0)
rds.set_tp(tp ~= 0)
return "+\r\n"
elseif cmd == "ta" then
local ta = tonumber(value)
if not ta then return "-\r\n" end
set_rds_ta(ta ~= 0)
rds.set_ta(ta ~= 0)
return "+\r\n"
elseif cmd == "rt1en" then
local en = tonumber(value)
if not en then return "-\r\n" end
set_rds_rt1_enabled(en ~= 0)
rds.set_rt1_enabled(en ~= 0)
return "+\r\n"
elseif cmd == "rt2en" then
local en = tonumber(value)
if not en then return "-\r\n" end
set_rds_rt2_enabled(en ~= 0)
rds.set_rt2_enabled(en ~= 0)
return "+\r\n"
elseif cmd == "ptynen" then
local en = tonumber(value)
if not en then return "-\r\n" end
set_rds_ptyn_enabled(en ~= 0)
rds.set_ptyn_enabled(en ~= 0)
return "+\r\n"
elseif cmd == "rttype" then
local type = tonumber(value)
if not type then return "-\r\n" end
set_rds_rt_type(type)
rds.set_rt_type(type)
return "+\r\n"
elseif cmd == "rds2mod" then
local type = tonumber(value)
if not type then return "-\r\n" end
set_rds2_mode(type)
rds.set_rds2_mode(type)
return "+\r\n"
elseif cmd == "rdsgen" then
local type = tonumber(value)
@@ -269,42 +269,42 @@ function data_handle(data)
set_rds_streams(type)
return "+\r\n"
elseif cmd == "ptyn" then
set_rds_ptyn(value)
rds.set_ptyn(value)
return "+\r\n"
elseif cmd == "ps" then
set_rds_ps(value)
rds.set_ps(value)
return "+\r\n"
elseif cmd == "tps" then
set_rds_tps(value)
rds.set_tps(value)
return "+\r\n"
elseif cmd == "rt1" or cmd == "text" then
set_rds_rt1(value)
rds.set_rt1(value)
return "+\r\n"
elseif cmd == "rt2" then
set_rds_rt2(value)
rds.set_rt2(value)
return "+\r\n"
elseif cmd == "lps" then
set_rds_lps(value)
rds.set_lps(value)
return "+\r\n"
elseif cmd == "ert" then
set_rds_ert(value)
rds.ext.set_ert(value)
return "+\r\n"
elseif cmd == "link" then
local link = tonumber(value)
if not link then return "-\r\n" end
set_rds_link(link ~= 0)
rds.set_link(link ~= 0)
return "+\r\n"
elseif cmd == "rtper" then
local period = tonumber(value)
if not period then return "-\r\n" end
set_rds_rt_switching_period(period*60)
rds.set_rt_switching_period(period*60)
return "+\r\n"
elseif cmd == "program" then
local program = tonumber(value)
if not program then return "-\r\n" end
if program < 1 or program > max_programs then return "-\r\n" end
set_rds_program(program-1)
set_rds_ta(false)
if program < 1 or program > dp.max_programs then return "-\r\n" end
dp.set_rds_program(program-1)
rds.set_ta(false)
return "+\r\n"
elseif cmd == "level" then
local level = tonumber(value)
@@ -314,20 +314,20 @@ function data_handle(data)
elseif cmd == "dttmout" then
local timeout = tonumber(value)
if not timeout then return "-\r\n" end
set_rds_rt_text_timeout(timeout*60)
rds.set_rt_text_timeout(timeout*60)
return "+\r\n"
elseif cmd == "grpseq" then
set_rds_grpseq(value)
rds.set_grpseq(value)
return "+\r\n"
elseif cmd == "grpseq2" then
set_rds_grpseq2(value)
rds.set_grpseq2(value)
return "+\r\n"
elseif cmd == "rtp" or cmd == "ertp" then
local is_ertp = (cmd == "ertp")
local t1, s1, l1, t2, s2, l2 = value:match("(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)")
if not l2 then return "-\r\n" end
set_rds_rtplus_tags(
rds.ext.set_rtplus_tags(
is_ertp,
---@diagnostic disable-next-line: param-type-mismatch
tonumber(t1), tonumber(s1), tonumber(l1), tonumber(t2), tonumber(s2), tonumber(l2)
@@ -336,13 +336,13 @@ function data_handle(data)
elseif cmd == "g" then
local a, b, c, d = value:match("^(%x%x%x%x)(%x%x%x%x)(%x%x%x%x)(%x%x%x%x)$")
if a and b and c and d then
put_rds2_custom_group(tonumber(a, 16), tonumber(b, 16), tonumber(c, 16), tonumber(d, 16))
rds.put_rds2_custom_group(tonumber(a, 16), tonumber(b, 16), tonumber(c, 16), tonumber(d, 16))
return "+\r\n"
end
local b1, c1, d1 = value:match("^(%x%x%x%x)(%x%x%x%x)(%x%x%x%x)$")
if b1 and c1 and d1 then
put_rds_custom_group(tonumber(b1, 16), tonumber(c1, 16), tonumber(d1, 16))
rds.put_custom_group(tonumber(b1, 16), tonumber(c1, 16), tonumber(d1, 16))
return "+\r\n"
end
@@ -357,14 +357,14 @@ function data_handle(data)
local f2 = tonumber(f2_str) or 0
local running = (f1 & 1) ~= 0
set_rds_rtp_meta(is_ertp, running)
if f2 ~= 0 then toggle_rds_rtp(is_ertp) end
rds.ext.set_rtp_meta(is_ertp, running)
if f2 ~= 0 then rds.ext.toggle_rtp(is_ertp) end
return "+\r\n"
elseif cmd == "af" then
local af_table = {}
if value == "" or value == "0" then
set_rds_af_group0({})
rds.set_af({})
return "+\r\n"
end
@@ -376,13 +376,13 @@ function data_handle(data)
if #af_table > 25 then return "-\r\n" end
set_rds_af_group0(af_table)
rds.set_af(af_table)
return "+\r\n"
elseif cmd == "afo" then
local af_table = {}
if value == "" or value == "0" then
set_rds_af_oda({})
rds.ext.set_af_oda({})
return "+\r\n"
end
@@ -394,7 +394,7 @@ function data_handle(data)
if #af_table > 25 then return "-\r\n" end
set_rds_af_oda(af_table)
rds.ext.set_af_oda(af_table)
return "+\r\n"
else
return "?\r\n"

425
src/lua_api.c Normal file
View File

@@ -0,0 +1,425 @@
#include "lua_api.h"
#include "lua_rds.h"
static int in_set_defaults = 0;
extern lua_State *L;
extern RDSModulator* mod;
extern uint8_t unload_refs[33];
int lua_get_userdata(lua_State *localL) {
lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data, LUA_USER_DATA);
return 1;
}
int lua_get_userdata_offset(lua_State *localL) {
uint16_t offset = luaL_checkinteger(localL, 1);
uint16_t size = luaL_checkinteger(localL, 2);
if((offset+size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit");
lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data[offset], size);
return 1;
}
int lua_set_userdata(lua_State *localL) {
size_t len;
const char *data = luaL_checklstring(localL, 1, &len);
if(len > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit");
memset(mod->enc->data[mod->enc->program].lua_data, 0, LUA_USER_DATA);
memcpy(mod->enc->data[mod->enc->program].lua_data, data, len);
return 0;
}
int lua_set_userdata_offset(lua_State *localL) {
uint16_t offset = luaL_checkinteger(localL, 1);
uint16_t size = luaL_checkinteger(localL, 2);
size_t len;
const char *data = luaL_checklstring(localL, 3, &len);
if(len > size || (offset + size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit");
memset(mod->enc->data[mod->enc->program].lua_data + offset, 0, size);
memcpy(mod->enc->data[mod->enc->program].lua_data + offset, data, len);
return 0;
}
int lua_force_save(lua_State *localL) {
(void)localL;
encoder_saveToFile(mod->enc);
Modulator_saveToFile(&mod->params);
return 0;
}
int lua_set_rds_program_defaults(lua_State *localL) {
(void)localL;
if (in_set_defaults) {
fprintf(stderr, "Warning: Recursive call to lua_set_rds_program_defaults blocked\n");
return 0;
}
in_set_defaults = 1;
for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]);
unload_refs[0] = 1;
set_rds_defaults(mod->enc, mod->enc->program);
lua_call_tfunction_nolock("on_init");
lua_call_tfunction_nolock("on_state");
in_set_defaults = 0;
return 0;
}
int lua_reset_rds(lua_State *localL) {
(void)localL;
for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]);
unload_refs[0] = 1;
encoder_saveToFile(mod->enc);
Modulator_saveToFile(&mod->params);
encoder_loadFromFile(mod->enc);
for(int i = 0; i < PROGRAMS; i++) reset_rds_state(mod->enc, i);
Modulator_loadFromFile(&mod->params);
lua_call_tfunction_nolock("on_state");
return 0;
}
#define BOOL_SETTER(name) \
int lua_set_rds_##name(lua_State *localL) { \
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); \
mod->enc->data[mod->enc->program].name = lua_toboolean(localL, 1); \
return 0; \
}
#define INT_SETTER(name) \
int lua_set_rds_##name(lua_State *localL) { \
mod->enc->data[mod->enc->program].name = luaL_checkinteger(localL, 1); \
return 0; \
}
#define STR_SETTER(name, function) \
int lua_set_rds_##name(lua_State *localL) { \
const char* str = luaL_checklstring(localL, 1, NULL); \
function(mod->enc, convert_to_rdscharset(str)); \
return 0; \
}
#define STR_RAW_SETTER(name, function) \
int lua_set_rds_##name(lua_State *localL) { \
const char* str = luaL_checklstring(localL, 1, NULL); \
function(mod->enc, str); \
return 0; \
}
#define AF_SETTER(name, af_field, af_struct, add_func) \
int lua_set_rds_##name(lua_State *localL) { \
luaL_checktype(localL, 1, LUA_TTABLE); \
\
int n = lua_rawlen(localL, 1); \
if (n == 0) { \
memset(&(mod->enc->data[mod->enc->program].af_field), 0, sizeof(af_struct)); \
return 0; \
} \
if(n > 25) return luaL_error(localL, "table length over 25"); \
\
af_struct new_af; \
memset(&new_af, 0, sizeof(af_struct)); \
\
for (int i = 1; i <= n; i++) { \
lua_rawgeti(localL, 1, i); \
if (lua_isnumber(localL, -1)) add_func(&new_af, lua_tonumber(localL, -1)); \
else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1)); \
lua_pop(localL, 1); \
} \
memcpy(&(mod->enc->data[mod->enc->program].af_field), &new_af, sizeof(new_af)); \
\
return 0; \
}
#define INT_GETTER(name) \
int lua_get_rds_##name(lua_State *localL) { \
lua_pushinteger(localL, mod->enc->data[mod->enc->program].name); \
return 1; \
}
#define BOOL_GETTER(name) \
int lua_get_rds_##name(lua_State *localL) { \
lua_pushboolean(localL, mod->enc->data[mod->enc->program].name); \
return 1; \
}
#define STR_RAW_GETTER(name, length) \
int lua_get_rds_##name(lua_State *localL) { \
lua_pushlstring(localL, mod->enc->data[mod->enc->program].name, length); \
return 1; \
}
INT_SETTER(pi)
INT_GETTER(pi)
INT_SETTER(pty)
INT_GETTER(pty)
INT_SETTER(ecc)
INT_GETTER(ecc)
INT_SETTER(slc_data)
INT_GETTER(slc_data)
BOOL_SETTER(ct)
BOOL_GETTER(ct)
BOOL_SETTER(dpty)
BOOL_GETTER(dpty)
BOOL_SETTER(tp)
BOOL_GETTER(tp)
BOOL_SETTER(ta)
BOOL_GETTER(ta)
BOOL_SETTER(rt1_enabled)
BOOL_GETTER(rt1_enabled)
BOOL_SETTER(rt2_enabled)
BOOL_GETTER(rt2_enabled)
BOOL_SETTER(ptyn_enabled)
BOOL_GETTER(ptyn_enabled)
INT_SETTER(rt_type)
INT_GETTER(rt_type)
int lua_set_rds2_mode(lua_State *localL) {
mod->enc->encoder_data.rds2_mode = luaL_checkinteger(localL, 1);
return 0;
}
int lua_get_rds2_mode(lua_State *localL) {
lua_pushinteger(localL, mod->enc->encoder_data.rds2_mode);
return 1;
}
int lua_set_rds_streams(lua_State *localL) {
mod->params.rdsgen = luaL_checkinteger(localL, 1);
return 0;
}
int lua_get_rds_streams(lua_State *localL) {
lua_pushinteger(localL, mod->params.rdsgen);
return 1;
}
int lua_set_rds_link(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
mod->enc->state[mod->enc->program].eon_linkage = lua_toboolean(localL, 1);
return 0;
}
int lua_get_rds_link(lua_State *localL) {
lua_pushboolean(localL, mod->enc->state[mod->enc->program].eon_linkage);
return 1;
}
int lua_set_rds_program(lua_State *localL) {
int program = luaL_checkinteger(localL, 1);
if(program >= PROGRAMS) program = (PROGRAMS-1);
if(program < 0) program = 0;
if(mod->enc->program == program) return 0;
mod->enc->data[mod->enc->program].ta = 0;
mod->enc->data[(uint8_t)program].ta = 0;
mod->enc->program = (uint8_t)program;
return 0;
}
int lua_get_rds_program(lua_State *localL) {
lua_pushinteger(localL, mod->enc->program);
return 1;
}
int lua_set_rds_rt_switching_period(lua_State *localL) {
mod->enc->data[mod->enc->program].rt_switching_period = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].rt_switching_period_state = mod->enc->data[mod->enc->program].rt_switching_period;
return 0;
}
INT_GETTER(rt_switching_period)
int lua_set_rds_rt_text_timeout(lua_State *localL) {
mod->enc->data[mod->enc->program].rt_text_timeout = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].rt_text_timeout_state = mod->enc->data[mod->enc->program].rt_text_timeout;
return 0;
}
INT_GETTER(rt_text_timeout)
int lua_set_rds_level(lua_State *localL) {
mod->params.level = luaL_checknumber(localL, 1);
return 0;
}
int lua_get_rds_level(lua_State *localL) {
lua_pushnumber(localL, mod->params.level);
return 1;
}
int lua_put_rds_custom_group(lua_State *localL) {
mod->enc->state[mod->enc->program].custom_group[0] = 1;
mod->enc->state[mod->enc->program].custom_group[1] = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].custom_group[2] = luaL_checkinteger(localL, 2);
mod->enc->state[mod->enc->program].custom_group[3] = luaL_checkinteger(localL, 3);
return 0;
}
int lua_put_rds2_custom_group(lua_State *localL) {
mod->enc->state[mod->enc->program].custom_group2[0] = 1;
mod->enc->state[mod->enc->program].custom_group2[1] = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].custom_group2[2] = luaL_checkinteger(localL, 2);
mod->enc->state[mod->enc->program].custom_group2[3] = luaL_checkinteger(localL, 3);
mod->enc->state[mod->enc->program].custom_group2[4] = luaL_checkinteger(localL, 4);
return 0;
}
STR_SETTER(ptyn, set_rds_ptyn)
STR_SETTER(ps, set_rds_ps)
STR_SETTER(tps, set_rds_tps)
STR_SETTER(rt1, set_rds_rt1)
STR_SETTER(rt2, set_rds_rt2)
STR_SETTER(default_rt, set_rds_default_rt)
STR_RAW_SETTER(lps, set_rds_lps)
STR_RAW_GETTER(lps, LPS_LENGTH)
STR_RAW_SETTER(grp_sqc_rds2, set_rds_grpseq2)
STR_RAW_GETTER(grp_sqc_rds2, 32)
int lua_set_rds_grp_sqc(lua_State *localL) {
const char* str = luaL_checklstring(localL, 1, NULL);
if(_strnlen(str, 2) < 1) set_rds_grpseq(mod->enc, DEFAULT_GRPSQC);
else set_rds_grpseq(mod->enc, str);
return 0;
}
STR_RAW_GETTER(grp_sqc, 32)
AF_SETTER(af_group0, af, RDSAFs, add_rds_af)
int lua_set_rds_eon(lua_State *localL) {
int eon = luaL_checkinteger(localL, 1);
if(eon >= EONs) return luaL_error(localL, "eon index exceeded");
if (!lua_isboolean(localL, 2)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 2));
if (!lua_isboolean(localL, 4)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 4));
if (!lua_isboolean(localL, 5)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 5));
luaL_checktype(localL, 8, LUA_TTABLE);
mod->enc->data[mod->enc->program].eon[eon].enabled = lua_toboolean(localL, 2);
mod->enc->data[mod->enc->program].eon[eon].pi = luaL_checkinteger(localL, 3);
mod->enc->data[mod->enc->program].eon[eon].tp = lua_toboolean(localL, 4);
mod->enc->data[mod->enc->program].eon[eon].ta = lua_toboolean(localL, 5);
mod->enc->data[mod->enc->program].eon[eon].pty = luaL_checkinteger(localL, 6);
_strncpy(mod->enc->data[mod->enc->program].eon[eon].ps, luaL_checklstring(localL, 7, NULL), 8);
int n = lua_rawlen(localL, 8);
if (n == 0) {
memset(&(mod->enc->data[mod->enc->program].eon[eon].af), 0, sizeof(RDSAFs));
return 0;
}
if(n > 25) return luaL_error(localL, "table length over 25");
RDSAFs new_af;
memset(&new_af, 0, sizeof(RDSAFs));
for (int i = 1; i <= n; i++) {
lua_rawgeti(localL, 8, i);
if (lua_isnumber(localL, -1)) add_rds_af(&new_af, lua_tonumber(localL, -1));
else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
memcpy(&(mod->enc->data[mod->enc->program].eon[eon].af), &new_af, sizeof(new_af));
mod->enc->data[mod->enc->program].eon[eon].data = luaL_checkinteger(localL, 9);
return 0;
}
int lua_get_rds_eon(lua_State *localL) {
int eon = luaL_checkinteger(localL, 1);
if(eon >= EONs) return luaL_error(localL, "eon index exceeded");
lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].enabled);
lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pi);
lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].tp);
lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].ta);
lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pty);
lua_pushlstring(localL, mod->enc->data[mod->enc->program].eon[eon].ps, 8);
lua_createtable(localL, 0, 0); // don't have decoding for AF, so just return empty table
lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].data);
return 8;
}
int lua_set_rds_udg(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int xy = lua_toboolean(localL, 1);
luaL_checktype(localL, 2, LUA_TTABLE);
int n = lua_rawlen(localL, 2);
if(n > 8) return luaL_error(localL, "table length over 8");
uint16_t blocks[8][3] = {0};
for (int i = 1; i <= n; i++) {
lua_rawgeti(localL, 2, i);
if(lua_istable(localL, -1)) {
int n2 = lua_rawlen(localL, -1);
if(n2 > 3) return luaL_error(localL, "table length over 3");
for(int j = 1; j <= n2; j++) {
lua_rawgeti(localL, -1, j);
if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1);
else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
}
else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
if(xy) {
memcpy(&(mod->enc->data[mod->enc->program].udg2), blocks, n * sizeof(uint16_t[3]));
mod->enc->data[mod->enc->program].udg2_len = n;
} else {
memcpy(&(mod->enc->data[mod->enc->program].udg1), blocks, n * sizeof(uint16_t[3]));
mod->enc->data[mod->enc->program].udg1_len = n;
}
return 0;
}
int lua_set_rds_udg2(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int xy = lua_toboolean(localL, 1);
luaL_checktype(localL, 2, LUA_TTABLE);
int n = lua_rawlen(localL, 2);
if(n > 8) return luaL_error(localL, "table length over 8");
uint16_t blocks[8][4] = {0};
for (int i = 1; i <= n; i++) {
lua_rawgeti(localL, 2, i);
if(lua_istable(localL, -1)) {
int n2 = lua_rawlen(localL, -1);
if(n2 > 4) return luaL_error(localL, "table length over 4");
for(int j = 1; j <= n2; j++) {
lua_rawgeti(localL, -1, j);
if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1);
else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
}
else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
if(xy) {
memcpy(&(mod->enc->data[mod->enc->program].udg2_rds2), blocks, n * sizeof(uint16_t[4]));
mod->enc->data[mod->enc->program].udg2_len_rds2 = n;
} else {
memcpy(&(mod->enc->data[mod->enc->program].udg1_rds2), blocks, n * sizeof(uint16_t[4]));
mod->enc->data[mod->enc->program].udg1_len_rds2 = n;
}
return 0;
}
int lua_get_available_rds_streams(lua_State *localL) {
lua_pushinteger(localL, mod->num_streams);
return 1;
}
int lua_crc16(lua_State *localL) {
size_t len;
const char* data = luaL_checklstring(localL, 1, &len);
lua_pushinteger(localL, crc16_ccitt(data, len));
return 1;
}

101
src/lua_api.h Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "rds.h"
#include "fs.h"
#include "modulator.h"
int lua_get_userdata(lua_State *localL);
int lua_get_userdata_offset(lua_State *localL);
int lua_set_userdata(lua_State *localL);
int lua_set_userdata_offset(lua_State *localL);
int lua_force_save(lua_State *localL);
int lua_set_rds_program_defaults(lua_State *localL);
int lua_reset_rds(lua_State *localL);
int lua_set_rds_pi(lua_State *localL);
int lua_get_rds_pi(lua_State *localL);
int lua_set_rds_pty(lua_State *localL);
int lua_get_rds_pty(lua_State *localL);
int lua_set_rds_ecc(lua_State *localL);
int lua_get_rds_ecc(lua_State *localL);
int lua_set_rds_slc_data(lua_State *localL);
int lua_get_rds_slc_data(lua_State *localL);
int lua_set_rds_ct(lua_State *localL);
int lua_get_rds_ct(lua_State *localL);
int lua_set_rds_dpty(lua_State *localL);
int lua_get_rds_dpty(lua_State *localL);
int lua_set_rds_tp(lua_State *localL);
int lua_get_rds_tp(lua_State *localL);
int lua_set_rds_ta(lua_State *localL);
int lua_get_rds_ta(lua_State *localL);
int lua_set_rds_rt1_enabled(lua_State *localL);
int lua_get_rds_rt1_enabled(lua_State *localL);
int lua_set_rds_rt2_enabled(lua_State *localL);
int lua_get_rds_rt2_enabled(lua_State *localL);
int lua_set_rds_ptyn_enabled(lua_State *localL);
int lua_get_rds_ptyn_enabled(lua_State *localL);
int lua_set_rds_rt_type(lua_State *localL);
int lua_get_rds_rt_type(lua_State *localL);
int lua_set_rds2_mode(lua_State *localL);
int lua_get_rds2_mode(lua_State *localL);
int lua_set_rds_streams(lua_State *localL);
int lua_get_rds_streams(lua_State *localL);
int lua_get_available_rds_streams(lua_State *localL);
int lua_set_rds_grp_sqc(lua_State *localL);
int lua_get_rds_grp_sqc(lua_State *localL);
int lua_set_rds_grp_sqc_rds2(lua_State *localL);
int lua_get_rds_grp_sqc_rds2(lua_State *localL);
int lua_set_rds_link(lua_State *localL);
int lua_get_rds_link(lua_State *localL);
int lua_set_rds_program(lua_State *localL);
int lua_get_rds_program(lua_State *localL);
int lua_set_rds_rt_switching_period(lua_State *localL);
int lua_get_rds_rt_switching_period(lua_State *localL);
int lua_set_rds_rt_text_timeout(lua_State *localL);
int lua_get_rds_rt_text_timeout(lua_State *localL);
int lua_set_rds_level(lua_State *localL);
int lua_get_rds_level(lua_State *localL);
int lua_put_rds_custom_group(lua_State *localL);
int lua_put_rds2_custom_group(lua_State *localL);
int lua_set_rds_lps(lua_State *localL);
int lua_get_rds_lps(lua_State *localL);
int lua_set_rds_af_group0(lua_State *localL);
int lua_set_rds_default_rt(lua_State *localL);
int lua_set_rds_rt2(lua_State *localL);
int lua_set_rds_rt1(lua_State *localL);
int lua_set_rds_tps(lua_State *localL);
int lua_set_rds_ps(lua_State *localL);
int lua_set_rds_ptyn(lua_State *localL);
int lua_set_rds_grp_sqc(lua_State *localL);
int lua_set_rds_eon(lua_State *localL);
int lua_get_rds_eon(lua_State *localL);
int lua_set_rds_udg(lua_State *localL);
int lua_set_rds_udg2(lua_State *localL);
int lua_crc16(lua_State *localL);

View File

@@ -1,418 +1,13 @@
#include "lua_rds.h"
#include <pthread.h>
#include "lua_api.h"
static RDSModulator* mod = NULL;
static lua_State *L = NULL;
RDSModulator* mod = NULL;
lua_State *L = NULL;
static pthread_mutex_t lua_mutex;
static uint8_t unload_refs[33] = {LUA_REFNIL};
static int in_set_defaults = 0;
int lua_get_userdata(lua_State *localL) {
lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data, LUA_USER_DATA);
return 1;
}
int lua_get_userdata_offset(lua_State *localL) {
uint16_t offset = luaL_checkinteger(localL, 1);
uint16_t size = luaL_checkinteger(localL, 2);
if((offset+size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit");
lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data[offset], size);
return 1;
}
int lua_set_userdata(lua_State *localL) {
size_t len;
const char *data = luaL_checklstring(localL, 1, &len);
if(len > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit");
memset(mod->enc->data[mod->enc->program].lua_data, 0, LUA_USER_DATA);
memcpy(mod->enc->data[mod->enc->program].lua_data, data, len);
return 0;
}
int lua_set_userdata_offset(lua_State *localL) {
uint16_t offset = luaL_checkinteger(localL, 1);
uint16_t size = luaL_checkinteger(localL, 2);
size_t len;
const char *data = luaL_checklstring(localL, 3, &len);
if(len > size || (offset + size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit");
memset(mod->enc->data[mod->enc->program].lua_data + offset, 0, size);
memcpy(mod->enc->data[mod->enc->program].lua_data + offset, data, len);
return 0;
}
int lua_force_save(lua_State *localL) {
(void)localL;
encoder_saveToFile(mod->enc);
Modulator_saveToFile(&mod->params);
return 0;
}
int lua_set_rds_program_defaults(lua_State *localL) {
(void)localL;
if (in_set_defaults) {
fprintf(stderr, "Warning: Recursive call to lua_set_rds_program_defaults blocked\n");
return 0;
}
in_set_defaults = 1;
for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]);
unload_refs[0] = 1;
set_rds_defaults(mod->enc, mod->enc->program);
lua_call_function_nolock("on_init");
lua_call_function_nolock("on_state");
in_set_defaults = 0;
return 0;
}
int lua_reset_rds(lua_State *localL) {
(void)localL;
for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]);
unload_refs[0] = 1;
encoder_saveToFile(mod->enc);
Modulator_saveToFile(&mod->params);
encoder_loadFromFile(mod->enc);
for(int i = 0; i < PROGRAMS; i++) reset_rds_state(mod->enc, i);
Modulator_loadFromFile(&mod->params);
lua_call_function_nolock("on_state");
return 0;
}
#define BOOL_SETTER(name) \
int lua_set_rds_##name(lua_State *localL) { \
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); \
mod->enc->data[mod->enc->program].name = lua_toboolean(localL, 1); \
return 0; \
}
#define INT_SETTER(name) \
int lua_set_rds_##name(lua_State *localL) { \
mod->enc->data[mod->enc->program].name = luaL_checkinteger(localL, 1); \
return 0; \
}
#define STR_SETTER(name, function) \
int lua_set_rds_##name(lua_State *localL) { \
const char* str = luaL_checklstring(localL, 1, NULL); \
function(mod->enc, convert_to_rdscharset(str)); \
return 0; \
}
#define STR_RAW_SETTER(name, function) \
int lua_set_rds_##name(lua_State *localL) { \
const char* str = luaL_checklstring(localL, 1, NULL); \
function(mod->enc, str); \
return 0; \
}
#define AF_SETTER(name, af_field, af_struct, add_func) \
int lua_set_rds_##name(lua_State *localL) { \
luaL_checktype(localL, 1, LUA_TTABLE); \
\
int n = lua_rawlen(localL, 1); \
if (n == 0) { \
memset(&(mod->enc->data[mod->enc->program].af_field), 0, sizeof(af_struct)); \
return 0; \
} \
if(n > 25) return luaL_error(localL, "table length over 25"); \
\
af_struct new_af; \
memset(&new_af, 0, sizeof(af_struct)); \
\
for (int i = 1; i <= n; i++) { \
lua_rawgeti(localL, 1, i); \
if (lua_isnumber(localL, -1)) add_func(&new_af, lua_tonumber(localL, -1)); \
else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1)); \
lua_pop(localL, 1); \
} \
memcpy(&(mod->enc->data[mod->enc->program].af_field), &new_af, sizeof(new_af)); \
\
return 0; \
}
#define INT_GETTER(name) \
int lua_get_rds_##name(lua_State *localL) { \
lua_pushinteger(localL, mod->enc->data[mod->enc->program].name); \
return 1; \
}
#define BOOL_GETTER(name) \
int lua_get_rds_##name(lua_State *localL) { \
lua_pushboolean(localL, mod->enc->data[mod->enc->program].name); \
return 1; \
}
#define STR_RAW_GETTER(name, length) \
int lua_get_rds_##name(lua_State *localL) { \
lua_pushlstring(localL, mod->enc->data[mod->enc->program].name, length); \
return 1; \
}
INT_SETTER(pi)
INT_GETTER(pi)
INT_SETTER(pty)
INT_GETTER(pty)
INT_SETTER(ecc)
INT_GETTER(ecc)
INT_SETTER(slc_data)
INT_GETTER(slc_data)
BOOL_SETTER(ct)
BOOL_GETTER(ct)
BOOL_SETTER(dpty)
BOOL_GETTER(dpty)
BOOL_SETTER(tp)
BOOL_GETTER(tp)
BOOL_SETTER(ta)
BOOL_GETTER(ta)
BOOL_SETTER(rt1_enabled)
BOOL_GETTER(rt1_enabled)
BOOL_SETTER(rt2_enabled)
BOOL_GETTER(rt2_enabled)
BOOL_SETTER(ptyn_enabled)
BOOL_GETTER(ptyn_enabled)
INT_SETTER(rt_type)
INT_GETTER(rt_type)
int lua_set_rds2_mode(lua_State *localL) {
mod->enc->encoder_data.rds2_mode = luaL_checkinteger(localL, 1);
return 0;
}
int lua_get_rds2_mode(lua_State *localL) {
lua_pushinteger(localL, mod->enc->encoder_data.rds2_mode);
return 1;
}
int lua_set_rds_streams(lua_State *localL) {
mod->params.rdsgen = luaL_checkinteger(localL, 1);
return 0;
}
int lua_get_rds_streams(lua_State *localL) {
lua_pushinteger(localL, mod->params.rdsgen);
return 1;
}
int lua_set_rds_link(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
mod->enc->state[mod->enc->program].eon_linkage = lua_toboolean(localL, 1);
return 0;
}
int lua_get_rds_link(lua_State *localL) {
lua_pushboolean(localL, mod->enc->state[mod->enc->program].eon_linkage);
return 1;
}
int lua_set_rds_program(lua_State *localL) {
int program = luaL_checkinteger(localL, 1);
if(program >= PROGRAMS) program = (PROGRAMS-1);
if(program < 0) program = 0;
if(mod->enc->program == program) return 0;
mod->enc->data[mod->enc->program].ta = 0;
mod->enc->data[(uint8_t)program].ta = 0;
mod->enc->program = (uint8_t)program;
return 0;
}
int lua_get_rds_program(lua_State *localL) {
lua_pushinteger(localL, mod->enc->program);
return 1;
}
int lua_set_rds_rt_switching_period(lua_State *localL) {
mod->enc->data[mod->enc->program].rt_switching_period = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].rt_switching_period_state = mod->enc->data[mod->enc->program].rt_switching_period;
return 0;
}
INT_GETTER(rt_switching_period)
int lua_set_rds_rt_text_timeout(lua_State *localL) {
mod->enc->data[mod->enc->program].rt_text_timeout = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].rt_text_timeout_state = mod->enc->data[mod->enc->program].rt_text_timeout;
return 0;
}
INT_GETTER(rt_text_timeout)
int lua_set_rds_level(lua_State *localL) {
mod->params.level = luaL_checknumber(localL, 1);
return 0;
}
int lua_get_rds_level(lua_State *localL) {
lua_pushnumber(localL, mod->params.level);
return 1;
}
int lua_put_rds_custom_group(lua_State *localL) {
mod->enc->state[mod->enc->program].custom_group[0] = 1;
mod->enc->state[mod->enc->program].custom_group[1] = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].custom_group[2] = luaL_checkinteger(localL, 2);
mod->enc->state[mod->enc->program].custom_group[3] = luaL_checkinteger(localL, 3);
return 0;
}
int lua_put_rds2_custom_group(lua_State *localL) {
mod->enc->state[mod->enc->program].custom_group2[0] = 1;
mod->enc->state[mod->enc->program].custom_group2[1] = luaL_checkinteger(localL, 1);
mod->enc->state[mod->enc->program].custom_group2[2] = luaL_checkinteger(localL, 2);
mod->enc->state[mod->enc->program].custom_group2[3] = luaL_checkinteger(localL, 3);
mod->enc->state[mod->enc->program].custom_group2[4] = luaL_checkinteger(localL, 4);
return 0;
}
STR_SETTER(ptyn, set_rds_ptyn)
STR_SETTER(ps, set_rds_ps)
STR_SETTER(tps, set_rds_tps)
STR_SETTER(rt1, set_rds_rt1)
STR_SETTER(rt2, set_rds_rt2)
STR_SETTER(default_rt, set_rds_default_rt)
STR_RAW_SETTER(lps, set_rds_lps)
STR_RAW_GETTER(lps, LPS_LENGTH)
STR_RAW_SETTER(grp_sqc_rds2, set_rds_grpseq2)
STR_RAW_GETTER(grp_sqc_rds2, 32)
int lua_set_rds_grp_sqc(lua_State *localL) {
const char* str = luaL_checklstring(localL, 1, NULL);
if(_strnlen(str, 2) < 1) set_rds_grpseq(mod->enc, DEFAULT_GRPSQC);
else set_rds_grpseq(mod->enc, str);
return 0;
}
STR_RAW_GETTER(grp_sqc, 32)
AF_SETTER(af_group0, af, RDSAFs, add_rds_af)
int lua_set_rds_eon(lua_State *localL) {
int eon = luaL_checkinteger(localL, 1);
if(eon >= EONs) return luaL_error(localL, "eon index exceeded");
if (!lua_isboolean(localL, 2)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 2));
if (!lua_isboolean(localL, 4)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 4));
if (!lua_isboolean(localL, 5)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 5));
luaL_checktype(localL, 8, LUA_TTABLE);
mod->enc->data[mod->enc->program].eon[eon].enabled = lua_toboolean(localL, 2);
mod->enc->data[mod->enc->program].eon[eon].pi = luaL_checkinteger(localL, 3);
mod->enc->data[mod->enc->program].eon[eon].tp = lua_toboolean(localL, 4);
mod->enc->data[mod->enc->program].eon[eon].ta = lua_toboolean(localL, 5);
mod->enc->data[mod->enc->program].eon[eon].pty = luaL_checkinteger(localL, 6);
_strncpy(mod->enc->data[mod->enc->program].eon[eon].ps, luaL_checklstring(localL, 7, NULL), 8);
int n = lua_rawlen(localL, 8);
if (n == 0) {
memset(&(mod->enc->data[mod->enc->program].eon[eon].af), 0, sizeof(RDSAFs));
return 0;
}
if(n > 25) return luaL_error(localL, "table length over 25");
RDSAFs new_af;
memset(&new_af, 0, sizeof(RDSAFs));
for (int i = 1; i <= n; i++) {
lua_rawgeti(localL, 8, i);
if (lua_isnumber(localL, -1)) add_rds_af(&new_af, lua_tonumber(localL, -1));
else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
memcpy(&(mod->enc->data[mod->enc->program].eon[eon].af), &new_af, sizeof(new_af));
mod->enc->data[mod->enc->program].eon[eon].data = luaL_checkinteger(localL, 9);
return 0;
}
int lua_get_rds_eon(lua_State *localL) {
int eon = luaL_checkinteger(localL, 1);
if(eon >= EONs) return luaL_error(localL, "eon index exceeded");
lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].enabled);
lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pi);
lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].tp);
lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].ta);
lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pty);
lua_pushlstring(localL, mod->enc->data[mod->enc->program].eon[eon].ps, 8);
lua_createtable(localL, 0, 0); // don't have decoding for AF, so just return empty table
lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].data);
return 8;
}
int lua_set_rds_udg(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int xy = lua_toboolean(localL, 1);
luaL_checktype(localL, 2, LUA_TTABLE);
int n = lua_rawlen(localL, 2);
if(n > 8) return luaL_error(localL, "table length over 8");
uint16_t blocks[8][3] = {0};
for (int i = 1; i <= n; i++) {
lua_rawgeti(localL, 2, i);
if(lua_istable(localL, -1)) {
int n2 = lua_rawlen(localL, -1);
if(n2 > 3) return luaL_error(localL, "table length over 3");
for(int j = 1; j <= n2; j++) {
lua_rawgeti(localL, -1, j);
if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1);
else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
}
else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
if(xy) {
memcpy(&(mod->enc->data[mod->enc->program].udg2), blocks, n * sizeof(uint16_t[3]));
mod->enc->data[mod->enc->program].udg2_len = n;
} else {
memcpy(&(mod->enc->data[mod->enc->program].udg1), blocks, n * sizeof(uint16_t[3]));
mod->enc->data[mod->enc->program].udg1_len = n;
}
return 0;
}
int lua_set_rds_udg2(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int xy = lua_toboolean(localL, 1);
luaL_checktype(localL, 2, LUA_TTABLE);
int n = lua_rawlen(localL, 2);
if(n > 8) return luaL_error(localL, "table length over 8");
uint16_t blocks[8][4] = {0};
for (int i = 1; i <= n; i++) {
lua_rawgeti(localL, 2, i);
if(lua_istable(localL, -1)) {
int n2 = lua_rawlen(localL, -1);
if(n2 > 4) return luaL_error(localL, "table length over 4");
for(int j = 1; j <= n2; j++) {
lua_rawgeti(localL, -1, j);
if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1);
else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
}
else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1));
lua_pop(localL, 1);
}
if(xy) {
memcpy(&(mod->enc->data[mod->enc->program].udg2_rds2), blocks, n * sizeof(uint16_t[4]));
mod->enc->data[mod->enc->program].udg2_len_rds2 = n;
} else {
memcpy(&(mod->enc->data[mod->enc->program].udg1_rds2), blocks, n * sizeof(uint16_t[4]));
mod->enc->data[mod->enc->program].udg1_len_rds2 = n;
}
return 0;
}
int lua_get_available_rds_streams(lua_State *localL) {
lua_pushinteger(localL, mod->num_streams);
return 1;
}
int lua_crc16(lua_State *localL) {
size_t len;
const char* data = luaL_checklstring(localL, 1, &len);
lua_pushinteger(localL, crc16_ccitt(data, len));
return 1;
}
uint8_t unload_refs[33] = {LUA_REFNIL};
#define lua_registertotable(L,n,f) (lua_pushcfunction(L, (f)), lua_setfield(L, -2, (n)))
void init_lua(RDSModulator* rds_mod) {
static int mutex_initialized = 0;
mod = rds_mod;
@@ -430,109 +25,131 @@ void init_lua(RDSModulator* rds_mod) {
lua_pushstring(L, VERSION);
lua_setglobal(L, "core_version");
lua_pushinteger(L, PROGRAMS);
lua_setglobal(L, "max_programs");
lua_pushinteger(L, EONs);
lua_setglobal(L, "eon_count");
lua_newtable(L);
lua_setglobal(L, "ext");
lua_newtable(L);
lua_setglobal(L, "on_inits");
lua_newtable(L);
lua_setglobal(L, "on_starts");
lua_newtable(L);
lua_setglobal(L, "on_states");
lua_newtable(L);
lua_setglobal(L, "ticks");
lua_newtable(L);
lua_registertotable(L, "get", lua_get_userdata);
lua_registertotable(L, "get_offset", lua_get_userdata_offset);
lua_registertotable(L, "set", lua_set_userdata);
lua_registertotable(L, "set_offset", lua_set_userdata_offset);
lua_pushinteger(L, LUA_USER_DATA);
lua_setglobal(L, "user_data_len");
lua_setfield(L, -2, "len");
lua_setglobal(L, "userdata");
lua_register(L, "set_rds_program_defaults", lua_set_rds_program_defaults);
lua_register(L, "reset_rds", lua_reset_rds);
lua_register(L, "force_save", lua_force_save);
lua_newtable(L);
lua_registertotable(L, "crc16", lua_crc16);
lua_registertotable(L, "force_save", lua_force_save);
lua_registertotable(L, "reset_rds", lua_reset_rds);
lua_registertotable(L, "set_rds_program_defaults", lua_set_rds_program_defaults);
lua_pushinteger(L, PROGRAMS);
lua_setfield(L, -2, "max_programs");
lua_registertotable(L, "set_rds_program", lua_set_rds_program);
lua_registertotable(L, "get_rds_program", lua_get_rds_program);
lua_setglobal(L, "dp");
lua_register(L, "set_rds_pi", lua_set_rds_pi);
lua_register(L, "get_rds_pi", lua_get_rds_pi);
lua_newtable(L);
lua_register(L, "set_rds_pty", lua_set_rds_pty);
lua_register(L, "get_rds_pty", lua_get_rds_pty);
lua_newtable(L);
lua_setfield(L, -2, "ext");
lua_register(L, "set_rds_ecc", lua_set_rds_ecc);
lua_register(L, "get_rds_ecc", lua_get_rds_ecc);
lua_pushinteger(L, EONs);
lua_setfield(L, -2, "eon_count");
lua_register(L, "set_rds_slc_data", lua_set_rds_slc_data);
lua_register(L, "get_rds_slc_data", lua_get_rds_slc_data);
lua_registertotable(L, "set_pi", lua_set_rds_pi);
lua_registertotable(L, "get_pi", lua_get_rds_pi);
lua_register(L, "set_rds_ct", lua_set_rds_ct);
lua_register(L, "get_rds_ct", lua_get_rds_ct);
lua_registertotable(L, "set_pty", lua_set_rds_pty);
lua_registertotable(L, "get_pty", lua_get_rds_pty);
lua_register(L, "set_rds_dpty", lua_set_rds_dpty);
lua_register(L, "get_rds_dpty", lua_get_rds_dpty);
lua_registertotable(L, "set_ecc", lua_set_rds_ecc);
lua_registertotable(L, "get_ecc", lua_get_rds_ecc);
lua_register(L, "set_rds_tp", lua_set_rds_tp);
lua_register(L, "get_rds_tp", lua_get_rds_tp);
lua_registertotable(L, "set_slc_data", lua_set_rds_slc_data);
lua_registertotable(L, "get_slc_data", lua_get_rds_slc_data);
lua_register(L, "set_rds_ta", lua_set_rds_ta);
lua_register(L, "get_rds_ta", lua_get_rds_ta);
lua_registertotable(L, "set_ct", lua_set_rds_ct);
lua_registertotable(L, "get_ct", lua_get_rds_ct);
lua_register(L, "set_rds_rt1_enabled", lua_set_rds_rt1_enabled);
lua_register(L, "get_rds_rt1_enabled", lua_get_rds_rt1_enabled);
lua_registertotable(L, "set_dpty", lua_set_rds_dpty);
lua_registertotable(L, "get_dpty", lua_get_rds_dpty);
lua_register(L, "set_rds_rt2_enabled", lua_set_rds_rt2_enabled);
lua_register(L, "get_rds_rt2_enabled", lua_get_rds_rt2_enabled);
lua_registertotable(L, "set_tp", lua_set_rds_tp);
lua_registertotable(L, "get_tp", lua_get_rds_tp);
lua_register(L, "set_rds_ptyn_enabled", lua_set_rds_ptyn_enabled);
lua_register(L, "get_rds_ptyn_enabled", lua_get_rds_ptyn_enabled);
lua_registertotable(L, "set_ta", lua_set_rds_ta);
lua_registertotable(L, "get_ta", lua_get_rds_ta);
lua_register(L, "set_rds_rt_type", lua_set_rds_rt_type);
lua_register(L, "get_rds_rt_type", lua_get_rds_rt_type);
lua_registertotable(L, "set_rt1_enabled", lua_set_rds_rt1_enabled);
lua_registertotable(L, "get_rt1_enabled", lua_get_rds_rt1_enabled);
lua_register(L, "set_rds2_mode", lua_set_rds2_mode);
lua_register(L, "get_rds2_mode", lua_get_rds2_mode);
lua_registertotable(L, "set_rt2_enabled", lua_set_rds_rt2_enabled);
lua_registertotable(L, "get_rt2_enabled", lua_get_rds_rt2_enabled);
lua_registertotable(L, "set_ptyn_enabled", lua_set_rds_ptyn_enabled);
lua_registertotable(L, "get_ptyn_enabled", lua_get_rds_ptyn_enabled);
lua_registertotable(L, "set_rt_type", lua_set_rds_rt_type);
lua_registertotable(L, "get_rt_type", lua_get_rds_rt_type);
lua_registertotable(L, "set_rds2_mode", lua_set_rds2_mode);
lua_registertotable(L, "get_rds2_mode", lua_get_rds2_mode);
lua_registertotable(L, "set_link", lua_set_rds_link);
lua_registertotable(L, "get_link", lua_get_rds_link);
lua_registertotable(L, "set_rt_switching_period", lua_set_rds_rt_switching_period);
lua_registertotable(L, "get_rt_switching_period", lua_get_rds_rt_switching_period);
lua_registertotable(L, "set_rt_text_timeout", lua_set_rds_rt_text_timeout);
lua_registertotable(L, "get_rt_text_timeout", lua_get_rds_rt_text_timeout);
lua_registertotable(L, "set_ptyn", lua_set_rds_ptyn);
lua_registertotable(L, "set_ps", lua_set_rds_ps);
lua_registertotable(L, "set_tps", lua_set_rds_tps);
lua_registertotable(L, "set_rt1", lua_set_rds_rt1);
lua_registertotable(L, "set_rt2", lua_set_rds_rt2);
lua_registertotable(L, "set_default_rt", lua_set_rds_default_rt);
lua_registertotable(L, "set_lps", lua_set_rds_lps);
lua_registertotable(L, "get_lps", lua_get_rds_lps);
lua_registertotable(L, "set_grpseq", lua_set_rds_grp_sqc);
lua_registertotable(L, "get_grpseq", lua_get_rds_grp_sqc);
lua_registertotable(L, "set_grpseq2", lua_set_rds_grp_sqc_rds2);
lua_registertotable(L, "get_grpseq2", lua_get_rds_grp_sqc_rds2);
lua_registertotable(L, "put_custom_group", lua_put_rds_custom_group);
lua_registertotable(L, "put_rds2_custom_group", lua_put_rds2_custom_group);
lua_registertotable(L, "set_af", lua_set_rds_af_group0);
lua_registertotable(L, "set_eon", lua_set_rds_eon);
lua_registertotable(L, "get_eon", lua_get_rds_eon);
lua_registertotable(L, "set_udg", lua_set_rds_udg);
lua_registertotable(L, "set_udg2", lua_set_rds_udg2);
lua_setglobal(L, "rds");
lua_register(L, "set_rds_streams", lua_set_rds_streams);
lua_register(L, "get_rds_streams", lua_get_rds_streams);
lua_register(L, "get_available_rds_streams", lua_get_available_rds_streams);
lua_register(L, "set_rds_grpseq", lua_set_rds_grp_sqc);
lua_register(L, "get_rds_grpseq", lua_get_rds_grp_sqc);
lua_register(L, "set_rds_grpseq2", lua_set_rds_grp_sqc_rds2);
lua_register(L, "get_rds_grpseq2", lua_get_rds_grp_sqc_rds2);
lua_register(L, "set_rds_link", lua_set_rds_link);
lua_register(L, "get_rds_link", lua_get_rds_link);
lua_register(L, "set_rds_program", lua_set_rds_program);
lua_register(L, "get_rds_program", lua_get_rds_program);
lua_register(L, "set_rds_rt_switching_period", lua_set_rds_rt_switching_period);
lua_register(L, "get_rds_rt_switching_period", lua_get_rds_rt_switching_period);
lua_register(L, "set_rds_rt_text_timeout", lua_set_rds_rt_text_timeout);
lua_register(L, "get_rds_rt_text_timeout", lua_get_rds_rt_text_timeout);
lua_register(L, "set_rds_level", lua_set_rds_level);
lua_register(L, "get_rds_level", lua_get_rds_level);
lua_register(L, "set_rds_ptyn", lua_set_rds_ptyn);
lua_register(L, "set_rds_ps", lua_set_rds_ps);
lua_register(L, "set_rds_tps", lua_set_rds_tps);
lua_register(L, "set_rds_rt1", lua_set_rds_rt1);
lua_register(L, "set_rds_rt2", lua_set_rds_rt2);
lua_register(L, "set_rds_default_rt", lua_set_rds_default_rt);
lua_register(L, "set_rds_lps", lua_set_rds_lps);
lua_register(L, "get_rds_lps", lua_get_rds_lps);
lua_register(L, "put_rds_custom_group", lua_put_rds_custom_group);
lua_register(L, "put_rds2_custom_group", lua_put_rds2_custom_group);
lua_register(L, "set_rds_af_group0", lua_set_rds_af_group0);
lua_register(L, "set_rds_eon", lua_set_rds_eon);
lua_register(L, "get_rds_eon", lua_get_rds_eon);
lua_register(L, "set_rds_udg", lua_set_rds_udg);
lua_register(L, "set_rds_udg2", lua_set_rds_udg2);
lua_register(L, "set_userdata", lua_set_userdata);
lua_register(L, "set_userdata_offset", lua_set_userdata_offset);
lua_register(L, "get_userdata", lua_get_userdata);
lua_register(L, "get_userdata_offset", lua_get_userdata_offset);
lua_register(L, "crc16", lua_crc16);
if (luaL_loadfile(L, "/etc/rds95.lua") != LUA_OK) {
fprintf(stderr, "Lua error loading file: %s\n", lua_tostring(L, -1));
lua_pop(L, 1);
@@ -562,6 +179,7 @@ void run_lua(char *str, char *cmd_output, size_t* out_len) {
lua_pop(L, 1);
pthread_mutex_unlock(&lua_mutex);
}
int lua_group(RDSGroup* group, const char grp) {
pthread_mutex_lock(&lua_mutex);
lua_getglobal(L, "group");
@@ -703,7 +321,58 @@ void lua_call_function(const char* function) {
pthread_mutex_unlock(&lua_mutex);
}
void destroy_lua(void) {
void lua_call_table_nolock(const char *table_name) {
lua_getglobal(L, table_name);
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return;
}
lua_Integer len = lua_rawlen(L, -1);
for (lua_Integer i = 1; i <= len; i++) {
lua_rawgeti(L, -1, i);
if (lua_isfunction(L, -1)) {
if (lua_pcall(L, 0, 0, 0) != LUA_OK) {
fprintf(stderr,
"Lua error: %s at '%s[%lld]'\n",
lua_tostring(L, -1),
table_name,
(long long)i);
lua_pop(L, 1);
}
} else lua_pop(L, 1);
}
lua_pop(L, 1); // pop table
}
void lua_call_table(const char* function) {
int need_lock = (pthread_mutex_trylock(&lua_mutex) == 0);
if (!need_lock) {
fprintf(stderr, "Warning: lua_mutex already locked when table calling %s\n", function);
return;
}
lua_call_table_nolock(function);
pthread_mutex_unlock(&lua_mutex);
}
void lua_call_tfunction_nolock(const char* name) {
char table_name[256];
lua_call_function_nolock(name);
snprintf(table_name, sizeof(table_name), "%ss", name);
lua_call_table_nolock(table_name);
}
void lua_call_tfunction(const char* name) {
int need_lock = (pthread_mutex_trylock(&lua_mutex) == 0);
if (!need_lock) {
fprintf(stderr, "Warning: lua_mutex already locked when table tcalling %s\n", name);
return;
}
lua_call_tfunction_nolock(name);
pthread_mutex_unlock(&lua_mutex);
}
void destroy_lua() {
if (L) {
for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]);
*unload_refs = 1;

View File

@@ -1,10 +1,10 @@
#pragma once
#include "common.h"
#include "rds.h"
#include "modulator.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "rds.h"
#include "fs.h"
#include "modulator.h"
void init_lua(RDSModulator* rds_mod);
void run_lua(char *str, char *cmd_output, size_t* out_len);
@@ -12,5 +12,9 @@ int lua_group(RDSGroup* group, const char grp);
int lua_rds2_group(RDSGroup* group, int stream);
void lua_call_function_nolock(const char* function);
void lua_call_function(const char* function);
void lua_call_table_nolock(const char *table_name);
void lua_call_table(const char* function);
void lua_call_tfunction_nolock(const char* name);
void lua_call_tfunction(const char* name);
void lua_group_ref(RDSGroup* group, int ref);
void destroy_lua();

View File

@@ -155,7 +155,7 @@ void get_rds_group(RDSEncoder* enc, RDSGroup *group, uint8_t stream) {
}
}
lua_call_function("tick");
lua_call_tfunction("tick");
}
if (utc->tm_min != enc->state[enc->program].last_minute) {
@@ -378,10 +378,10 @@ void init_rds_encoder(RDSEncoder* enc) {
if (encoder_loadFromFile(enc)) {
printf("Encoder file will be reinitialized.\n");
lua_call_function("on_init");
lua_call_tfunction("on_init");
}
for(int i = 0; i < PROGRAMS; i++) reset_rds_state(enc, i);
lua_call_function("on_start");
lua_call_function("on_state");
lua_call_tfunction("on_start");
lua_call_tfunction("on_state");
encoder_saveToFile(enc);
}

View File

@@ -101,12 +101,12 @@ int main(int argc, char **argv) {
const char *short_opt = "c:ah";
struct option long_opt[] =
struct option long_opt[] =
{
{"config", required_argument, NULL, 'c'},
{"asciig", no_argument, NULL, 'a'},
{"help", no_argument, NULL, 'h'},
{ 0, 0, 0, 0 }
{"config", required_argument, NULL, 'c'},
{"asciig", no_argument, NULL, 'a'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0}
};
int opt;
@@ -144,7 +144,6 @@ int main(int argc, char **argv) {
pthread_attr_init(&attr);
struct sigaction sa_stop;
sa_stop.sa_handler = stop;
sigemptyset(&sa_stop.sa_mask);
sa_stop.sa_flags = 0;
@@ -160,17 +159,7 @@ int main(int argc, char **argv) {
buffer.tlength = buffer.maxlength = NUM_MPX_FRAMES * config.num_streams;
if(config.asciig == 0) {
rds_device = pa_simple_new(
NULL,
"rds95",
PA_STREAM_PLAYBACK,
config.rds_device_name,
"RDS Generator",
&format,
NULL,
&buffer,
NULL
);
rds_device = pa_simple_new(NULL, "rds95", PA_STREAM_PLAYBACK, config.rds_device_name, "RDS Generator", &format, NULL, &buffer, NULL);
if (rds_device == NULL) {
fprintf(stderr, "Error: cannot open sound device.\n");
goto exit;