LinJam.cpp 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366
  1. /*
  2. ==============================================================================
  3. Linjam.cpp
  4. Created: 24 May 2014 5:03:01pm
  5. Author: me
  6. ==============================================================================
  7. */
  8. #include "LinJam.h"
  9. #include "Channel.h"
  10. #include "Constants.h"
  11. #if DEBUG
  12. # include "./Trace/TraceLinJam.h"
  13. #endif // DEBUG
  14. #ifdef _MSC_VER
  15. # include <float.h>
  16. # define isnan(x) _isnan(x)
  17. #endif
  18. /* LinJam class public class variables */
  19. LinJamConfig* LinJam::Config ;
  20. /* LinJam class private class variables */
  21. NJClient* LinJam::Client = nullptr ; // Initialize()
  22. MainContent* LinJam::Gui = nullptr ; // Initialize()
  23. audioStreamer* LinJam::Audio = nullptr ; // Initialize()
  24. bool LinJam::IsAudioInitialized = false ; // InitializeAudio()
  25. Value LinJam::IsAudioEnabled = Value() ; // InitializeAudio()
  26. SortedSet<int> LinJam::FreeAudioSources = SortedSet<int>() ; // InitializeAudio()
  27. SortedSet<int> LinJam::FreeAudioSourcePairs = SortedSet<int>() ; // InitializeAudio()
  28. double LinJam::GuiBeatOffset ; // InitializeAudio()
  29. File LinJam::SessionDir ; // PrepareSessionDirectory()
  30. Value LinJam::Status = Value() ; // Initialize()
  31. String LinJam::PrevRecordingTime ; // Disconnect()
  32. /* LinJam class public class methods */
  33. /* state methods */
  34. void LinJam::Connect()
  35. {
  36. Client->Disconnect() ;
  37. String host = Config->server[CONFIG::HOST_ID].toString() ;
  38. String login = Config->server[CONFIG::LOGIN_ID].toString() ;
  39. String pass = Config->server[CONFIG::PASS_ID].toString() ;
  40. bool is_anonymous = bool(Config->server[CONFIG::IS_ANONYMOUS_ID]) ;
  41. if (is_anonymous) { login = "anonymous:" + login ; pass = "" ; }
  42. DEBUG_TRACE_CONNECT
  43. Gui->statusbar->setStatusL(GUI::CONNECTING_STATUS_TEXT + host) ;
  44. Client->Connect(host.toRawUTF8() , login.toRawUTF8() , pass.toRawUTF8()) ;
  45. }
  46. void LinJam::Disconnect() { Client->Disconnect() ; PrevRecordingTime = "" ; }
  47. void LinJam::Shutdown()
  48. {
  49. JNL::close_socketlib() ; delete Audio ;
  50. CleanSessionDir() ; delete Config ;
  51. }
  52. /* getters/setters */
  53. bool LinJam::IsAgreed()
  54. {
  55. ValueTree server = Config->getCurrentServer() ;
  56. bool should_always_agree = server.isValid() &&
  57. bool(server.getProperty(CONFIG::SHOULD_AGREE_ID)) ;
  58. bool is_agreed = bool(Config->server[CONFIG::IS_AGREED_ID]) ||
  59. should_always_agree ;
  60. return is_agreed ;
  61. }
  62. SortedSet<int> LinJam::GetFreeAudioSources() { return FreeAudioSources ; }
  63. SortedSet<int> LinJam::GetFreeAudioSourcePairs() { return FreeAudioSourcePairs ; }
  64. /* GUI event handlers */
  65. bool LinJam::AddLocalChannel(ValueTree channel_store)
  66. {
  67. DEBUG_TRACE_ADD_LOCAL_CHANNEL
  68. int channel_idx = int(channel_store[CONFIG::CHANNEL_IDX_ID]) ;
  69. int source_n = int(channel_store[CONFIG::SOURCE_N_ID]) ;
  70. bool is_stereo = int(channel_store[CONFIG::STEREO_ID]) != CONFIG::MONO ;
  71. // sanity check
  72. int max_n_channels = Client->GetMaxLocalChannels() ;
  73. bool is_valid_channel_idx = channel_idx >= 0 && channel_idx < max_n_channels ;
  74. bool does_channel_exist = IsConfiguredChannel(channel_idx) ;
  75. bool is_valid_source_n = source_n >= 0 && source_n < GetNumAudioSources() ;
  76. int n_vacant_channels = GetNumVacantChannels() ;
  77. bool are_sufficient_n_channels = (!is_stereo && n_vacant_channels >= 1) ||
  78. (is_stereo && n_vacant_channels >= 2) ;
  79. if (does_channel_exist || !is_valid_source_n || !are_sufficient_n_channels)
  80. return false ;
  81. // assign NJClient channel index for new unstored local channel
  82. if (!is_valid_channel_idx)
  83. {
  84. channel_idx = GetVacantLocalChannelIdx() ;
  85. channel_store.setProperty(CONFIG::CHANNEL_IDX_ID , channel_idx , nullptr) ;
  86. }
  87. // add new channel to store
  88. channel_store = Config->addChannel(Config->localChannels , channel_store) ;
  89. if (!channel_store.isValid()) return false ;
  90. DEBUG_TRACE_DUMP_FREE_INPUTS_VB
  91. DEBUG_TRACE_INSTANTIATE_LOCAL_CHANNEL
  92. // create local Channel GUI
  93. Gui->mixer->addChannel(GUI::LOCALS_GUI_ID , channel_store) ;
  94. // configure NJClient
  95. ConfigureLocalChannel(channel_store , CONFIG::CONFIG_INIT_ID) ;
  96. return true ;
  97. }
  98. void LinJam::RemoveLocalChannel(ValueTree channel_store)
  99. {
  100. DEBUG_TRACE_REMOVE_LOCAL_CHANNEL
  101. Identifier channel_id = channel_store.getType() ;
  102. int channel_idx = int(channel_store[CONFIG::CHANNEL_IDX_ID]) ;
  103. int pair_idx = int(channel_store[CONFIG::PAIR_IDX_ID]) ;
  104. // configure NJClient
  105. Client->DeleteLocalChannel(channel_idx) ;
  106. Client->DeleteLocalChannel(pair_idx) ;
  107. Client->NotifyServerOfChannelChange() ;
  108. // destroy channel GUI
  109. Gui->mixer->removeChannel(GUI::LOCALS_GUI_ID , channel_id) ;
  110. // destroy channel storage
  111. Config->destroyChannel(Config->localChannels , channel_store) ;
  112. DEBUG_TRACE_DUMP_FREE_INPUTS_VB
  113. }
  114. void LinJam::SendChat(String chat_text)
  115. {
  116. DEBUG_TRACE_CHAT_OUT
  117. if ((chat_text = chat_text.trim()).isEmpty()) return ;
  118. if (!chat_text.startsWith("/"))
  119. Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_MSG.toRawUTF8() , chat_text.toRawUTF8()) ;
  120. else
  121. {
  122. // handle irc-style command
  123. String command = chat_text.upToFirstOccurrenceOf(" " , false , false) ;
  124. bool is_me_command = (!command.compare(CLIENT::CHATMSG_CMD_ME)) ;
  125. bool is_pm_command = (!command.compare(CLIENT::CHATMSG_CMD_MSG)) ;
  126. bool is_admin_command = (!command.compare(CLIENT::CHATMSG_CMD_ADMIN)) ;
  127. bool is_user_command = (!command.compare(CLIENT::CHATMSG_CMD_TOPIC) ||
  128. !command.compare(CLIENT::CHATMSG_CMD_KICK) ||
  129. !command.compare(CLIENT::CHATMSG_CMD_BPI) ||
  130. !command.compare(CLIENT::CHATMSG_CMD_BPM) ) ;
  131. #ifndef ACCEPT_CHAT_COMMANDS // (issue #19)
  132. Gui->chat->addChatLine(GUI::SERVER_NICK , "commands disabled") ; return ;
  133. #endif // CHAT_COMMANDS_BUGGY
  134. if (is_me_command)
  135. {
  136. String msg = String(Client->GetUserName()) + " " + chat_text ;
  137. Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_MSG.toRawUTF8() , msg.toRawUTF8()) ;
  138. }
  139. else if (is_user_command)
  140. {
  141. String msg = chat_text.substring(1) ;
  142. Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_ADMIN.toRawUTF8() , msg.toRawUTF8()) ;
  143. }
  144. else if (is_admin_command)
  145. {
  146. String msg = chat_text.substring(6).trim() ;
  147. Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_ADMIN.toRawUTF8() , msg.toRawUTF8()) ;
  148. }
  149. else if (is_pm_command)
  150. {
  151. String to_user = chat_text.substring(4).trim() ;
  152. to_user = to_user.upToFirstOccurrenceOf(StringRef(" ") , false , false) ;
  153. String msg = to_user.fromFirstOccurrenceOf(StringRef(" ") , false , false).trim() ;
  154. if (to_user.isEmpty() || msg.isEmpty())
  155. Gui->chat->addChatLine(GUI::SERVER_NICK , GUI::INVALID_PM_MSG) ;
  156. else // if (does_user_exist(to_user)) // TODO: this safe yea ? // (issue #19)
  157. {
  158. Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_PRIVMSG.toRawUTF8() , msg.toRawUTF8()) ;
  159. Gui->chat->addChatLine("(PM -> " + to_user + ")" , msg) ;
  160. }
  161. }
  162. else Gui->chat->addChatLine(GUI::SERVER_NICK , GUI::UNKNOWN_COMMAND_MSG) ;
  163. }
  164. }
  165. void LinJam::ConfigPending() { Status = LINJAM_STATUS_CONFIGPENDING ; }
  166. void LinJam::ConfigDismissed() { Status = LINJAM_STATUS_READY ; }
  167. /* LinJam class private class methods */
  168. /* initialization methods */
  169. bool LinJam::Initialize(NJClient* nj_client , MainContent* main_content ,
  170. const String& args )
  171. {
  172. DEBUG_TRACE_LINJAM_INIT
  173. Client = nj_client ;
  174. Gui = main_content ;
  175. // TODO: parse command line args for autojoin (issue #9)
  176. // load persistent configuration and prepare audio save directory
  177. if ((Config = new LinJamConfig()) == nullptr) return false ;
  178. if (!Config->isConfigSane()) return false ;
  179. if (!PrepareSessionDirectory()) return false ;
  180. // instantiate configuration GUI components
  181. Gui->instantiateConfig(Config->audio , Config->client , Config->subscriptions) ;
  182. // configure NINJAM client
  183. ConfigureNinjam() ;
  184. // initialize networking
  185. Status.addListener(Config) ; JNL::open_socketlib() ;
  186. // create audiostreamer
  187. IsAudioEnabled.addListener(Gui->config) ; InitializeAudio() ;
  188. return true ;
  189. }
  190. bool LinJam::PrepareSessionDirectory()
  191. {
  192. File this_binary = File::getSpecialLocation(File::currentExecutableFile) ;
  193. File this_dir = this_binary.getParentDirectory() ;
  194. SessionDir = File(this_dir.getFullPathName() + CONFIG::SESSION_DIR) ;
  195. SessionDir.createDirectory() ; CleanSessionDir() ;
  196. bool does_session_dir_exist = SessionDir.isDirectory() ;
  197. const char* session_dir_path = SessionDir.getFullPathName().toRawUTF8() ;
  198. if (does_session_dir_exist) Client->SetWorkDir(session_dir_path) ;
  199. return does_session_dir_exist ;
  200. }
  201. void LinJam::ConfigureNinjam()
  202. {
  203. int save_audio_mode = int( Config->client [CONFIG::SAVE_AUDIO_MODE_ID]) ;
  204. bool should_save_log = bool(Config->client [CONFIG::SHOULD_SAVE_LOG_ID]) ;
  205. int debug_level = int( Config->client [CONFIG::DEBUG_LEVEL_ID]) ;
  206. int subscribe_mode = int( Config->subscriptions[CONFIG::SUBSCRIBE_MODE_ID]) ;
  207. Client->LicenseAgreementCallback = OnLicense ;
  208. Client->ChatMessage_Callback = OnChatmsg ;
  209. Client->config_savelocalaudio = save_audio_mode ;
  210. Client->config_debug_level = debug_level ;
  211. Client->config_autosubscribe = subscribe_mode ;
  212. // set log file
  213. if (should_save_log && save_audio_mode > NJClient::SAVE_AUDIO_NONE)
  214. Client->SetLogFile((SessionDir.getFullPathName() + CONFIG::LOG_FILE).toRawUTF8()) ;
  215. // add bots and ignored users to ignore list
  216. ConfigureSubscriptions() ;
  217. }
  218. void LinJam::InitializeAudio()
  219. {
  220. if (Audio != nullptr) { delete Audio ; Audio = nullptr ; }
  221. #ifdef _WIN32
  222. int win_interface_n = int(Config->audio[CONFIG::WIN_AUDIO_IF_ID]) ;
  223. int asio_driver = int(Config->audio[CONFIG::ASIO_DRIVER_ID]) ;
  224. int asio_input0 = int(Config->audio[CONFIG::ASIO_INPUT0_ID]) ;
  225. int asio_input1 = int(Config->audio[CONFIG::ASIO_INPUT1_ID]) ;
  226. int asio_output0 = int(Config->audio[CONFIG::ASIO_OUTPUT0_ID]) ;
  227. int asio_output1 = int(Config->audio[CONFIG::ASIO_OUTPUT1_ID]) ;
  228. int ks_sample_rate = int(Config->audio[CONFIG::KS_SAMPLERATE_ID]) ;
  229. int ks_bit_depth = int(Config->audio[CONFIG::KS_BITDEPTH_ID]) ;
  230. int ks_n_buffers = int(Config->audio[CONFIG::KS_NBLOCKS_ID]) ;
  231. int ks_buffer_size = int(Config->audio[CONFIG::KS_BLOCKSIZE_ID]) ;
  232. int ds_device[2][4] = { { int(Config->audio[CONFIG::DS_INPUT0_ID]) ,
  233. int(Config->audio[CONFIG::DS_INPUT1_ID]) ,
  234. int(Config->audio[CONFIG::DS_INPUT2_ID]) ,
  235. int(Config->audio[CONFIG::DS_INPUT3_ID]) } ,
  236. { int(Config->audio[CONFIG::DS_OUTPUT0_ID]) ,
  237. int(Config->audio[CONFIG::DS_OUTPUT1_ID]) ,
  238. int(Config->audio[CONFIG::DS_OUTPUT2_ID]) ,
  239. int(Config->audio[CONFIG::DS_OUTPUT3_ID]) } } ;
  240. int ds_sample_rate = int(Config->audio[CONFIG::DS_SAMPLERATE_ID]) ;
  241. int ds_bit_depth = int(Config->audio[CONFIG::DS_BITDEPTH_ID]) ;
  242. int ds_n_buffers = int(Config->audio[CONFIG::DS_NBLOCKS_ID]) ;
  243. int ds_buffer_size = int(Config->audio[CONFIG::DS_BLOCKSIZE_ID]) ;
  244. /*
  245. ds_sample_rate = CONFIG::DEFAULT_DS_SAMPLERATE ;
  246. ds_bit_depth = CONFIG::DEFAULT_DS_BITDEPTH ;
  247. ds_device[2][4] = {{0,0,0,0},{0,0,0,0}};
  248. ds_n_buffers = CONFIG::DEFAULT_DS_N_BLOCKS;
  249. ds_buffer_size = CONFIG::DEFAULT_DS_BLOCKSIZE;
  250. */
  251. int wave_device[2] = { int(Config->audio[CONFIG::WAVE_INPUT_ID]) ,
  252. int(Config->audio[CONFIG::WAVE_OUTPUT_ID]) } ;
  253. int wave_sample_rate = int(Config->audio[CONFIG::WAVE_SAMPLERATE_ID]) ;
  254. int wave_bit_depth = int(Config->audio[CONFIG::WAVE_BITDEPTH_ID]) ;
  255. int wave_n_buffers = int(Config->audio[CONFIG::WAVE_NBLOCKS_ID]) ;
  256. int wave_buffer_size = int(Config->audio[CONFIG::WAVE_BLOCKSIZE_ID]) ;
  257. #else // _WIN32
  258. # ifdef _MAC
  259. String mac_device_name = Config->audio[CONFIG::MAC_DEVICE_ID].toString() ;
  260. int mac_n_inputs = int(Config->audio[CONFIG::MAC_NINPUTS_ID]) ;
  261. int mac_sample_rate = int(Config->audio[CONFIG::MAC_SAMPLERATE_ID]) ;
  262. int mac_bit_depth = int(Config->audio[CONFIG::MAC_BITDEPTH_ID]) ;
  263. # else // _MAC
  264. int nix_interface_n = int(Config->audio[CONFIG::NIX_AUDIO_IF_ID]) ;
  265. int jack_n_inputs = int(Config->audio[CONFIG::JACK_NINPUTS_ID]) ;
  266. int jack_n_outputs = int(Config->audio[CONFIG::JACK_NOUTPUTS_ID]) ;
  267. String jack_name = Config->audio[CONFIG::JACK_NAME_ID].toString() ;
  268. String alsa_config = Config->audio[CONFIG::ALSA_CONFIG_ID].toString() ;
  269. # endif // _MAC
  270. #endif // _WIN32
  271. /* TODO: NJClient alsa init takes a string config
  272. ideally alsa would have individual config params
  273. and config via the GUI like the rest
  274. ideally libninjam would accomodate this but we could as well
  275. concatenate the expected string here
  276. NJClient accepts the following config_strings (original cli params) =>
  277. win =>
  278. -noaudiocfg
  279. -jesusonic <path to jesusonic root dir>
  280. mac =>
  281. -audiostr device_name[,output_device_name]
  282. nix =>
  283. -audiostr "option value [option value ...]"
  284. ALSA audio options are:
  285. in hw:0,0 -- set input device
  286. out hw:0,0 -- set output device
  287. srate 48000 -- set samplerate
  288. nch 2 -- set channels
  289. bps 16 -- set bits/sample
  290. bsize 2048 -- set blocksize (bytes)
  291. nblock 16 -- set number of blocks
  292. */
  293. #ifdef _WIN32
  294. DEBUG_TRACE_AUDIO_INIT_WIN
  295. switch ((audioStreamer::Interface)win_interface_n)
  296. {
  297. case audioStreamer::WIN_AUDIO_ASIO:
  298. {
  299. if (njasiodrv_avail())
  300. {
  301. char device_buffer[2050] ; // 1025 wchars wsprintf max
  302. wsprintf(device_buffer , CLIENT::ASIO_DEVICE_FMT , asio_driver , asio_input0 ,
  303. asio_input1 , asio_output0 , asio_output1 ) ;
  304. char *device = device_buffer ;
  305. Audio = njasiodrv_create_asio_streamer(&device , OnSamples) ;
  306. }
  307. break ;
  308. }
  309. case audioStreamer::WIN_AUDIO_KS:
  310. {
  311. Audio = create_audioStreamer_KS(ks_sample_rate , ks_bit_depth , &ks_n_buffers ,
  312. &ks_buffer_size , OnSamples ) ;
  313. break ;
  314. }
  315. case audioStreamer::WIN_AUDIO_DS:
  316. {
  317. GUID guid[2] ; memcpy(guid , ds_device , sizeof(guid)) ;
  318. Audio = create_audioStreamer_DS(ds_sample_rate , ds_bit_depth , guid ,
  319. &ds_n_buffers , &ds_buffer_size , OnSamples) ;
  320. break ;
  321. }
  322. case audioStreamer::WIN_AUDIO_WAVE:
  323. {
  324. Audio = create_audioStreamer_WO(wave_sample_rate , wave_bit_depth ,
  325. wave_device , &wave_n_buffers ,
  326. &wave_buffer_size , OnSamples ) ;
  327. break ;
  328. }
  329. default: break ;
  330. }
  331. #else // _WIN32
  332. # ifdef _MAC
  333. Audio = create_audioStreamer_CoreAudio(&mac_device_name , mac_sample_rate ,
  334. mac_n_inputs , mac_bit_depth , OnSamples) ;
  335. DEBUG_TRACE_AUDIO_INIT_MAC
  336. # else // _MAC
  337. DEBUG_TRACE_AUDIO_INIT_NIX
  338. switch ((audioStreamer::Interface)nix_interface_n)
  339. {
  340. case audioStreamer::NIX_AUDIO_JACK:
  341. {
  342. Audio = create_audioStreamer_JACK(jack_name.toRawUTF8() , jack_n_inputs ,
  343. jack_n_outputs , OnSamples , Client) ;
  344. DEBUG_TRACE_AUDIO_INIT_JACK
  345. if (Audio != nullptr) break ; // else fallback on ALSA
  346. }
  347. case audioStreamer::NIX_AUDIO_ALSA:
  348. {
  349. Audio = create_audioStreamer_ALSA("alsa_config.toRawUTF8()" , OnSamples) ;
  350. DEBUG_TRACE_AUDIO_INIT_ALSA
  351. break ;
  352. }
  353. default: break ;
  354. }
  355. # endif // _MAC
  356. #endif // _WIN32
  357. DEBUG_TRACE_AUDIO_INIT
  358. if (Audio != nullptr)
  359. {
  360. // kludge to sync loop progress to audible ticks
  361. GuiBeatOffset = Audio->m_srate * GUI::BEAT_PROGRESS_OFFSET ;
  362. // populate input source names arrays for ChannelConfig GUI
  363. int n_audio_sources = GetNumAudioSources() ;
  364. FreeAudioSources.clear() ; FreeAudioSourcePairs.clear() ;
  365. for (int source_n = 0 ; source_n < n_audio_sources ; ++source_n)
  366. {
  367. FreeAudioSources.add(source_n) ;
  368. if (source_n % 2) FreeAudioSourcePairs.add(source_n - 1) ;
  369. }
  370. // create master and stored local input channels
  371. if (!IsAudioInitialized) { ConfigureInitialChannels() ; IsAudioInitialized = true ; }
  372. }
  373. // set audio and status value holders for Config GUI
  374. IsAudioEnabled = Audio != nullptr ;
  375. Status = (Audio != nullptr)? LINJAM_STATUS_READY : LINJAM_STATUS_AUDIOERROR ;
  376. }
  377. void LinJam::ConfigureInitialChannels()
  378. {
  379. if (IsAudioInitialized) return ;
  380. // add master and metro channel GUI mixers and configure NJClient master channels
  381. ValueTree master_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::MASTER_ID) ;
  382. ValueTree metro_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::METRO_ID) ;
  383. Gui->mixer->addChannel(GUI::MASTERS_GUI_ID , master_store) ;
  384. Gui->mixer->addChannel(GUI::MASTERS_GUI_ID , metro_store) ;
  385. ConfigureMasterChannel(CONFIG::CONFIG_INIT_ID) ;
  386. ConfigureMetroChannel( CONFIG::CONFIG_INIT_ID) ;
  387. DEBUG_TRACE_INITIAL_CHANNELS
  388. // add local Channel GUI mixers and configure NJClient input channels
  389. ValueTree channels = Config->localChannels ;
  390. for (int channel_n = 0 ; channel_n < channels.getNumChildren() ; ++channel_n)
  391. {
  392. ValueTree channel_store = Config->localChannels.getChild(channel_n) ;
  393. if (!AddLocalChannel(channel_store))
  394. {
  395. // destroy corrupted channel storage
  396. Config->destroyChannel(Config->localChannels , channel_store) ;
  397. --channel_n ;
  398. }
  399. }
  400. }
  401. void LinJam::CleanSessionDir()
  402. {
  403. int save_audio_state = int(Config->client[CONFIG::SAVE_AUDIO_MODE_ID]) ;
  404. if (save_audio_state > 0) return ;
  405. DEBUG_TRACE_CLEAN_SESSION
  406. #ifdef CLEAN_SESSION
  407. File this_binary = File::getSpecialLocation(File::currentExecutableFile) ;
  408. File this_dir = this_binary.getParentDirectory() ;
  409. if (!SessionDir.isDirectory() || !SessionDir.isAChildOf(this_dir)) return ;
  410. // TODO: the *.ninjam directories created when save_loca_audio == -1 (delete ASAP)
  411. // are not being deleted implicitly as they presumably should be (issue #32)
  412. DirectoryIterator session_dir_iter(SessionDir , false , "*.*" , File::findFilesAndDirectories) ;
  413. while (session_dir_iter.next()) session_dir_iter.getFile().deleteRecursively() ;
  414. #endif // CLEAN_SESSION
  415. }
  416. /* NJClient callbacks */
  417. int LinJam::OnLicense(int user32 , char* license_text)
  418. {
  419. if (!IsAgreed()) Gui->license->setLicenseText(CharPointer_UTF8(license_text)) ;
  420. DEBUG_TRACE_LICENSE
  421. return IsAgreed() ;
  422. }
  423. void LinJam::OnChatmsg(int user32 , NJClient* instance , const char** parms , int nparms)
  424. {
  425. if (!parms[0]) return ;
  426. String chat_type = String(CharPointer_UTF8(parms[CLIENT::CHATMSG_TYPE_IDX])) ;
  427. String chat_user = String(CharPointer_UTF8(parms[CLIENT::CHATMSG_USER_IDX]))
  428. .upToFirstOccurrenceOf(CONFIG::USER_IP_SPLIT_CHAR , false , false) ;
  429. String chat_text = String(CharPointer_UTF8(parms[CLIENT::CHATMSG_MSG_IDX])) ;
  430. bool is_topic_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_TOPIC)) ;
  431. bool is_bcast_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_MSG)) ;
  432. bool is_priv_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_PRIVMSG)) ;
  433. bool is_join_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_JOIN)) ;
  434. bool is_part_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_PART)) ;
  435. DEBUG_TRACE_CHAT_IN
  436. if (is_topic_msg)
  437. {
  438. if (chat_text.isEmpty()) return ;
  439. Gui->chat->setTopic(chat_text) ;
  440. if (chat_user.isEmpty()) chat_text = GUI::TOPIC_TEXT + chat_text ;
  441. else chat_text = chat_user + GUI::SET_TOPIC_TEXT + chat_text ;
  442. chat_user = GUI::SERVER_NICK ;
  443. }
  444. else if (is_bcast_msg)
  445. {
  446. if (chat_text.isEmpty()) return ;
  447. if (chat_user.isEmpty()) chat_user = GUI::SERVER_NICK ;
  448. else if (chat_text.startsWith(CLIENT::CHATMSG_CMD_VOTE))
  449. {
  450. // customize voting messages
  451. StringArray tokens = StringArray::fromTokens(StringRef(chat_text) , false) ;
  452. String bpi_bpm_cmd = tokens[1] ;
  453. String bpi_bpm_val = tokens[2] ;
  454. bool is_bpi_msg = !bpi_bpm_cmd.compare(CLIENT::CHATMSG_CMD_BPI.substring(1).trim()) ;
  455. bool is_bpm_msg = !bpi_bpm_cmd.compare(CLIENT::CHATMSG_CMD_BPM.substring(1).trim()) ;
  456. if ((is_bpi_msg || is_bpm_msg) && bpi_bpm_val.containsOnly(NETWORK::DIGITS))
  457. {
  458. chat_text = chat_user + " votes to set " + bpi_bpm_cmd + " to " + bpi_bpm_val ;
  459. chat_user = GUI::SERVER_NICK ;
  460. }
  461. }
  462. }
  463. else if (is_priv_msg)
  464. {
  465. if (chat_user.isEmpty() || chat_text.isEmpty()) return ;
  466. chat_user += GUI::PM_TEXT ;
  467. }
  468. else if (is_join_msg || is_part_msg)
  469. {
  470. if (chat_user.isEmpty()) return ;
  471. chat_text = chat_user + GUI::JOINPART_TEXTa +
  472. ((is_join_msg)? GUI::JOIN_TEXT : GUI::PART_TEXT) + GUI::JOINPART_TEXTb ;
  473. chat_user = GUI::SERVER_NICK ;
  474. }
  475. Gui->chat->addChatLine(chat_user , chat_text) ;
  476. }
  477. void LinJam::OnSamples(float** input_buffer , int n_input_channels ,
  478. float** output_buffer , int n_output_channels ,
  479. int n_samples , int sample_rate)
  480. {
  481. if (Audio == nullptr)
  482. {
  483. // clear all output buffers
  484. size_t n_bytes = n_samples * sizeof(float) ;
  485. for (int channel_n = 0 ; channel_n < n_output_channels ; ++channel_n)
  486. memset(output_buffer[channel_n] , 0 , n_bytes) ;
  487. }
  488. else Client->AudioProc(input_buffer , n_input_channels ,
  489. output_buffer , n_output_channels ,
  490. n_samples , sample_rate ) ;
  491. }
  492. /* NJClient runtime routines */
  493. void LinJam::HandleTimer(int timer_id) override
  494. {
  495. #if DEBUG_EXIT_IMMEDIAYELY
  496. DBG("[DEBUG]: DEBUG_EXIT_IMMEDIAYELY defined - bailing") ; Client->quit() ;
  497. #endif // DEBUG_EXIT_IMMEDIAYELY
  498. switch (timer_id)
  499. {
  500. case CLIENT::CLIENT_TIMER_ID: PumpClient() ; break ;
  501. case CLIENT::GUI_TIMER_LO_ID: UpdateGuiLowPriority() ; break ;
  502. case CLIENT::GUI_TIMER_HI_ID: UpdateGuiHighPriority() ; break ;
  503. default: break ;
  504. }
  505. }
  506. void LinJam::UpdateStatus()
  507. {
  508. // update status if not in error or hold state
  509. int status = int(Status.getValue()) ;
  510. // TODO: when room is full and Status goes to LINJAM_STATUS_CONFIGPENDING (opens Config GUI)
  511. // but Status will toggle back to LINJAM_STATUS_ROOMFULL (closing Config GUI)
  512. // either Value holders do not update fast enough to configuring audio while in room
  513. // or this comparisson is failing some reason
  514. // may need to make Status synchronous again or leave room while configuring audio
  515. // if (State != LINJAM_STATUS_CONFIGPENDING)
  516. if (status >= LINJAM_STATUS_READY) status = Client->GetStatus() ;
  517. String error_msg = CharPointer_UTF8(Client->GetErrorStr()) ;
  518. bool is_licence_pending = status == NJClient::NJC_STATUS_INVALIDAUTH && !IsAgreed() ;
  519. bool is_room_full = !error_msg.compare(CLIENT::SERVER_FULL_STATUS) ;
  520. if (is_licence_pending) status = LINJAM_STATUS_LICENSEPENDING ;
  521. else if (is_room_full) status = LINJAM_STATUS_ROOMFULL ;
  522. Status = status ;
  523. }
  524. void LinJam::PumpClient()
  525. {
  526. UpdateStatus() ;
  527. if (Client->HasUserInfoChanged()) HandleUserInfoChanged() ;
  528. if (Client->GetStatus() >= NJClient::NJC_STATUS_OK) while (!Client->Run()) ;
  529. }
  530. void LinJam::HandleStatusChanged()
  531. {
  532. DEBUG_TRACE_STATUS_CHANGED
  533. #ifdef DEBUG_AUTOLOGIN_CHANNEL
  534. if (Status == NJClient::NJC_STATUS_PRECONNECT)
  535. { Config->setCurrentServer(DEBUG_AUTOLOGIN_CHANNEL , "nobody" , "" , true) ;
  536. LinJam::Config->server.setProperty(CONFIG::IS_AGREED_ID , true , nullptr) ; Connect() ; }
  537. #endif // DEBUG_AUTOLOGIN_CHANNEL
  538. // ignore sentinel value
  539. if (Status == LINJAM_STATUS_READY) return ;
  540. // set status indicator
  541. String host = Client->GetHostName() ;
  542. String status_text =
  543. (Status == LINJAM_STATUS_AUDIOERROR) ? GUI::AUDIO_INIT_ERROR_MSG :
  544. (Status == LINJAM_STATUS_CONFIGPENDING) ? GUI::CONFIG_PENDING_MSG :
  545. (Status == LINJAM_STATUS_LICENSEPENDING) ? GUI::PENDING_LICENSE_STATUS_TEXT :
  546. (Status == LINJAM_STATUS_ROOMFULL) ? GUI::ROOM_FULL_STATUS_TEXT :
  547. (Status == NJClient::NJC_STATUS_DISCONNECTED) ? GUI::DISCONNECTED_STATUS_TEXT :
  548. (Status == NJClient::NJC_STATUS_INVALIDAUTH) ? GUI::INVALID_AUTH_STATUS_TEXT :
  549. (Status == NJClient::NJC_STATUS_CANTCONNECT) ? GUI::FAILED_CONNECTION_STATUS_TEXT :
  550. (Status == NJClient::NJC_STATUS_OK) ? GUI::CONNECTED_STATUS_TEXT + host :
  551. (Status == NJClient::NJC_STATUS_PRECONNECT) ? GUI::IDLE_STATUS_TEXT :
  552. Status.toString() ;
  553. Gui->statusbar->setStatusL(status_text) ;
  554. // set GUI state
  555. Component* top_component =
  556. (Status == LINJAM_STATUS_AUDIOERROR) ? Gui->config :
  557. (Status == LINJAM_STATUS_CONFIGPENDING) ? Gui->config :
  558. (Status == LINJAM_STATUS_LICENSEPENDING) ? Gui->license :
  559. (Status == LINJAM_STATUS_ROOMFULL) ? Gui->login :
  560. (Status == NJClient::NJC_STATUS_DISCONNECTED) ? Gui->login :
  561. (Status == NJClient::NJC_STATUS_INVALIDAUTH) ? Gui->login :
  562. (Status == NJClient::NJC_STATUS_CANTCONNECT) ? Gui->login :
  563. (Status == NJClient::NJC_STATUS_OK) ? Gui->chat :
  564. (Status == NJClient::NJC_STATUS_PRECONNECT) ? Gui->login :
  565. (Component*)Gui->background ;
  566. top_component ->toFront(true) ;
  567. Gui->background->toBehind(top_component) ;
  568. if (Status == NJClient::NJC_STATUS_OK)
  569. {
  570. Gui->mixer->toFront(false) ; Gui->loop->toFront(false) ; UpdateGuiLowPriority() ;
  571. }
  572. // reset login state
  573. if (Status == NJClient::NJC_STATUS_INVALIDAUTH)
  574. Config->server.setProperty(CONFIG::IS_AGREED_ID , false , nullptr) ;
  575. // store the current server configuration
  576. if (Status == NJClient::NJC_STATUS_OK) Config->setServer() ;
  577. }
  578. void LinJam::HandleUserInfoChanged()
  579. {
  580. #ifdef NO_UPDATE_REMOTES
  581. return ;
  582. #endif // NO_UPDATE_REMOTES
  583. DEBUG_TRACE_REMOTE_CHANNELS_VB
  584. // initialize dictionary for pruning parted users GUI elements
  585. ValueTree active_users = ValueTree("active-users") ;
  586. // fetch remote user states from server
  587. int user_idx = -1 ; String user_name ;
  588. while ((user_name = GetRemoteUserName(++user_idx)).isNotEmpty())
  589. {
  590. Identifier user_id = Config->makeUserId(user_name) ;
  591. bool is_bot = NETWORK::KNOWN_BOTS.contains(user_id) ;
  592. std::string name = (user_name = String(user_id)).toStdString() ;
  593. bool should_ignore_user = !!Client->config_autosubscribe_userlist.count(name) ;
  594. // get or create remote user storage
  595. ValueTree user_store = Config->getOrAddRemoteUser(user_name) ;
  596. if (!user_store.isValid()) continue ;
  597. // update stored remote user state
  598. UpdateRemoteUserState(user_store , user_idx , should_ignore_user) ;
  599. if (should_ignore_user) continue ;
  600. // create remote user GUI
  601. if (Gui->mixer->addRemoteUser(user_store , Config->subscriptions))
  602. {
  603. // create remote master channel storage
  604. ValueTree master_store = Config->getOrAddRemoteChannel(user_id , CONFIG::MASTER_KEY) ;
  605. if (!master_store.isValid()) continue ;
  606. // create remote master channel GUI and restore stored NJClient user state
  607. if (Gui->mixer->addChannel(user_id , master_store))
  608. ConfigureRemoteChannel(user_store , master_store , CONFIG::CONFIG_INIT_ID) ;
  609. }
  610. // initialize array for pruning removed channels GUI elements
  611. Array<var> active_channels = Array<var>() ;
  612. int channel_n = -1 ; int channel_idx ;
  613. while (~(channel_idx = Client->EnumUserChannels(user_idx , ++channel_n)))
  614. {
  615. // get or create remote channel storage
  616. String channel_name = GetRemoteChannelClientName(user_idx , channel_idx) ;
  617. ValueTree channel_store = Config->getOrAddRemoteChannel(user_id , channel_name ,
  618. channel_idx ) ;
  619. String stored_name = GetStoredChannelName(channel_store) ;
  620. if (!channel_store.isValid()) continue ;
  621. // rename existing channel (and GUI asynchronously)
  622. if (channel_name.compare(stored_name))
  623. channel_store.setProperty(CONFIG::CHANNEL_NAME_ID , channel_name , nullptr) ;
  624. // update faux-stereo status (configures NJClient and GUI asynchronously)
  625. int stereo_status = Config->setRemoteStereo(user_store , channel_store , stored_name) ;
  626. // create remote channel GUI and restore stored NJClient remote channel state
  627. if (Gui->mixer->addChannel(user_id , channel_store))
  628. ConfigureRemoteChannel(user_store , channel_store , CONFIG::CONFIG_INIT_ID) ;
  629. // add channel to GUI prune list unless hidden stereo pair channel
  630. if (stereo_status != CONFIG::STEREO_R)
  631. active_channels.add(var(String(channel_store.getType()))) ;
  632. }
  633. // add user to GUI prune list
  634. active_users.setProperty(user_id , active_channels , nullptr) ;
  635. }
  636. // prune user and channel GUIs
  637. Gui->mixer->pruneRemotes(active_users) ;
  638. }
  639. void LinJam::UpdateGuiHighPriority() { UpdateLoopProgress() ; UpdateVuMeters() ; }
  640. void LinJam::UpdateGuiLowPriority() { UpdateRecordingTime() ; }
  641. void LinJam::UpdateLoopProgress()
  642. {
  643. #ifdef NO_UPDATE_LOOP_PROGRESS_GUI
  644. return ;
  645. #endif // NO_UPDATE_LOOP_PROGRESS_GUI
  646. // compute loop progress
  647. int sample_n , n_samples ; Client->GetPosition(&sample_n , &n_samples) ;
  648. int bpi = Client->GetBPI() ;
  649. float bpm = Client->GetActualBPM() ;
  650. double linear_progress = (sample_n + GuiBeatOffset) / n_samples ;
  651. int beat_n = ((int)(bpi * linear_progress) % bpi) + 1 ;
  652. double discrete_progress = (float)beat_n / bpi ;
  653. // update loop progress
  654. Gui->loop->updateBeat(beat_n) ;
  655. Gui->loop->loopProgress = discrete_progress ; // linear_progress ;
  656. Gui->statusbar->setStatusR(String(bpi) + " bpi @ " + String(bpm) + " bpm") ;
  657. // update metro VU loop progress
  658. ValueTree metro_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::METRO_ID) ;
  659. double metro_is_muted = bool( metro_store[CONFIG::IS_MUTED_ID]) ;
  660. double metro_pan = double(metro_store[CONFIG::PAN_ID]) ;
  661. double metro_vu = (metro_is_muted)? 0.0 : discrete_progress ;
  662. double metro_vu_l = (metro_pan < 0.0)? metro_vu : metro_vu * (1.0 - metro_pan) ;
  663. double metro_vu_r = (metro_pan > 0.0)? metro_vu : metro_vu * (1.0 + metro_pan) ;
  664. metro_store.setProperty(CONFIG::VU_LEFT_ID , metro_vu_l , nullptr) ;
  665. metro_store.setProperty(CONFIG::VU_RIGHT_ID , metro_vu_r , nullptr) ;
  666. }
  667. void LinJam::UpdateVuMeters()
  668. {
  669. #ifdef NO_UPDATE_VU_METERS_GUI
  670. return ;
  671. #endif // NO_UPDATE_VU_METERS_GUI
  672. double master_vu_l = 0.0 ; double master_vu_r = 0.0 ;
  673. /* update local VUs asynchronously */
  674. int channel_n = -1 ; int channel_idx ;
  675. while (~(channel_idx = Client->EnumLocalChannels(++channel_n)))
  676. {
  677. ValueTree channel_store = Config->getChannelByIdx(Config->localChannels , channel_idx) ;
  678. double local_pan = double(channel_store[CONFIG::PAN_ID]) ;
  679. double local_vu_l = VAL2DB(Client->GetLocalChannelPeak(channel_idx)) -
  680. CLIENT::VU_DB_MIN ;
  681. int stereo_status = int(channel_store[CONFIG::STEREO_ID]) ;
  682. if (!channel_store.isValid()) continue ;
  683. if (stereo_status == CONFIG::MONO)
  684. {
  685. channel_store.setProperty(CONFIG::VU_LEFT_ID , local_vu_l , nullptr) ;
  686. // compensate master for local channel pan
  687. ScalePannedMonoVus(local_vu_l , local_pan , &master_vu_l , &master_vu_r) ; // mutates
  688. }
  689. else if (stereo_status == CONFIG::STEREO_L)
  690. {
  691. // ensure this faux-stereo channel has a matching faux-stereo pair channel
  692. int pair_idx = int(channel_store[CONFIG::PAIR_IDX_ID]) ;
  693. double local_vu_r = VAL2DB(Client->GetLocalChannelPeak(pair_idx)) -
  694. CLIENT::VU_DB_MIN ;
  695. // compensate for pan
  696. ComputePannedVus(local_pan , &local_vu_l , &local_vu_r) ; // mutates
  697. // update local channel VUs asynchronously
  698. channel_store.setProperty(CONFIG::VU_LEFT_ID , local_vu_l , nullptr) ;
  699. master_vu_l = AddDecibels(master_vu_l , local_vu_l) ;
  700. channel_store.setProperty(CONFIG::VU_RIGHT_ID , local_vu_r , nullptr) ;
  701. master_vu_r = AddDecibels(master_vu_r , local_vu_r) ;
  702. }
  703. }
  704. /* update remote VUs asynchronously */
  705. int user_idx = -1 ; String user_name ;
  706. while ((user_name = GetRemoteUserName(++user_idx)).isNotEmpty())
  707. {
  708. Identifier user_id = Config->makeUserId(user_name) ;
  709. ValueTree user_store = Config->getUserById(user_id) ;
  710. double remote_master_vu_l = 0.0 ;
  711. double remote_master_vu_r = 0.0 ; channel_n = -1 ; Identifier vu_id ;
  712. while (~(channel_idx = Client->EnumUserChannels(user_idx , ++channel_n)))
  713. {
  714. ValueTree channel_store = Config->getChannelByIdx(user_store , channel_idx) ;
  715. double remote_pan = double(channel_store[CONFIG::PAN_ID]) ;
  716. double remote_vu_l = VAL2DB(Client->GetUserChannelPeak(user_idx , channel_idx)) -
  717. CLIENT::VU_DB_MIN ;
  718. int stereo_status = int(channel_store[CONFIG::STEREO_ID]) ;
  719. if (!channel_store.isValid()) continue ;
  720. if (stereo_status == CONFIG::MONO)
  721. {
  722. channel_store.setProperty(CONFIG::VU_LEFT_ID , remote_vu_l , nullptr) ;
  723. // compensate remote master for remote channel pan
  724. ScalePannedMonoVus(remote_vu_l , remote_pan ,
  725. &remote_master_vu_l , &remote_master_vu_r) ; // mutates
  726. }
  727. else if (stereo_status == CONFIG::STEREO_L)
  728. {
  729. // ensure this faux-stereo channel has a matching faux-stereo pair channel
  730. int pair_idx = int(channel_store[CONFIG::PAIR_IDX_ID]) ;
  731. double remote_vu_r = VAL2DB(Client->GetUserChannelPeak(user_idx , pair_idx)) -
  732. CLIENT::VU_DB_MIN ;
  733. // compensate for pan
  734. ComputePannedVus(remote_pan , &remote_vu_l , &remote_vu_r) ; // mutates
  735. // update remote channel VUs asynchronously
  736. channel_store.setProperty(CONFIG::VU_LEFT_ID , remote_vu_l , nullptr) ;
  737. remote_master_vu_l = AddDecibels(remote_master_vu_l , remote_vu_l) ;
  738. channel_store.setProperty(CONFIG::VU_RIGHT_ID , remote_vu_r , nullptr) ;
  739. remote_master_vu_r = AddDecibels(remote_master_vu_r , remote_vu_r) ;
  740. }
  741. }
  742. // update remote master VUs asynchronously
  743. ValueTree remote_master_store = Config->getChannelById(user_id , CONFIG::MASTER_ID) ;
  744. double remote_master_pan = double(remote_master_store[CONFIG::PAN_ID]) ;
  745. if (!remote_master_store.isValid()) continue ;
  746. // compensate remote master for remote master pan
  747. if (remote_master_pan > 0.0)
  748. ScalePannedMonoVus( remote_master_vu_l , remote_master_pan ,
  749. &remote_master_vu_l , &remote_master_vu_r) ; // mutates
  750. if (remote_master_pan < 0.0)
  751. ScalePannedMonoVus( remote_master_vu_r , remote_master_pan ,
  752. &remote_master_vu_l , &remote_master_vu_r) ; // mutates
  753. remote_master_store.setProperty(CONFIG::VU_LEFT_ID , remote_master_vu_l , nullptr) ;
  754. remote_master_store.setProperty(CONFIG::VU_RIGHT_ID , remote_master_vu_r , nullptr) ;
  755. master_vu_l = AddDecibels(master_vu_l , remote_master_vu_l) ;
  756. master_vu_r = AddDecibels(master_vu_r , remote_master_vu_r) ;
  757. }
  758. /* update master VU asynchronously */
  759. ValueTree master_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::MASTER_ID) ;
  760. double master_pan = double(master_store[CONFIG::PAN_ID]) ;
  761. // compensate master for master pan
  762. if (master_pan > 0.0)
  763. ScalePannedMonoVus(master_vu_l , master_pan , &master_vu_l , &master_vu_r) ; // mutates
  764. if (master_pan < 0.0)
  765. ScalePannedMonoVus(master_vu_r , master_pan , &master_vu_l , &master_vu_r) ; // mutates
  766. master_store.setProperty(CONFIG::VU_LEFT_ID , master_vu_l , nullptr) ;
  767. master_store.setProperty(CONFIG::VU_RIGHT_ID , master_vu_r , nullptr) ;
  768. }
  769. void LinJam::UpdateRecordingTime()
  770. {
  771. #ifdef NO_UPDATE_RECORDING_TIME_GUI
  772. return ;
  773. #endif // NO_UPDATE_RECORDING_TIME_GUI
  774. if (Status != NJClient::NJC_STATUS_OK) return ;
  775. // NOTE: parsing recording time is brittle - strictly dependent on the values
  776. // in NETWORK::KNOWN_HOSTS , NETWORK::KNOWN_BOTS , and CLIENT::BOT_CHANNELIDX
  777. String host = String(Client->GetHostName()) ;
  778. String bpi = String(Client->GetBPI()) ;
  779. String bpm = String((int)Client->GetActualBPM()) ;
  780. bool server_has_bot = NETWORK::KNOWN_HOSTS.contains(host) ;
  781. String recording_time = String::empty ;
  782. if (server_has_bot)
  783. {
  784. for (int bot_n = 0 ; bot_n < NETWORK::N_KNOWN_BOTS ; ++bot_n)
  785. {
  786. ValueTree user_store = Config->getUserById(NETWORK::KNOWN_BOTS.getUnchecked(bot_n)) ;
  787. if (!user_store.isValid()) continue ;
  788. int bot_idx = int(user_store[CONFIG::USER_IDX_ID]) ;
  789. recording_time = GetRemoteChannelClientName(bot_idx , CLIENT::BOT_CHANNELIDX) ;
  790. }
  791. bool has_recording_time_changed = !!recording_time.compare(PrevRecordingTime) ;
  792. bool is_this_first_pass = PrevRecordingTime.isEmpty() ;
  793. bool should_show_time = (has_recording_time_changed && !is_this_first_pass) ;
  794. PrevRecordingTime = recording_time ;
  795. recording_time = (should_show_time)? " - " + recording_time : String::empty ;
  796. }
  797. Gui->setTitle(host + " - " + bpi + "bpi / " + bpm + "bpm" + recording_time) ;
  798. }
  799. /* GUI event handlers */
  800. void LinJam::ConfigureSubscriptions()
  801. {
  802. DEBUG_TRACE_SUBSCRIPTIONS
  803. ValueTree subscriptions = Config->subscriptions.createCopy() ;
  804. int subscribe_mode = int( Config->subscriptions[CONFIG::SUBSCRIBE_MODE_ID]) ;
  805. int should_hide_bots = bool(Config->client [CONFIG::SHOULD_HIDE_BOTS_ID]) ;
  806. bool should_ignore_users = subscribe_mode == (int)NJClient::SUBSCRIBE_DENY ;
  807. Client->config_autosubscribe_userlist.clear() ;
  808. if (!should_ignore_users) return ;
  809. if (should_hide_bots) for (int bot_n = 0 ; bot_n < NETWORK::KNOWN_BOTS.size() ; ++bot_n)
  810. {
  811. ValueTree bot_node = ValueTree(NETWORK::KNOWN_BOTS.getUnchecked(bot_n)) ;
  812. subscriptions.addChild(bot_node , -1 , nullptr) ;
  813. }
  814. for (int user_n = 0 ; user_n < subscriptions.getNumChildren() ; ++user_n)
  815. {
  816. String user_name = String(subscriptions.getChild(user_n).getType()) ;
  817. Client->config_autosubscribe_userlist.insert(user_name.toStdString()) ;
  818. }
  819. }
  820. /* NJClient config helpers */
  821. int LinJam::GetNumAudioSources()
  822. {
  823. return (Audio != nullptr)? Audio->m_innch : 0 ;
  824. }
  825. int LinJam::GetNumLocalChannels()
  826. {
  827. int n_occupied_slots = -1 ; while (~Client->EnumLocalChannels(++n_occupied_slots)) ;
  828. return n_occupied_slots ;
  829. }
  830. int LinJam::GetNumVacantChannels()
  831. {
  832. return (Audio != nullptr)? GetNumAudioSources() - GetNumLocalChannels() : 0 ;
  833. }
  834. int LinJam::GetVacantLocalChannelIdx()
  835. {
  836. // find the first vacant NJClient local channel slot index
  837. int channel_idx = -1 ; while (IsConfiguredChannel(++channel_idx)) ;
  838. bool is_vacant_slot = (channel_idx < GetNumAudioSources()) ;
  839. return (is_vacant_slot)? channel_idx : -1 ;
  840. }
  841. String LinJam::GetStoredChannelName(ValueTree channel_store)
  842. {
  843. return channel_store[CONFIG::CHANNEL_NAME_ID].toString() ;
  844. }
  845. String LinJam::GetLocalChannelClientName(int channel_idx)
  846. {
  847. return CharPointer_UTF8(Client->GetLocalChannelName(channel_idx)) ;
  848. }
  849. String LinJam::GetRemoteUserName(int user_idx)
  850. {
  851. return CharPointer_UTF8(Client->GetUserState(user_idx)) ;
  852. }
  853. String LinJam::GetRemoteChannelClientName(int user_idx , int channel_idx)
  854. {
  855. return CharPointer_UTF8(Client->GetUserChannelState(user_idx , channel_idx)) ;
  856. }
  857. bool LinJam::IsConfiguredChannel(int channel_idx)
  858. {
  859. return GetLocalChannelClientName(channel_idx).isNotEmpty() ;
  860. }
  861. double LinJam::AddDecibels(double vu_l , double vu_r)
  862. {
  863. return 10 * log10(pow(10.0 , (vu_l / 10.0)) + pow(10.0 , (vu_r / 10.0))) ;
  864. }
  865. void LinJam::ComputePannedVus(double pan , double* vu_l , double* vu_r)
  866. {
  867. double vu_l_in = *vu_l ;
  868. double vu_r_in = *vu_r ;
  869. if (pan > 0.0) // pan right
  870. {
  871. // decrease left contribution to left vu
  872. *vu_l *= log10(10.0 * (1.0 - pan)) ;
  873. // increase left contribution to right vu
  874. *vu_r = AddDecibels(vu_r_in , vu_l_in * pan) ;
  875. }
  876. if (pan < 0.0) // pan left
  877. {
  878. // increase right contribution to left vu
  879. *vu_l = AddDecibels(vu_l_in , vu_r_in * -pan) ;
  880. // decrease right contribution to right vu
  881. *vu_r *= log10(10.0 * (1.0 + pan)) ;
  882. }
  883. // cap underflows
  884. if (isnan(*vu_l)) *vu_l = 0.0 ;
  885. if (isnan(*vu_r)) *vu_r = 0.0 ;
  886. if (vu_l_in == 0.0 && vu_r_in == 0.0) *vu_l = *vu_r = 0.0 ;
  887. }
  888. void LinJam::ScalePannedMonoVus(double vu_mono , double pan , double* vu_l , double* vu_r)
  889. {
  890. // compute panned mono channel contributions to left and right VU master meters
  891. *vu_l = (pan > 0.0)? vu_mono * log10(10.0 * (1.0 - pan)) : vu_mono ; // pan_left
  892. *vu_r = (pan < 0.0)? vu_mono * log10(10.0 * (1.0 + pan)) : vu_mono ; // pan right
  893. // cap underflows
  894. if (isnan(*vu_l)) *vu_l = 0.0 ;
  895. if (isnan(*vu_r)) *vu_r = 0.0 ;
  896. }
  897. float LinJam::ComputeStereoPan(float pan , int stereo_status)
  898. {
  899. bool is_mono_channel = stereo_status == CONFIG::MONO ;
  900. bool is_pair_channel = stereo_status == CONFIG::STEREO_R ;
  901. return (is_mono_channel)? pan : (!is_pair_channel) ?
  902. ((pan <= 0.0f)? -1.0f : -1.0f + (pan * 2.0f)) :
  903. ((pan >= 0.0f)? +1.0f : +1.0f + (pan * 2.0f)) ;
  904. }
  905. void LinJam::UpdateRemoteUserState(ValueTree user_store , int user_idx ,
  906. bool should_ignore_user )
  907. {
  908. Identifier user_id = user_store.getType() ;
  909. ValueTree master_store = Config->getOrAddRemoteChannel(user_id , CONFIG::MASTER_KEY) ;
  910. user_store .setProperty(CONFIG::USER_IDX_ID , user_idx , nullptr) ;
  911. master_store.setProperty(CONFIG::IS_XMIT_RCV_ID , !should_ignore_user , nullptr) ;
  912. }
  913. void LinJam::ConfigureMasterChannel(Identifier a_key)
  914. {
  915. ValueTree master_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::MASTER_ID) ;
  916. if (!master_store.isValid()) return ;
  917. // load stored config for this channel
  918. float volume = float(master_store[CONFIG::VOLUME_ID]) ;
  919. float pan = float(master_store[CONFIG::PAN_ID]) ;
  920. bool is_muted = bool( master_store[CONFIG::IS_MUTED_ID]) ;
  921. // determine which NJClient channel params to modify
  922. bool should_init_all = (a_key == CONFIG::CONFIG_INIT_ID) ;
  923. bool should_set_volume = (should_init_all || a_key == CONFIG::VOLUME_ID) ;
  924. bool should_set_pan = (should_init_all || a_key == CONFIG::PAN_ID) ;
  925. bool should_set_is_muted = (should_init_all || a_key == CONFIG::IS_MUTED_ID) ;
  926. // configure NJClient master channel
  927. if (should_set_volume) Client->config_mastervolume = (float)DB2VAL(volume) ;
  928. if (should_set_pan) Client->config_masterpan = pan ;
  929. if (should_set_is_muted) Client->config_mastermute = is_muted ;
  930. }
  931. void LinJam::ConfigureMetroChannel(Identifier a_key)
  932. {
  933. ValueTree metro_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::METRO_ID) ;
  934. if (!metro_store.isValid()) return ;
  935. // load stored config for this channel
  936. float volume = float(metro_store[CONFIG::VOLUME_ID]) ;
  937. float pan = float(metro_store[CONFIG::PAN_ID]) ;
  938. bool is_muted = bool( metro_store[CONFIG::IS_MUTED_ID]) ;
  939. int source_n = int( metro_store[CONFIG::SOURCE_N_ID]) ;
  940. bool is_stereo = int( metro_store[CONFIG::STEREO_ID]) != CONFIG::MONO ;
  941. // determine which NJClient channel params to modify
  942. bool should_init_all = (a_key == CONFIG::CONFIG_INIT_ID) ;
  943. bool should_set_volume = (should_init_all || a_key == CONFIG::VOLUME_ID) ;
  944. bool should_set_pan = (should_init_all || a_key == CONFIG::PAN_ID) ;
  945. bool should_set_is_muted = (should_init_all || a_key == CONFIG::IS_MUTED_ID) ;
  946. bool should_set_source_n = (should_init_all || a_key == CONFIG::SOURCE_N_ID) ;
  947. bool should_set_stereo = (should_init_all || a_key == CONFIG::STEREO_ID) ;
  948. // configure NJClient metro channel
  949. if (should_set_volume) Client->config_metronome = (float)DB2VAL(volume) ;
  950. if (should_set_pan) Client->config_metronome_pan = pan ;
  951. if (should_set_is_muted) Client->config_metronome_mute = is_muted ;
  952. if (should_set_source_n) Client->config_metronome_channel = source_n ;
  953. if (should_set_stereo) Client->config_metronome_stereoout = is_stereo ;
  954. }
  955. void LinJam::ConfigureLocalChannel(ValueTree channel_store , Identifier a_key)
  956. {
  957. if (!channel_store.isValid() ||
  958. a_key == CONFIG::CHANNEL_IDX_ID || a_key == CONFIG::PAIR_IDX_ID ||
  959. a_key == CONFIG::VU_LEFT_ID || a_key == CONFIG::VU_RIGHT_ID ) return ;
  960. // load stored config for this channel
  961. int channel_idx = int( channel_store[CONFIG::CHANNEL_IDX_ID]) ;
  962. int pair_idx = int( channel_store[CONFIG::PAIR_IDX_ID]) ;
  963. float volume = float(channel_store[CONFIG::VOLUME_ID]) ;
  964. float pan = float(channel_store[CONFIG::PAN_ID]) ;
  965. bool is_xmit = bool( channel_store[CONFIG::IS_XMIT_RCV_ID]) ;
  966. bool is_muted = bool( channel_store[CONFIG::IS_MUTED_ID]) ;
  967. bool is_solo = bool( channel_store[CONFIG::IS_SOLO_ID]) ;
  968. int source_n = int( channel_store[CONFIG::SOURCE_N_ID]) ;
  969. int bit_depth = int( channel_store[CONFIG::BIT_DEPTH_ID]) ;
  970. int stereo_status = int( channel_store[CONFIG::STEREO_ID]) ;
  971. String channel_name = channel_store[CONFIG::CHANNEL_NAME_ID].toString() ;
  972. channel_name = Config->makeStereoName(channel_name , stereo_status) ;
  973. // determine which NJClient channel params to modify
  974. bool should_init_all = a_key == CONFIG::CONFIG_INIT_ID ;
  975. bool should_set_name = a_key == CONFIG::CHANNEL_NAME_ID || should_init_all ;
  976. bool should_set_volume = a_key == CONFIG::VOLUME_ID || should_init_all ;
  977. bool should_set_pan = a_key == CONFIG::PAN_ID ||
  978. a_key == CONFIG::STEREO_ID || should_init_all ;
  979. bool should_set_is_xmit = a_key == CONFIG::IS_XMIT_RCV_ID || should_init_all ;
  980. bool should_set_is_muted = a_key == CONFIG::IS_MUTED_ID || should_init_all ;
  981. bool should_set_is_solo = a_key == CONFIG::IS_SOLO_ID || should_init_all ;
  982. bool should_set_source_n = a_key == CONFIG::SOURCE_N_ID || should_init_all ;
  983. bool should_set_bit_depth = a_key == CONFIG::BIT_DEPTH_ID || should_init_all ;
  984. bool should_set_stereo = a_key == CONFIG::STEREO_ID || should_init_all ;
  985. DEBUG_TRACE_CONFIGURE_LOCAL_CHANNEL
  986. // handle channel name change
  987. const char* new_name = (should_set_name)? channel_name.toRawUTF8() : nullptr ;
  988. // handle faux-stereo panning
  989. if (should_set_pan) pan = ComputeStereoPan(pan , stereo_status) ;
  990. // configure NJClient local channel
  991. if (should_set_name || should_set_source_n || should_set_bit_depth || should_set_is_xmit)
  992. Client->SetLocalChannelInfo(channel_idx , new_name ,
  993. should_set_source_n , source_n ,
  994. should_set_bit_depth , bit_depth ,
  995. should_set_is_xmit , is_xmit ) ;
  996. if (should_set_volume || should_set_pan || should_set_is_muted || should_set_is_solo)
  997. Client->SetLocalChannelMonitoring(channel_idx ,
  998. should_set_volume , (float)DB2VAL(volume) ,
  999. should_set_pan , pan ,
  1000. should_set_is_muted , is_muted ,
  1001. should_set_is_solo , is_solo ) ;
  1002. // configure faux-stereo pair implicitly
  1003. if (stereo_status == CONFIG::STEREO_L)
  1004. {
  1005. if (should_set_stereo)
  1006. {
  1007. // handle mono->stereo conversion
  1008. if (~(pair_idx = GetVacantLocalChannelIdx()))
  1009. {
  1010. a_key = CONFIG::CONFIG_INIT_ID ;
  1011. channel_store.setProperty(CONFIG::PAIR_IDX_ID , pair_idx , nullptr) ;
  1012. }
  1013. else
  1014. {
  1015. channel_store.setProperty(CONFIG::STEREO_ID , CONFIG::MONO , nullptr) ;
  1016. return ;
  1017. }
  1018. }
  1019. // force mono conversion bypassing configuration of pair to be deleted (fires next case)
  1020. if (a_key == CONFIG::SOURCE_N_ID && source_n % 2)
  1021. channel_store.setProperty(CONFIG::STEREO_ID , CONFIG::MONO , nullptr) ;
  1022. // configure unstored pair channel
  1023. else
  1024. {
  1025. ValueTree pair_store = channel_store.createCopy() ;
  1026. String pair_name = Config->makeStereoName(channel_name , CONFIG::STEREO_R) ;
  1027. int pair_source_n = source_n + 1 ;
  1028. pair_store.setProperty(CONFIG::CHANNEL_NAME_ID , pair_name , nullptr) ;
  1029. pair_store.setProperty(CONFIG::CHANNEL_IDX_ID , pair_idx , nullptr) ;
  1030. pair_store.setProperty(CONFIG::SOURCE_N_ID , pair_source_n , nullptr) ;
  1031. pair_store.setProperty(CONFIG::STEREO_ID , CONFIG::STEREO_R , nullptr) ;
  1032. ConfigureLocalChannel(pair_store , a_key) ;
  1033. }
  1034. // update client channels names on mono->stereo conversion
  1035. if (should_set_stereo)
  1036. channel_store.setProperty(CONFIG::CHANNEL_NAME_ID , channel_name , nullptr) ;
  1037. }
  1038. // handle stereo->mono conversion
  1039. else if (stereo_status == CONFIG::MONO && should_set_stereo)
  1040. {
  1041. Client->DeleteLocalChannel(pair_idx) ;
  1042. channel_store.setProperty(CONFIG::PAIR_IDX_ID , CONFIG::DEFAULT_CHANNEL_IDX , nullptr) ;
  1043. channel_store.setProperty(CONFIG::CHANNEL_NAME_ID , channel_name , nullptr) ;
  1044. }
  1045. Client->NotifyServerOfChannelChange() ;
  1046. }
  1047. void LinJam::ConfigureRemoteChannel(ValueTree user_store , ValueTree channel_store ,
  1048. Identifier a_key )
  1049. {
  1050. if (!user_store.isValid() || !channel_store.isValid() ||
  1051. a_key == CONFIG::CHANNEL_NAME_ID ||
  1052. a_key == CONFIG::CHANNEL_IDX_ID || a_key == CONFIG::PAIR_IDX_ID ||
  1053. a_key == CONFIG::VU_LEFT_ID || a_key == CONFIG::VU_RIGHT_ID ) return ;
  1054. // load stored config for this channel
  1055. int user_idx = int( user_store [CONFIG::USER_IDX_ID]) ;
  1056. int channel_idx = int( channel_store[CONFIG::CHANNEL_IDX_ID]) ;
  1057. int pair_idx = int( channel_store[CONFIG::PAIR_IDX_ID]) ;
  1058. float volume = float(channel_store[CONFIG::VOLUME_ID]) ;
  1059. float pan = float(channel_store[CONFIG::PAN_ID]) ;
  1060. bool is_rcv = bool( channel_store[CONFIG::IS_XMIT_RCV_ID]) ;
  1061. bool is_muted = bool( channel_store[CONFIG::IS_MUTED_ID]) ;
  1062. bool is_solo = bool( channel_store[CONFIG::IS_SOLO_ID]) ;
  1063. int sink_n = 0 ; // TODO: not yet clear how to handle remote sink_n
  1064. int stereo_status = int( channel_store[CONFIG::STEREO_ID]) ; \
  1065. bool is_stereo = true ;
  1066. // determine which NJClient channel params to modify
  1067. bool should_init_all = a_key == CONFIG::CONFIG_INIT_ID ;
  1068. bool should_set_volume = a_key == CONFIG::VOLUME_ID || should_init_all ;
  1069. bool should_set_pan = a_key == CONFIG::PAN_ID ||
  1070. a_key == CONFIG::STEREO_ID || should_init_all ;
  1071. bool should_set_is_rcv = a_key == CONFIG::IS_XMIT_RCV_ID || should_init_all ;
  1072. bool should_set_is_muted = a_key == CONFIG::IS_MUTED_ID || should_init_all ;
  1073. bool should_set_is_solo = a_key == CONFIG::IS_SOLO_ID || should_init_all ;
  1074. bool should_set_stereo = a_key == CONFIG::STEREO_ID || should_init_all ;
  1075. DEBUG_TRACE_CONFIGURE_REMOTE_CHANNEL
  1076. if (channel_idx == CONFIG::MASTER_CHANNEL_IDX)
  1077. {
  1078. int channel_n = -1 ;
  1079. // configure NJClient remote master channel
  1080. if (should_set_volume || should_set_pan || should_set_is_muted)
  1081. Client->SetUserState(user_idx ,
  1082. should_set_volume , (float)DB2VAL(volume) ,
  1083. should_set_pan , pan ,
  1084. should_set_is_muted , is_muted ) ;
  1085. // or apply user master pseudo rcv or solo control over all real user channels
  1086. else if (should_set_is_rcv || should_set_is_solo)
  1087. while (~(channel_idx = Client->EnumUserChannels(user_idx , ++channel_n)))
  1088. {
  1089. channel_store = Config->getChannelByIdx(user_store , channel_idx) ;
  1090. ConfigureRemoteChannel(user_store , channel_store , a_key) ;
  1091. }
  1092. }
  1093. else
  1094. {
  1095. // handle faux-stereo panning
  1096. if (should_set_pan) pan = ComputeStereoPan(pan , stereo_status) ;
  1097. // configure NJClient remote channel allowing master overrides
  1098. ValueTree master_store = Config->getUserMasterChannel(user_store) ;
  1099. bool is_master_rcv = bool(master_store[CONFIG::IS_XMIT_RCV_ID]) ;
  1100. bool is_master_solo = bool(master_store[CONFIG::IS_SOLO_ID]) ;
  1101. Client->SetUserChannelState(user_idx , channel_idx ,
  1102. should_set_is_rcv , is_rcv && is_master_rcv ,
  1103. should_set_volume , (float)DB2VAL(volume) ,
  1104. should_set_pan , pan ,
  1105. should_set_is_muted , is_muted ,
  1106. should_set_is_solo , is_solo || is_master_solo ,
  1107. should_init_all , sink_n ,
  1108. should_init_all , is_stereo ) ;
  1109. // configure faux-stereo pair implicitly
  1110. if (stereo_status == CONFIG::STEREO_L)
  1111. {
  1112. ValueTree pair_store = Config->getChannelByIdx(user_store , pair_idx) ;
  1113. if (should_set_stereo || should_init_all)
  1114. ConfigureRemoteChannel(user_store , pair_store , CONFIG::CONFIG_INIT_ID) ;
  1115. else pair_store.setProperty(a_key , channel_store[a_key] , nullptr) ;
  1116. }
  1117. }
  1118. }