LinJamConfig.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. /*
  2. ==============================================================================
  3. LinJamConfig.cpp
  4. Created: 12 Jun 2014 7:05:12am
  5. Author: me
  6. ==============================================================================
  7. */
  8. #include "LinJam.h"
  9. #include "./Trace/TraceLinJamConfig.h"
  10. /* LinJamConfig class public class methods */
  11. LinJamConfig::LinJamConfig() { initialize() ; }
  12. LinJamConfig::~LinJamConfig() { storeConfig() ; }
  13. Identifier LinJamConfig::MakeHostId(String host)
  14. {
  15. return Identifier(CONFIG::SERVER_KEY + "-" + host.replaceCharacters(".:" , "--")) ;
  16. }
  17. Identifier LinJamConfig::MakeUserId(String user_name)
  18. {
  19. return user_name.upToFirstOccurrenceOf(CONFIG::USER_IP_SPLIT_CHAR , false , true)
  20. .retainCharacters(CONFIG::VALID_NAME_CHARS)
  21. .replaceCharacter(' ', '-') ;
  22. }
  23. Identifier LinJamConfig::MakeChannelId(int channel_idx)
  24. {
  25. return (channel_idx == CONFIG::MASTER_CHANNEL_IDX) ?
  26. CONFIG::MASTER_ID :
  27. Identifier(CONFIG::CHANNEL_BASE_ID + "-" + String(channel_idx)) ;
  28. }
  29. String LinJamConfig::MakeStereoName(String channel_name , int stereo_status)
  30. {
  31. return TrimStereoName(channel_name) +
  32. ((stereo_status == CONFIG::STEREO_L) ? CLIENT::STEREO_L_POSTFIX :
  33. (stereo_status == CONFIG::STEREO_R) ? CLIENT::STEREO_R_POSTFIX : "") ;
  34. }
  35. String LinJamConfig::TrimStereoName(String channel_name)
  36. {
  37. String stereo_postfix = channel_name.getLastCharacters(CLIENT::STEREO_POSTFIX_N_CHARS) ;
  38. return (stereo_postfix != CLIENT::STEREO_L_POSTFIX &&
  39. stereo_postfix != CLIENT::STEREO_R_POSTFIX ) ? channel_name :
  40. channel_name.dropLastCharacters(CLIENT::STEREO_POSTFIX_N_CHARS) ;
  41. }
  42. int LinJamConfig::ParseStereoStatus(String channel_name)
  43. {
  44. // determine faux-stereo stereo status based on channel name
  45. String postfix = channel_name.getLastCharacters(CLIENT::STEREO_POSTFIX_N_CHARS) ;
  46. return (!postfix.compare(CLIENT::STEREO_L_POSTFIX))? CONFIG::STEREO_L :
  47. (!postfix.compare(CLIENT::STEREO_R_POSTFIX))? CONFIG::STEREO_R :
  48. CONFIG::MONO ;
  49. }
  50. ValueTree LinJamConfig::NewChannel(String channel_name , int channel_idx)
  51. {
  52. return ValueTree(CONFIG::NEWCHANNEL_ID)
  53. .setProperty(CONFIG::CHANNEL_NAME_ID , channel_name , nullptr)
  54. .setProperty(CONFIG::CHANNEL_IDX_ID , channel_idx , nullptr)
  55. .setProperty(CONFIG::PAIR_IDX_ID , CONFIG::DEFAULT_CHANNEL_IDX , nullptr)
  56. .setProperty(CONFIG::VOLUME_ID , CONFIG::DEFAULT_VOLUME , nullptr)
  57. .setProperty(CONFIG::PAN_ID , CONFIG::DEFAULT_PAN , nullptr)
  58. .setProperty(CONFIG::IS_XMIT_RCV_ID , CONFIG::DEFAULT_IS_XMIT_RCV , nullptr)
  59. .setProperty(CONFIG::IS_MUTED_ID , CONFIG::DEFAULT_IS_MUTED , nullptr)
  60. .setProperty(CONFIG::IS_SOLO_ID , CONFIG::DEFAULT_IS_SOLO , nullptr)
  61. .setProperty(CONFIG::SOURCE_N_ID , CONFIG::DEFAULT_SOURCE_N , nullptr)
  62. .setProperty(CONFIG::VU_LEFT_ID , CONFIG::DEFAULT_VU , nullptr)
  63. .setProperty(CONFIG::VU_RIGHT_ID , CONFIG::DEFAULT_VU , nullptr)
  64. .setProperty(CONFIG::STEREO_ID , CONFIG::DEFAULT_STEREO_STATUS , nullptr) ;
  65. }
  66. Value LinJamConfig::GetValueHolder(ValueTree config_store , Identifier a_key)
  67. {
  68. return config_store.getPropertyAsValue(a_key , nullptr) ;
  69. }
  70. /* LinJamConfig class private instance methods */
  71. /* init */
  72. void LinJamConfig::initialize()
  73. {
  74. // load default and stored configs
  75. this->dataDir = File::getSpecialLocation(File::userApplicationDataDirectory) ;
  76. if (!this->dataDir.isDirectory()) return ;
  77. this-> configXmlFile = this->dataDir.getChildFile(CLIENT::STORAGE_FILENAME) ;
  78. UPTR<XmlElement> default_xml = XmlDocument::parse(CONFIG::DEFAULT_CONFIG_XML) ;
  79. UPTR<XmlElement> stored_xml = XmlDocument::parse(this->configXmlFile) ;
  80. bool has_stored_config = stored_xml != nullptr &&
  81. stored_xml->hasTagName(CONFIG::STORAGE_ID) ;
  82. DEBUG_TRACE_LOAD_CONFIG
  83. if (default_xml == nullptr) { stored_xml.reset() ; return ; } // panic
  84. // validate config version
  85. double stored_version = (!has_stored_config) ? 0.0 :
  86. stored_xml->getDoubleAttribute(CONFIG::CONFIG_VERSION_ID) ;
  87. bool do_versions_match = stored_version == CONFIG::CONFIG_VERSION ;
  88. if (!do_versions_match) {;} // TODO: convert (if ever necessary)
  89. DEBUG_TRACE_DUMP_CONFIG
  90. // create shared config ValueTree from stored xml persistence or default
  91. if (has_stored_config && do_versions_match)
  92. this->configRoot = sanitizeConfig(ValueTree::fromXml(*default_xml) ,
  93. ValueTree::fromXml(*stored_xml)) ;
  94. else this->configRoot = ValueTree::fromXml(*default_xml) ;
  95. // instantiate shared value holders and restore type data
  96. establishSharedStore() ; restoreVarTypeInfo(this->configRoot) ;
  97. // prune any corrupted user-defined data
  98. validateServers() ; validateUsers() ; validateChannels(this->localChannels) ;
  99. // repair any rouge values
  100. sanitizeGui() ;
  101. // write back sanitized config to disk and cleanup
  102. storeConfig() ; default_xml.reset() ; stored_xml.reset() ;
  103. // register listener on LinJamStatus for Gui state
  104. LinJam::Status.addListener(this) ;
  105. // register listeners on interesting config nodes for central dispatcher
  106. this->gui .addListener(this) ;
  107. this->blacklist .addListener(this) ;
  108. this->audio .addListener(this) ;
  109. this->masterChannels.addListener(this) ;
  110. this->localChannels .addListener(this) ;
  111. this->remoteUsers .addListener(this) ;
  112. }
  113. void LinJamConfig::establishSharedStore()
  114. {
  115. // client config
  116. this->gui = this->configRoot.getChildWithName(CONFIG::GUI_ID ) ;
  117. // client config
  118. this->client = this->configRoot.getChildWithName(CONFIG::CLIENT_ID ) ;
  119. // ignore list
  120. this->blacklist = this->configRoot.getChildWithName(CONFIG::BLACKLIST_ID) ;
  121. // device config
  122. this->audio = this->configRoot.getChildWithName(CONFIG::AUDIO_ID ) ;
  123. // login state
  124. this->server = this->configRoot.getChildWithName(CONFIG::SERVER_ID ) ;
  125. // per server credentials
  126. this->servers = this->configRoot.getChildWithName(CONFIG::SERVERS_ID ) ;
  127. // channels
  128. this->masterChannels = this->configRoot.getChildWithName(CONFIG::MASTERS_ID ) ;
  129. this->localChannels = this->configRoot.getChildWithName(CONFIG::LOCALS_ID ) ;
  130. this->remoteUsers = this->configRoot.getChildWithName(CONFIG::REMOTES_ID ) ;
  131. }
  132. void LinJamConfig::restoreVarTypeInfo(ValueTree config_store)
  133. {
  134. Identifier node_id = config_store.getType() ; // for leaves at toplevel
  135. ValueTree parent_node = config_store.getParent() ; // for leaves in list
  136. ValueTree grandparent_node = parent_node .getParent() ; // for leaves in nested list
  137. UPTR<XmlElement> config_types_xml = XmlDocument::parse(CONFIG::CONFIG_DATATYPES_XML) ;
  138. ValueTree config_types = ValueTree::fromXml(*config_types_xml) ;
  139. ValueTree root_types = config_types ;
  140. ValueTree toplevel_types = config_types.getChildWithName(node_id ) ;
  141. ValueTree server_types = config_types.getChildWithName(CONFIG::SERVER_ID ) ;
  142. ValueTree user_types = config_types.getChildWithName(CONFIG::USERS_ID ) ;
  143. ValueTree channel_types = config_types.getChildWithName(CONFIG::CHANNELS_ID) ;
  144. ValueTree types_store ; config_types_xml.reset() ;
  145. // load property datatypes info for this node
  146. types_store = (config_store == this->configRoot ) ? root_types :
  147. (config_store == this->gui ||
  148. config_store == this->client ||
  149. config_store == this->blacklist ||
  150. config_store == this->audio ||
  151. config_store == this->server ) ? toplevel_types :
  152. (parent_node == this->servers ) ? server_types :
  153. (parent_node == this->remoteUsers ) ? user_types :
  154. (parent_node == this->masterChannels ||
  155. parent_node == this->localChannels ||
  156. grandparent_node == this->remoteUsers ) ? channel_types :
  157. ValueTree() ;
  158. DEBUG_TRACE_CONFIG_TYPES_VB
  159. // restore datatypes info for each property
  160. for (int property_n = 0 ; property_n < config_store.getNumProperties() ; ++property_n)
  161. {
  162. Identifier key = config_store.getPropertyName(property_n) ;
  163. var a_var = config_store[key] ;
  164. String datatype = types_store [key] ;
  165. bool is_bool = !datatype.compare(CONFIG::BOOL_TYPE ) ;
  166. bool is_double = !datatype.compare(CONFIG::DOUBLE_TYPE) ;
  167. bool is_int = !datatype.compare(CONFIG::INT_TYPE ) ;
  168. bool is_string = !datatype.compare(CONFIG::STRING_TYPE) ;
  169. if (is_bool) config_store.setProperty( key , bool( a_var) , nullptr) ;
  170. else if (is_double) config_store.setProperty( key , double(a_var) , nullptr) ;
  171. else if (is_int) config_store.setProperty( key , int( a_var) , nullptr) ;
  172. else if (!is_string) config_store.removeProperty(key , nullptr) ;
  173. DEBUG_TRACE_CONFIG_TYPES_VB_EACH
  174. }
  175. // recurse on child nodes
  176. for (int child_n = 0 ; child_n < config_store.getNumChildren() ; ++child_n)
  177. restoreVarTypeInfo(config_store.getChild(child_n)) ;
  178. }
  179. void LinJamConfig::storeConfig()
  180. {
  181. DEBUG_TRACE_STORE_CONFIG
  182. ValueTree root_clone = this->configRoot.createCopy() ;
  183. ValueTree servers_store = root_clone.getChildWithName(CONFIG::SERVERS_ID) ;
  184. for (int server_n = 0 ; server_n < servers_store.getNumChildren() ; ++server_n)
  185. {
  186. ValueTree server_store = servers_store.getChild(server_n) ;
  187. ValueTree clients_store = server_store.getChildWithName(CONFIG::CLIENTS_ID) ;
  188. server_store.removeChild(clients_store , nullptr) ;
  189. }
  190. UPTR<XmlElement> config_xml = root_clone.createXml() ;
  191. config_xml->writeToFile(this->configXmlFile , StringRef() , StringRef("UTF-8") , 0) ;
  192. config_xml.reset() ;
  193. }
  194. /* validation */
  195. ValueTree LinJamConfig::sanitizeConfig(ValueTree default_config , ValueTree stored_config)
  196. {
  197. Identifier default_node_name = default_config.getType() ;
  198. int n_properties = default_config.getNumProperties() ;
  199. int n_default_children = default_config.getNumChildren() ;
  200. int n_stored_children = stored_config .getNumChildren() ;
  201. // transfer any missing attributes
  202. for (int property_n = 0 ; property_n < n_properties ; ++property_n)
  203. {
  204. Identifier key = default_config.getPropertyName(property_n) ;
  205. var value = default_config.getProperty(key) ;
  206. if (!stored_config.hasProperty(key)) stored_config.setProperty(key , value , nullptr) ;
  207. }
  208. // transfer any missing nodes
  209. for (int child_n = 0 ; child_n < n_default_children ; ++child_n)
  210. {
  211. ValueTree default_child = default_config.getChild(child_n) ;
  212. Identifier default_child_name = default_child .getType() ;
  213. ValueTree stored_child = stored_config .getChildWithName(default_child_name) ;
  214. // transfer missing node
  215. if (!stored_child.isValid())
  216. {
  217. // for local channels we transfer the default channel only if none are stored
  218. if (default_node_name != CONFIG::LOCALS_ID || !n_stored_children)
  219. {
  220. default_config.removeChild(default_child , nullptr) ;
  221. stored_config .addChild( default_child , -1 , nullptr) ;
  222. --child_n ;
  223. }
  224. continue ;
  225. }
  226. // recurse on child node
  227. sanitizeConfig(default_child , stored_child) ;
  228. }
  229. return stored_config ;
  230. }
  231. void LinJamConfig::validateServers()
  232. {
  233. ValueTree volatile_server = this->server.createCopy() ;
  234. for (int server_n = 0 ; server_n < NETWORK::KNOWN_HOSTS.getNumChildren() ; ++server_n)
  235. {
  236. String known_host = STRING(NETWORK::KNOWN_HOSTS.getChild(server_n).getType()) ;
  237. ValueTree server = getServer(known_host) ;
  238. if (!server.isValid())
  239. {
  240. // create new per-server storage
  241. this->server.setProperty(CONFIG::HOST_ID , known_host , nullptr)
  242. .setProperty(CONFIG::LOGIN_ID , CONFIG::DEFAULT_LOGIN , nullptr)
  243. .setProperty(CONFIG::PASS_ID , CONFIG::DEFAULT_PASS , nullptr)
  244. .setProperty(CONFIG::IS_ANONYMOUS_ID , CONFIG::DEFAULT_IS_ANONYMOUS , nullptr)
  245. .setProperty(CONFIG::SHOULD_AGREE_ID , CONFIG::DEFAULT_SHOULD_AGREE , nullptr) ;
  246. storeServer() ;
  247. }
  248. // append empty clients list
  249. else server.getOrCreateChildWithName(CONFIG::CLIENTS_ID , nullptr) ;
  250. }
  251. }
  252. void LinJamConfig::validateUsers()
  253. {
  254. for (int user_n = 0 ; user_n < this->remoteUsers.getNumChildren() ; ++user_n)
  255. {
  256. ValueTree user_store = this->remoteUsers.getChild(user_n) ;
  257. bool user_has_useridx_property = user_store.hasProperty(CONFIG::USER_IDX_ID) ;
  258. if (!user_has_useridx_property) this->remoteUsers.removeChild(user_store , nullptr) ;
  259. else
  260. {
  261. // ensure that NJClient will be configured for ignored users upon join
  262. ValueTree master_store = getChannelByIdx(user_store , CONFIG::MASTER_CHANNEL_IDX) ;
  263. master_store.setProperty(CONFIG::IS_XMIT_RCV_ID , true , nullptr) ;
  264. validateChannels(user_store) ;
  265. }
  266. DEBUG_TRACE_VALIDATE_USER
  267. }
  268. }
  269. void LinJamConfig::validateChannels(ValueTree channels)
  270. {
  271. for (int channel_n = 0 ; channel_n < channels.getNumChildren() ; ++channel_n)
  272. {
  273. ValueTree channel = channels.getChild(channel_n) ;
  274. bool channel_has_channelname_property = channel.hasProperty(CONFIG::CHANNEL_NAME_ID) ;
  275. bool channel_has_channelidx_property = channel.hasProperty(CONFIG::CHANNEL_IDX_ID) ;
  276. bool channel_has_pairidx_property = channel.hasProperty(CONFIG::PAIR_IDX_ID) ;
  277. bool channel_has_volume_property = channel.hasProperty(CONFIG::VOLUME_ID) ;
  278. bool channel_has_pan_property = channel.hasProperty(CONFIG::PAN_ID) ;
  279. bool channel_has_xmit_property = channel.hasProperty(CONFIG::IS_XMIT_RCV_ID) ;
  280. bool channel_has_mute_property = channel.hasProperty(CONFIG::IS_MUTED_ID) ;
  281. bool channel_has_solo_property = channel.hasProperty(CONFIG::IS_SOLO_ID) ;
  282. bool channel_has_source_property = channel.hasProperty(CONFIG::SOURCE_N_ID) ;
  283. bool channel_has_stereo_property = channel.hasProperty(CONFIG::STEREO_ID) ;
  284. bool channel_has_vuleft_property = channel.hasProperty(CONFIG::VU_LEFT_ID) ;
  285. bool channel_has_vuright_property = channel.hasProperty(CONFIG::VU_RIGHT_ID) ;
  286. if (!channel_has_channelname_property || !channel_has_channelidx_property ||
  287. !channel_has_pairidx_property || !channel_has_volume_property ||
  288. !channel_has_pan_property || !channel_has_xmit_property ||
  289. !channel_has_mute_property || !channel_has_solo_property ||
  290. !channel_has_source_property || !channel_has_stereo_property ||
  291. !channel_has_vuleft_property || !channel_has_vuright_property )
  292. { channels.removeChild(channel , nullptr) ; --channel_n ; }
  293. DEBUG_TRACE_VALIDATE_CHANNEL
  294. }
  295. }
  296. void LinJamConfig::sanitizeGui()
  297. {
  298. int font_size_n = int(this->gui[CONFIG::FONT_SIZE_ID ]) ;
  299. int update_ivl_n = int(this->gui[CONFIG::UPDATE_IVL_ID]) ;
  300. bool is_invalid_font_size = font_size_n < 0 || font_size_n >= GUI::FONT_SIZES .size() ;
  301. bool is_invalid_update_ivl = update_ivl_n < 0 || update_ivl_n >= GUI::UPDATE_IVLS.size() ;
  302. DEBUG_TRACE_SANITIZE_GUI
  303. if (is_invalid_font_size)
  304. this->gui.setProperty(CONFIG::FONT_SIZE_ID , CONFIG::DEFAULT_FONT_SIZE_N , nullptr) ;
  305. if (is_invalid_update_ivl)
  306. this->gui.setProperty(CONFIG::UPDATE_IVL_ID , CONFIG::DEFAULT_UPDATE_IVL_N , nullptr) ;
  307. }
  308. bool LinJamConfig::validateConfig()
  309. {
  310. // validate subscribed trees
  311. bool root_is_valid = this->configRoot .isValid() ;
  312. bool gui_is_valid = this->gui .isValid() ;
  313. bool client_is_valid = this->client .isValid() ;
  314. bool blacklist_is_valid = this->blacklist .isValid() ;
  315. bool audio_is_valid = this->audio .isValid() ;
  316. bool server_is_valid = this->server .isValid() ;
  317. bool servers_is_valid = this->servers .isValid() ;
  318. bool master_channels_is_valid = this->masterChannels .isValid() ;
  319. bool local_channels_is_valid = this->localChannels .isValid() ;
  320. bool remote_users_is_valid = this->remoteUsers .isValid() ;
  321. bool is_valid = (root_is_valid && gui_is_valid &&
  322. client_is_valid && blacklist_is_valid &&
  323. audio_is_valid && server_is_valid &&
  324. servers_is_valid && master_channels_is_valid &&
  325. local_channels_is_valid && remote_users_is_valid ) ;
  326. DEBUG_TRACE_VALIDATE_CONFIG // modifies 'bool is_valid'
  327. return is_valid ;
  328. }
  329. bool LinJamConfig::isConfigValid()
  330. {
  331. bool is_config_valid = validateConfig() ;
  332. if (!is_config_valid)
  333. {
  334. this->configRoot = ValueTree() ;
  335. this->gui = ValueTree() ; this->gui .removeListener(this) ;
  336. this->client = ValueTree() ;
  337. this->blacklist = ValueTree() ; this->blacklist .removeListener(this) ;
  338. this->audio = ValueTree() ; this->audio .removeListener(this) ;
  339. this->server = ValueTree() ;
  340. this->servers = ValueTree() ;
  341. this->masterChannels = ValueTree() ; this->masterChannels.removeListener(this) ;
  342. this->localChannels = ValueTree() ; this->localChannels .removeListener(this) ;
  343. this->remoteUsers = ValueTree() ; this->remoteUsers .removeListener(this) ;
  344. DEBUG_TRACE_CLOBBER_CONFIG
  345. if (this->configXmlFile.existsAsFile())
  346. {
  347. this->configXmlFile.deleteFile() ; initialize() ;
  348. }
  349. is_config_valid = validateConfig() ;
  350. }
  351. return is_config_valid ;
  352. }
  353. /* getters/setters */
  354. ValueTree LinJamConfig::addChannel(ValueTree channels_store ,
  355. ValueTree new_channel_node)
  356. {
  357. DEBUG_TRACE_ADD_CHANNEL_STORE
  358. // ensure trees are valid and storage does not already exist for this channel
  359. if (!channels_store.isValid() || !new_channel_node.isValid()) return ValueTree() ;
  360. if (new_channel_node.getParent() == channels_store) return new_channel_node ;
  361. int channel_idx = int(new_channel_node[CONFIG::CHANNEL_IDX_ID]) ;
  362. ValueTree channel_store = ValueTree(MakeChannelId(channel_idx)) ;
  363. channel_store .copyPropertiesFrom(new_channel_node , nullptr) ;
  364. channels_store.addChild(channel_store , -1 , nullptr) ;
  365. return channel_store ;
  366. }
  367. void LinJamConfig::removeChannel(ValueTree channels_store , ValueTree channel_store)
  368. {
  369. DEBUG_TRACE_REMOVE_CHANNEL_STORE
  370. channels_store.removeChild(channel_store , nullptr) ;
  371. }
  372. ValueTree LinJamConfig::getOrAddRemoteUser(String user_name)
  373. {
  374. Identifier user_id = MakeUserId(user_name) ;
  375. ValueTree user_store = getUserById(user_id) ;
  376. if (!user_store.isValid())
  377. {
  378. user_store = ValueTree(user_id) ;
  379. this->remoteUsers.addChild(user_store , -1 , nullptr) ;
  380. DEBUG_TRACE_ADD_REMOTE_USER_STORE
  381. }
  382. return user_store ;
  383. }
  384. ValueTree LinJamConfig::getOrAddRemoteChannel(Identifier user_id ,
  385. String channel_name ,
  386. int channel_idx )
  387. {
  388. ValueTree user_store = getUserById(user_id) ;
  389. ValueTree channel_store = getChannelByIdx(user_store , channel_idx) ;
  390. if (user_store.isValid() && !channel_store.isValid())
  391. {
  392. // add new channel to store (masters always faux-stereo)
  393. channel_store = NewChannel(channel_name , channel_idx) ;
  394. if (channel_idx == CONFIG::DEFAULT_CHANNEL_IDX)
  395. setStereo(channel_store , CONFIG::STEREO_L) ;
  396. channel_store = addChannel(user_store , channel_store) ;
  397. }
  398. return channel_store ;
  399. }
  400. ValueTree LinJamConfig::getUserById(Identifier user_id)
  401. {
  402. return this->remoteUsers.getChildWithName(user_id) ;
  403. }
  404. ValueTree LinJamConfig::getChannelById(Identifier channels_id , Identifier channel_id)
  405. {
  406. ValueTree channels_store ;
  407. if (channels_id == CONFIG::MASTERS_ID) channels_store = this->masterChannels ;
  408. else if (channels_id == CONFIG::LOCALS_ID) channels_store = this->localChannels ;
  409. else channels_store = getUserById(channels_id) ;
  410. return channels_store.getChildWithName(channel_id) ;
  411. }
  412. ValueTree LinJamConfig::getChannelByIdx(ValueTree channels_store , int channel_idx)
  413. {
  414. return channels_store.getChildWithProperty(CONFIG::CHANNEL_IDX_ID , channel_idx) ;
  415. }
  416. ValueTree LinJamConfig::getChannelByPairIdx(ValueTree channels_store , int pair_idx)
  417. {
  418. return channels_store.getChildWithProperty(CONFIG::PAIR_IDX_ID , pair_idx) ;
  419. }
  420. ValueTree LinJamConfig::getChannelByName(ValueTree channels_store , String channel_name)
  421. {
  422. return channels_store.getChildWithProperty(CONFIG::CHANNEL_NAME_ID , channel_name) ;
  423. }
  424. ValueTree LinJamConfig::getUserMasterChannel(ValueTree user_store)
  425. {
  426. return getChannelByIdx(user_store , CONFIG::MASTER_CHANNEL_IDX) ;
  427. }
  428. void LinJamConfig::updateRemoteUserState(ValueTree user_store , int user_idx ,
  429. bool should_rcv )
  430. {
  431. Identifier user_id = user_store.getType() ;
  432. ValueTree master_store = this->getOrAddRemoteChannel(user_id , CONFIG::MASTER_KEY) ;
  433. user_store .setProperty(CONFIG::USER_IDX_ID , user_idx , nullptr) ;
  434. master_store.setProperty(CONFIG::IS_XMIT_RCV_ID , should_rcv , nullptr) ;
  435. }
  436. void LinJamConfig::setCredentials(String host , String login ,
  437. String pass , bool is_anonymous)
  438. {
  439. if (is_anonymous) pass = "" ;
  440. bool is_agreed = bool(getServer(host)[CONFIG::SHOULD_AGREE_ID]) ;
  441. String bot_name = str(NETWORK::KNOWN_BOTS.getProperty(MakeHostId(host) , "")) ;
  442. int bot_idx = CONFIG::DEFAULT_BOT_USERIDX ;
  443. this->server.setProperty(CONFIG::HOST_ID , host , nullptr)
  444. .setProperty(CONFIG::LOGIN_ID , login , nullptr)
  445. .setProperty(CONFIG::PASS_ID , pass , nullptr)
  446. .setProperty(CONFIG::IS_ANONYMOUS_ID , is_anonymous , nullptr)
  447. .setProperty(CONFIG::SHOULD_AGREE_ID , is_agreed , nullptr)
  448. .setProperty(CONFIG::IS_AGREED_ID , is_agreed , nullptr)
  449. .setProperty(CONFIG::BOT_NAME_ID , bot_name , nullptr)
  450. .setProperty(CONFIG::BOT_USERIDX_ID , bot_idx , nullptr) ;
  451. }
  452. ValueTree LinJamConfig::getCredentials(String host)
  453. {
  454. // return copy of stored credentials
  455. ValueTree stored_server = getServer(host) ;
  456. ValueTree server_copy = ValueTree(MakeHostId(host)) ;
  457. if (stored_server.isValid()) server_copy.copyPropertiesFrom(stored_server , nullptr) ;
  458. else server_copy = ValueTree() ;
  459. return server_copy ;
  460. }
  461. void LinJamConfig::storeServer()
  462. {
  463. // copy volatile login state to persistent storage
  464. String host = str( this->server[CONFIG::HOST_ID ]) ;
  465. String login = str( this->server[CONFIG::LOGIN_ID ]) ;
  466. String pass = str( this->server[CONFIG::PASS_ID ]) ;
  467. bool is_anonymous = bool(this->server[CONFIG::IS_ANONYMOUS_ID]) ;
  468. bool should_agree = bool(this->server[CONFIG::SHOULD_AGREE_ID]) ;
  469. ValueTree server = getServer(host) ;
  470. // create new server entry
  471. if (!server.isValid())
  472. {
  473. DEBUG_TRACE_STORE_SERVER
  474. server = ValueTree(MakeHostId(host)) ;
  475. this->servers.addChild(server , -1 , nullptr) ;
  476. }
  477. // create clients list
  478. server.getOrCreateChildWithName(CONFIG::CLIENTS_ID , nullptr) ;
  479. // set per server credentials
  480. server.setProperty(CONFIG::HOST_ID , host , nullptr)
  481. .setProperty(CONFIG::LOGIN_ID , login , nullptr)
  482. .setProperty(CONFIG::PASS_ID , pass , nullptr)
  483. .setProperty(CONFIG::IS_ANONYMOUS_ID , is_anonymous , nullptr)
  484. .setProperty(CONFIG::SHOULD_AGREE_ID , should_agree , nullptr) ;
  485. }
  486. ValueTree LinJamConfig::getServer(String host)
  487. {
  488. return this->servers.getChildWithProperty(CONFIG::HOST_ID , var(host)) ;
  489. }
  490. void LinJamConfig::setStereo(ValueTree channel_store , int stereo_status)
  491. {
  492. channel_store.setProperty(CONFIG::STEREO_ID , stereo_status , nullptr) ;
  493. }
  494. int LinJamConfig::setRemoteStereo(ValueTree user_store , ValueTree channel_store ,
  495. String prev_channel_name )
  496. {
  497. String channel_name = str(channel_store[CONFIG::CHANNEL_NAME_ID]) ;
  498. int stereo_status = ParseStereoStatus(channel_name) ;
  499. int prev_status = ParseStereoStatus(prev_channel_name) ;
  500. // ensure remote faux-stereo channels are paired
  501. if (stereo_status != CONFIG::MONO)
  502. {
  503. // find channel with name matching this channel_name + opposite_postfix
  504. // to ignore duplicate names this assumes that stereo pairs are contiguous
  505. int channel_idx = int(channel_store[CONFIG::CHANNEL_IDX_ID]) ;
  506. String l_pair_name = MakeStereoName(channel_name , CONFIG::STEREO_L) ;
  507. String r_pair_name = MakeStereoName(channel_name , CONFIG::STEREO_R) ;
  508. String expected_pair_name ; int pair_stereo_status ;
  509. int l_pair_idx ; int r_pair_idx ;
  510. if (stereo_status == CONFIG::STEREO_L)
  511. {
  512. l_pair_idx = channel_idx ;
  513. r_pair_idx = channel_idx + 1 ;
  514. expected_pair_name = r_pair_name ;
  515. pair_stereo_status = CONFIG::STEREO_R ;
  516. }
  517. else if (stereo_status == CONFIG::STEREO_R)
  518. {
  519. l_pair_idx = channel_idx - 1 ;
  520. r_pair_idx = channel_idx ;
  521. expected_pair_name = l_pair_name ;
  522. pair_stereo_status = CONFIG::STEREO_L ;
  523. }
  524. ValueTree l_pair_store = getChannelByIdx(user_store , l_pair_idx) ;
  525. ValueTree r_pair_store = getChannelByIdx(user_store , r_pair_idx) ;
  526. ValueTree pair_store = (pair_stereo_status == CONFIG::STEREO_L) ? l_pair_store :
  527. r_pair_store ;
  528. String pair_name = str(pair_store[CONFIG::CHANNEL_NAME_ID]) ;
  529. bool is_paired = !pair_name.compare(expected_pair_name) ;
  530. // set this and matched pair channel stereo status to stereo
  531. if (is_paired)
  532. {
  533. l_pair_store.setProperty(CONFIG::PAIR_IDX_ID , r_pair_idx , nullptr) ;
  534. setStereo(channel_store , stereo_status) ;
  535. setStereo(pair_store , pair_stereo_status) ;
  536. }
  537. // set this unpaired channel stereo status to mono
  538. else setStereo(channel_store , (stereo_status = CONFIG::MONO)) ;
  539. DEBUG_TRACE_STEREO_STATUS
  540. }
  541. if (stereo_status == CONFIG::MONO && stereo_status != prev_status)
  542. {
  543. // find channel with name matching prev_channel_name + either_postfix
  544. String l_pair_name = MakeStereoName(prev_channel_name , CONFIG::STEREO_L) ;
  545. String r_pair_name = MakeStereoName(prev_channel_name , CONFIG::STEREO_R) ;
  546. ValueTree l_pair_channel_store = getChannelByName(user_store , l_pair_name) ;
  547. ValueTree r_pair_channel_store = getChannelByName(user_store , r_pair_name) ;
  548. bool has_l_pair = l_pair_channel_store.isValid() &&
  549. l_pair_name.compare(channel_name) ;
  550. bool has_r_pair = r_pair_channel_store.isValid() &&
  551. r_pair_name.compare(channel_name) ;
  552. bool has_orphaned_pair = has_l_pair != has_r_pair ;
  553. DEBUG_TRACE_MONO_STATUS
  554. // set orphaned pair channel stereo status to mono
  555. if (has_orphaned_pair)
  556. {
  557. if (has_l_pair) setStereo(l_pair_channel_store , CONFIG::MONO) ;
  558. else if (has_r_pair) setStereo(r_pair_channel_store , CONFIG::MONO) ;
  559. }
  560. // set this channel stereo status to mono
  561. setStereo(channel_store , CONFIG::MONO) ;
  562. }
  563. return stereo_status ;
  564. }
  565. /* event handlers */
  566. void LinJamConfig::valueChanged(Value& a_value)
  567. {
  568. DEBUG_TRACE_CONFIG_VALUE_CHANGED
  569. // update state
  570. if (a_value.refersToSameSourceAs(LinJam::Status)) LinJam::HandleStatusChanged() ;
  571. }
  572. void LinJamConfig::valueTreePropertyChanged(ValueTree& a_node , const Identifier& a_key)
  573. {
  574. Identifier node_id = a_node .getType() ;
  575. ValueTree parent_node = a_node .getParent() ;
  576. Identifier parent_id = parent_node.getType() ;
  577. ValueTree grandparent_node = parent_node.getParent() ;
  578. bool is_gui = a_node == this->gui ;
  579. bool is_blacklist = a_node == this->blacklist ;
  580. bool is_audio = a_node == this->audio ;
  581. bool is_master = parent_node == this->masterChannels &&
  582. node_id == CONFIG::MASTER_ID ;
  583. bool is_metro = parent_node == this->masterChannels &&
  584. node_id == CONFIG::METRO_ID ;
  585. bool is_local = parent_node == this->localChannels ;
  586. bool is_remote = grandparent_node == this->remoteUsers ;
  587. DEBUG_TRACE_CONFIG_TREE_CHANGED
  588. if (is_gui ) LinJam::ConfigureGui(a_key) ;
  589. else if (is_blacklist) LinJam::ConfigureBlacklist() ;
  590. else if (is_audio ) LinJam::ConfigureAudio() ;
  591. else if (is_master ) LinJam::ConfigureMasterChannel(a_key) ;
  592. else if (is_metro ) LinJam::ConfigureMetroChannel(a_key) ;
  593. else if (is_local ) LinJam::ConfigureLocalChannel(a_node , a_key) ;
  594. else if (is_remote ) LinJam::ConfigureRemoteChannel(parent_node , a_node , a_key) ;
  595. }
  596. void LinJamConfig::valueTreeChildAdded(ValueTree& parent_node , ValueTree& node)
  597. {
  598. DEBUG_TRACE_CONFIG_TREE_ADDED
  599. Identifier node_id = node.getType() ;
  600. bool is_blacklist = parent_node == this->blacklist ;
  601. if (is_blacklist)
  602. {
  603. LinJam::ConfigureBlacklist() ; LinJam::HandleUserInfoChanged() ;
  604. }
  605. }
  606. void LinJamConfig::valueTreeChildRemoved(ValueTree& parent_node , ValueTree& node ,
  607. int /*prev_idx*/ )
  608. {
  609. DEBUG_TRACE_CONFIG_TREE_REMOVED
  610. Identifier node_id = node.getType() ;
  611. bool is_blacklist = parent_node == this->blacklist ;
  612. if (is_blacklist)
  613. {
  614. LinJam::ConfigureBlacklist() ; LinJam::HandleUserInfoChanged() ;
  615. }
  616. }