1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516 |
- /*
- ==============================================================================
- Linjam.cpp
- Created: 24 May 2014 5:03:01pm
- Author: me
- ==============================================================================
- */
- #include "LinJam.h"
- #include "./Trace/TraceLinJam.h"
- #ifdef _MSC_VER
- # include <float.h>
- # define isnan(x) _isnan(x)
- #endif
- /* LinJam class public class variables */
- LinJamConfig* LinJam::Config ;
- /* LinJam class private class variables */
- NJClient* LinJam::Client = nullptr ; // Initialize()
- MainContent* LinJam::Gui = nullptr ; // Initialize()
- MultiTimer* LinJam::Timer = nullptr ; // Initialize()
- audioStreamer* LinJam::Audio = nullptr ; // Initialize()
- String LinJam::AutoJoinHost ; // Initialize()
- Value LinJam::Status = Value() ; // Initialize()
- bool LinJam::IsAudioInitialized = false ; // InitializeAudio()
- SortedSet<int> LinJam::FreeAudioSources = SortedSet<int>() ; // InitializeAudio()
- SortedSet<int> LinJam::FreeAudioSourcePairs = SortedSet<int>() ; // InitializeAudio()
- double LinJam::GuiBeatOffset ; // InitializeAudio()
- File LinJam::SessionDir ; // PrepareSessionDirectory()
- int LinJam::RetryLogin ; // Connect()
- String LinJam::PrevRecordingTime ; // Disconnect()
- URL LinJam::PollUrl ;
- ScopedPointer<LinJam::RoomSort> LinJam::RoomSorter = new LinJam::RoomSort() ;
- /* LinJam class public class methods */
- /* state methods */
- void LinJam::SignIn(String host , String login , String pass , bool is_anonymous)
- {
- Config->setCredentials(host , login , pass , is_anonymous) ;
- RetryLogin = NETWORK::N_LOGIN_RETRIES ; Connect() ;
- }
- void LinJam::Connect()
- {
- Client->Disconnect() ;
- String host = str( Config->server[CONFIG::HOST_ID ]) ;
- String login = str( Config->server[CONFIG::LOGIN_ID ]) ;
- String pass = str( Config->server[CONFIG::PASS_ID ]) ;
- bool is_anonymous = bool(Config->server[CONFIG::IS_ANONYMOUS_ID]) ;
- if (is_anonymous) { login = "anonymous:" + login ; pass = "" ; }
- DEBUG_TRACE_CONNECT
- Gui->statusbar->setStatusL(GUI::CONNECTING_TEXT + host) ;
- Client->Connect(host.toRawUTF8() , login.toRawUTF8() , pass.toRawUTF8()) ;
- }
- void LinJam::Disconnect() { Client->Disconnect() ; PrevRecordingTime = "" ; }
- /* getters/setters */
- ValueTree LinJam::GetCredentials(String host) { return Config->getCredentials(host) ; }
- bool LinJam::IsAgreed() { return bool(Config->server[CONFIG::IS_AGREED_ID]) ; }
- SortedSet<int> LinJam::GetFreeAudioSources() { return FreeAudioSources ; }
- SortedSet<int> LinJam::GetFreeAudioSourcePairs() { return FreeAudioSourcePairs ; }
- /* GUI event handlers */
- bool LinJam::AddLocalChannel(ValueTree channel_store)
- {
- DEBUG_TRACE_ADD_LOCAL_CHANNEL
- int channel_idx = int(channel_store[CONFIG::CHANNEL_IDX_ID]) ;
- int source_n = int(channel_store[CONFIG::SOURCE_N_ID ]) ;
- bool is_stereo = int(channel_store[CONFIG::STEREO_ID ]) != CONFIG::MONO ;
- // sanity check
- int n_sources = GetNumAudioSources() ;
- int max_n_channels = Client->GetMaxLocalChannels() ;
- int n_vacant_channels = GetNumVacantChannels() ;
- bool is_valid_channel_idx = channel_idx >= 0 && channel_idx < max_n_channels ;
- bool is_valid_source_n = source_n >= 0 && source_n < n_sources ;
- bool does_channel_exist = IsConfiguredChannel(channel_idx) ;
- bool are_sufficient_n_channels = (!is_stereo && n_vacant_channels >= 1) ||
- (is_stereo && n_vacant_channels >= 2) ;
- if (does_channel_exist || !is_valid_source_n || !are_sufficient_n_channels)
- return false ;
- // assign NJClient channel index for new unstored local channel
- if (!is_valid_channel_idx)
- {
- channel_idx = GetVacantLocalChannelIdx() ;
- channel_store.setProperty(CONFIG::CHANNEL_IDX_ID , channel_idx , nullptr) ;
- }
- // add new channel to store
- channel_store = Config->addChannel(Config->localChannels , channel_store) ;
- if (!channel_store.isValid()) return false ;
- DEBUG_TRACE_DUMP_FREE_INPUTS_VB
- DEBUG_TRACE_INSTANTIATE_LOCAL_CHANNEL
- // create local Channel GUI
- Gui->mixer->addChannel(GUI::LOCALS_GUI_ID , channel_store) ;
- // configure NJClient
- ConfigureLocalChannel(channel_store , CONFIG::CONFIG_INIT_ID) ;
- return true ;
- }
- void LinJam::RemoveLocalChannel(ValueTree channel_store)
- {
- DEBUG_TRACE_REMOVE_LOCAL_CHANNEL
- Identifier channel_id = channel_store.getType() ;
- int channel_idx = int(channel_store[CONFIG::CHANNEL_IDX_ID]) ;
- int pair_idx = int(channel_store[CONFIG::PAIR_IDX_ID]) ;
- // configure NJClient
- Client->DeleteLocalChannel(channel_idx) ;
- Client->DeleteLocalChannel(pair_idx) ;
- Client->NotifyServerOfChannelChange() ;
- // destroy channel GUI
- Gui->mixer->removeChannel(GUI::LOCALS_GUI_ID , channel_id) ;
- // destroy channel storage
- Config->removeChannel(Config->localChannels , channel_store) ;
- DEBUG_TRACE_DUMP_FREE_INPUTS_VB
- }
- void LinJam::SendChat(String chat_text)
- {
- DEBUG_TRACE_CHAT_OUT
- if ((chat_text = chat_text.trim()).isEmpty()) return ;
- if (!chat_text.startsWith("/"))
- Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_MSG.toRawUTF8() , chat_text.toRawUTF8()) ;
- else
- {
- // handle irc-style command
- String command = chat_text.upToFirstOccurrenceOf(" " , false , false) ;
- bool is_me_command = (!command.compare(CLIENT::CHATMSG_CMD_ME)) ;
- bool is_pm_command = (!command.compare(CLIENT::CHATMSG_CMD_MSG)) ;
- bool is_admin_command = (!command.compare(CLIENT::CHATMSG_CMD_ADMIN)) ;
- bool is_user_command = (!command.compare(CLIENT::CHATMSG_CMD_TOPIC) ||
- !command.compare(CLIENT::CHATMSG_CMD_KICK) ||
- !command.compare(CLIENT::CHATMSG_CMD_BPI) ||
- !command.compare(CLIENT::CHATMSG_CMD_BPM) ) ;
- #ifndef ACCEPT_CHAT_COMMANDS // (issue #19)
- Gui->chat->addChatLine(GUI::SERVER_NICK , "commands disabled") ; return ;
- #endif // CHAT_COMMANDS_BUGGY
- if (is_me_command)
- {
- String msg = String(Client->GetUserName()) + " " + chat_text ;
- Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_MSG.toRawUTF8() , msg.toRawUTF8()) ;
- }
- else if (is_user_command)
- {
- String msg = chat_text.substring(1) ;
- Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_ADMIN.toRawUTF8() , msg.toRawUTF8()) ;
- }
- else if (is_admin_command)
- {
- String msg = chat_text.substring(6).trim() ;
- Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_ADMIN.toRawUTF8() , msg.toRawUTF8()) ;
- }
- else if (is_pm_command)
- {
- String to_user = chat_text.substring(4).trim() ;
- to_user = to_user.upToFirstOccurrenceOf(StringRef(" ") , false , false) ;
- String msg = to_user.fromFirstOccurrenceOf(StringRef(" ") , false , false).trim() ;
- if (to_user.isEmpty() || msg.isEmpty())
- Gui->chat->addChatLine(GUI::SERVER_NICK , GUI::INVALID_PM_MSG) ;
- else // if (does_user_exist(to_user)) // TODO: this safe yea ? // (issue #19)
- {
- Client->ChatMessage_Send(CLIENT::CHATMSG_TYPE_PRIVMSG.toRawUTF8() , msg.toRawUTF8()) ;
- Gui->chat->addChatLine("(PM -> " + to_user + ")" , msg) ;
- }
- }
- else Gui->chat->addChatLine(GUI::SERVER_NICK , GUI::UNKNOWN_COMMAND_MSG) ;
- }
- }
- void LinJam::CleanSessionDir() { SessionDir.deleteRecursively() ; }
- /* LinJam class private class methods */
- /* initialization methods */
- bool LinJam::Initialize(NJClient* nj_client , MainContent* main_content ,
- MultiTimer* multi_timer , const String& cli_args )
- {
- DEBUG_TRACE_INIT
- Client = nj_client ;
- Gui = main_content ;
- Timer = multi_timer ;
- AutoJoinHost = cli_args ; // TODO: parse/validate command line for auto-join (issue #9)
- Status = APP::LINJAM_STATUS_INIT ;
- // prepare runtime initialized constants
- APP::Initialize() ;
- // load persistent configuration and prepare audio save directory
- if ((Config = new LinJamConfig()) == nullptr ||
- !Config->isConfigValid() ||
- !PrepareSessionDirectory() ) return false ;
- // instantiate GUI components requiring model hooks
- Gui->instantiate(Config->gui , Config->client , Config->blacklist ,
- Config->audio , Config->server , Config->servers ,
- Status ) ;
- // configure NINJAM client and initialize networking
- ConfigureNinjam() ; JNL::open_socketlib() ;
- // instantiate audioStreamer
- if (InitializeAudio()) Status = APP::LINJAM_STATUS_READY ;
- // start NJClient pump and GUI update timers
- Timer->startTimer(APP::CLIENT_TIMER_ID , APP::CLIENT_DRIVER_IVL) ;
- Timer->startTimer(APP::GUI_LO_TIMER_ID , APP::GUI_LO_UPDATE_IVL) ;
- //Timer->startTimer(APP::GUI_MD_TIMER_ID , APP::GUI_MD_UPDATE_IVL) ; // unused
- Timer->startTimer(APP::GUI_HI_TIMER_ID , APP::GUI_LO_UPDATE_IVL) ;
- ConfigureGui(CONFIG::UPDATE_IVL_ID) ;
- return true ;
- }
- bool LinJam::PrepareSessionDirectory()
- {
- SessionDir = Config->dataDir.getChildFile(CLIENT::SESSION_DIRNAME) ;
- SessionDir.createDirectory() ;
- DEBUG_TRACE_SESSIONDIR
- bool does_session_dir_exist = SessionDir.isDirectory() ;
- if (does_session_dir_exist)
- Client->SetWorkDir(SessionDir.getFullPathName().toRawUTF8()) ;
- return does_session_dir_exist ;
- }
- void LinJam::ConfigureNinjam()
- {
- int save_audio_mode = int( Config->client [CONFIG::SAVE_AUDIO_MODE_ID]) ;
- bool should_save_log = bool(Config->client [CONFIG::SHOULD_SAVE_LOG_ID]) ;
- int debug_level = int( Config->client [CONFIG::DEBUG_LEVEL_ID ]) ;
- int subscribe_mode = int( Config->blacklist[CONFIG::SUBSCRIBE_MODE_ID ]) ;
- Client->LicenseAgreementCallback = OnLicense ;
- Client->ChatMessage_Callback = OnChatmsg ;
- Client->config_savelocalaudio = save_audio_mode ;
- Client->config_debug_level = debug_level ;
- Client->config_autosubscribe = subscribe_mode ;
- // set log file
- if (should_save_log && save_audio_mode > NJClient::SAVE_NONE)
- Client->SetLogFile((SessionDir.getFullPathName() + CLIENT::LOG_FILENAME).toRawUTF8()) ;
- // add bots and ignored users to ignore list
- ConfigureBlacklist() ;
- }
- void LinJam::ConfigureGui(const Identifier& a_key)
- {
- if (a_key != CONFIG::UPDATE_IVL_ID) return ;
- int gui_update_ivl_n = int(Config->gui[CONFIG::UPDATE_IVL_ID]) ;
- int gui_update_ivl = APP::GUI_HI_UPDATE_IVLS[gui_update_ivl_n] ;
- if (!!gui_update_ivl) Timer->startTimer(APP::GUI_HI_TIMER_ID , gui_update_ivl) ;
- else Timer->stopTimer( APP::GUI_HI_TIMER_ID ) ;
- if (!!gui_update_ivl) return ;
- // zero all VUs
- int n_channels = Config->localChannels.getNumChildren() ;
- for (int channel_n = 0 ; channel_n < n_channels ; ++channel_n)
- {
- ValueTree channel_store = Config->localChannels.getChild(channel_n) ;
- channel_store.setProperty(CONFIG::VU_LEFT_ID , 0.0 , nullptr) ;
- channel_store.setProperty(CONFIG::VU_RIGHT_ID , 0.0 , nullptr) ;
- }
- int n_users = Config->remoteUsers.getNumChildren() ;
- for (int user_n = 0 ; user_n < n_users ; ++user_n)
- {
- ValueTree user_store = Config->remoteUsers.getChild(user_n) ;
- int n_channels = user_store.getNumChildren() ;
- for (int channel_n = 0 ; channel_n < n_channels ; ++channel_n)
- {
- ValueTree channel_store = user_store.getChild(channel_n) ;
- channel_store.setProperty(CONFIG::VU_LEFT_ID , 0.0 , nullptr) ;
- channel_store.setProperty(CONFIG::VU_RIGHT_ID , 0.0 , nullptr) ;
- }
- }
- ValueTree master_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::MASTER_ID) ;
- ValueTree metro_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::METRO_ID ) ;
- master_store.setProperty(CONFIG::VU_LEFT_ID , 0.0 , nullptr) ;
- master_store.setProperty(CONFIG::VU_RIGHT_ID , 0.0 , nullptr) ;
- metro_store .setProperty(CONFIG::VU_LEFT_ID , 0.0 , nullptr) ;
- metro_store .setProperty(CONFIG::VU_RIGHT_ID , 0.0 , nullptr) ;
- }
- void LinJam::ConfigureBlacklist()
- {
- DEBUG_TRACE_BLACKLIST
- ValueTree blacklist = Config->blacklist.createCopy() ;
- int subscribe_mode = int( Config->blacklist[CONFIG::SUBSCRIBE_MODE_ID ]) ;
- int should_hide_bots = bool(Config->client [CONFIG::SHOULD_HIDE_BOTS_ID]) ;
- bool should_ignore_users = subscribe_mode == NJClient::SUBSCRIBE_DENY ;
- Client->config_autosubscribe_userlist.clear() ;
- if (!should_ignore_users) return ;
- if (should_hide_bots)
- for (int bot_n = 0 ; bot_n < NETWORK::KNOWN_BOTS.getNumChildren() ; ++bot_n)
- {
- Identifier bot_name = NETWORK::KNOWN_BOTS.getChild(bot_n).getType() ;
- blacklist.getOrCreateChildWithName(bot_name , nullptr) ;
- }
- for (int user_n = 0 ; user_n < blacklist.getNumChildren() ; ++user_n)
- {
- String user_name = STRING(blacklist.getChild(user_n).getType()) ;
- Client->config_autosubscribe_userlist.insert(user_name.toStdString()) ;
- }
- }
- bool LinJam::InitializeAudio()
- {
- if (Audio != nullptr) { delete Audio ; Audio = nullptr ; }
- // load config
- int audio_api_n = int(Config->audio[CONFIG::AUDIO_API_ID ]) ;
- #ifdef _WIN32
- int asio_driver_n = int(Config->audio[CONFIG::ASIO_DRIVER_ID ]) ;
- int asio_input_b_n = int(Config->audio[CONFIG::ASIO_INPUTB_ID ]) ;
- int asio_input_e_n = int(Config->audio[CONFIG::ASIO_INPUTE_ID ]) ;
- int asio_output_b_n = int(Config->audio[CONFIG::ASIO_OUTPUTB_ID ]) ;
- int asio_output_e_n = int(Config->audio[CONFIG::ASIO_OUTPUTE_ID ]) ;
- bool should_show_asio_cp = int(Config->audio[CONFIG::ASIO_CONTROL_ID ]) ;
- int ks_sample_rate = int(Config->audio[CONFIG::KS_SAMPLERATE_ID ]) ;
- int ks_input_device_n = int(Config->audio[CONFIG::KS_INPUT_ID ]) ;
- int ks_output_device_n = int(Config->audio[CONFIG::KS_OUTPUT_ID ]) ;
- int ks_bit_depth = int(Config->audio[CONFIG::KS_BITDEPTH_ID ]) ;
- int ks_n_buffers = int(Config->audio[CONFIG::KS_NBLOCKS_ID ]) ;
- int ks_buffer_size = int(Config->audio[CONFIG::KS_BLOCKSIZE_ID ]) ;
- String ds_input_device = str(Config->audio[CONFIG::DS_INPUT_ID ]) ;
- String ds_output_device = str(Config->audio[CONFIG::DS_OUTPUT_ID ]) ;
- int ds_sample_rate = int(Config->audio[CONFIG::DS_SAMPLERATE_ID ]) ;
- int ds_bit_depth = int(Config->audio[CONFIG::DS_BITDEPTH_ID ]) ;
- int ds_n_buffers = int(Config->audio[CONFIG::DS_NBLOCKS_ID ]) ;
- int ds_buffer_size = int(Config->audio[CONFIG::DS_BLOCKSIZE_ID ]) ;
- int wave_input_device_n = int(Config->audio[CONFIG::WAVE_INPUT_ID ]) ;
- int wave_output_device_n = int(Config->audio[CONFIG::WAVE_OUTPUT_ID ]) ;
- int wave_sample_rate = int(Config->audio[CONFIG::WAVE_SAMPLERATE_ID]) ;
- int wave_bit_depth = int(Config->audio[CONFIG::WAVE_BITDEPTH_ID ]) ;
- int wave_n_buffers = int(Config->audio[CONFIG::WAVE_NBLOCKS_ID ]) ;
- int wave_buffer_size = int(Config->audio[CONFIG::WAVE_BLOCKSIZE_ID ]) ;
- #else // _WIN32
- # ifdef _MAC
- String ca_input_device = str(Config->audio[CONFIG::CA_INPUT_ID ]) ;
- String ca_output_device = str(Config->audio[CONFIG::CA_OUTPUT_ID ]) ;
- int ca_n_channels = int(Config->audio[CONFIG::CA_NCHANNELS_ID ]) ;
- int ca_sample_rate = int(Config->audio[CONFIG::CA_SAMPLERATE_ID ]) ;
- int ca_bit_depth = int(Config->audio[CONFIG::CA_BITDEPTH_ID ]) ;
- # else // _MAC
- int jack_server_n = int(Config->audio[CONFIG::JACK_SERVER_ID ]) ;
- String jack_client_name = str(Config->audio[CONFIG::JACK_NAME_ID ]) ;
- int jack_n_inputs = int(Config->audio[CONFIG::JACK_NINPUTS_ID ]) ;
- int jack_n_outputs = int(Config->audio[CONFIG::JACK_NOUTPUTS_ID ]) ;
- String alsa_input_device = str(Config->audio[CONFIG::ALSA_INPUT_ID ]) ;
- String alsa_output_device = str(Config->audio[CONFIG::ALSA_OUTPUT_ID ]) ;
- int alsa_n_channels = int(Config->audio[CONFIG::ALSA_NCHANNELS_ID ]) ;
- int alsa_sample_rate = int(Config->audio[CONFIG::ALSA_SAMPLERATE_ID]) ;
- int alsa_bit_depth = int(Config->audio[CONFIG::ALSA_BITDEPTH_ID ]) ;
- int alsa_n_buffers = int(Config->audio[CONFIG::ALSA_NBLOCKS_ID ]) ;
- int alsa_buffer_size = int(Config->audio[CONFIG::ALSA_BLOCKSIZE_ID ]) ;
- # endif // _MAC
- #endif // _WIN32
- #ifdef _WIN32
- DEBUG_TRACE_AUDIO_INIT_WIN
- switch ((audioStreamer::WinApi)audio_api_n)
- {
- # ifndef NO_SUPPORT_ASIO
- case audioStreamer::WIN_AUDIO_ASIO:
- {
- Audio = audioStreamer::NewASIO(OnSamples , asio_driver_n ,
- asio_input_b_n , asio_input_e_n ,
- asio_output_b_n , asio_output_e_n ,
- should_show_asio_cp ) ;
- break ;
- }
- # endif // NO_SUPPORT_ASIO
- # ifndef NO_SUPPORT_KS
- case audioStreamer::WIN_AUDIO_KS:
- {
- // TODO: ks_input_device_n, ks_output_device_n unused
- Audio = audioStreamer::NewKS(OnSamples ,
- ks_sample_rate , ks_bit_depth ,
- &ks_n_buffers , &ks_buffer_size) ;
- // store back possibly modified params
- Config->audio.removeListener(Config) ;
- Config->audio.setProperty(CONFIG::KS_NBLOCKS_ID , ks_n_buffers , nullptr) ;
- Config->audio.setProperty(CONFIG::KS_BLOCKSIZE_ID , ks_buffer_size , nullptr) ;
- Config->audio.addListener(Config) ;
- break ;
- }
- # endif // NO_SUPPORT_KS
- # ifndef NO_SUPPORT_DS
- case audioStreamer::WIN_AUDIO_DS:
- {
- GUID input_guid , output_guid ;
- audioStreamer::GetDsGuidByName(ds_input_device .toRawUTF8() , &input_guid) ;
- audioStreamer::GetDsGuidByName(ds_output_device.toRawUTF8() , &output_guid) ;
- Audio = audioStreamer::NewDS(OnSamples ,
- input_guid , output_guid ,
- ds_sample_rate , ds_bit_depth ,
- ds_n_buffers , ds_buffer_size) ;
- break ;
- }
- # endif // NO_SUPPORT_DS
- # ifndef NO_SUPPORT_WAVE
- case audioStreamer::WIN_AUDIO_WAVE:
- {
- Audio = audioStreamer::NewWAVE(OnSamples ,
- wave_input_device_n , wave_output_device_n ,
- wave_sample_rate , wave_bit_depth ,
- wave_n_buffers , wave_buffer_size ) ;
- break ;
- }
- # endif // NO_SUPPORT_WAVE
- default: break ;
- }
- #else // _WIN32
- # ifdef _MAC
- UNUSED(audio_api_n) ;
- std::string input_device = ca_input_device .toStdString() ;
- std::string output_device = ca_output_device.toStdString() ;
- Audio = audioStreamer::NewCA(OnSamples , input_device , output_device ,
- ca_n_channels , ca_sample_rate , ca_bit_depth ) ;
- Config->audio.setProperty(CONFIG::CA_DEVICE_ID , device_names , nullptr) ;
- DEBUG_TRACE_AUDIO_INIT_MAC
- # else // _MAC
- DEBUG_TRACE_AUDIO_INIT_NIX
- switch ((audioStreamer::NixApi)audio_api_n)
- {
- case audioStreamer::NIX_AUDIO_JACK:
- {
- UNUSED(jack_server_n) ;
- std::string jack_name = jack_client_name.toStdString() ;
- Audio = audioStreamer::NewJACK(OnSamples , Client ,
- jack_name , jack_n_inputs , jack_n_outputs) ;
- DEBUG_TRACE_AUDIO_INIT_JACK_FAIL
- if (Audio != nullptr) break ; // else fallback on ALSA
- }
- case audioStreamer::NIX_AUDIO_ALSA:
- {
- std::string input_device = alsa_input_device .toStdString() ;
- std::string output_device = alsa_output_device.toStdString() ;
- Audio = audioStreamer::NewALSA(OnSamples ,
- input_device , output_device ,
- alsa_n_channels ,
- alsa_sample_rate , alsa_bit_depth ,
- alsa_n_buffers , alsa_buffer_size) ;
- break ;
- }
- default: break ;
- }
- # endif // _MAC
- #endif // _WIN32
- DEBUG_TRACE_AUDIO_INIT
- bool isAudioEnabled = Audio != nullptr ;
- if (isAudioEnabled)
- {
- // kludge to sync loop progress to audible ticks
- // TODO: this may not be necessary at hipri update speed
- // probably just disable loop progress at lopri speed
- double update_ivl = double(Config->gui[CONFIG::UPDATE_IVL_ID]) ;
- GuiBeatOffset = Audio->getSampleRate() * (update_ivl * 0.002) ;
- // populate input source names arrays for ChannelConfig GUI
- int n_audio_sources = GetNumAudioSources() ;
- FreeAudioSources.clear() ; FreeAudioSourcePairs.clear() ;
- for (int source_n = 0 ; source_n < n_audio_sources ; ++source_n)
- {
- FreeAudioSources.add(source_n) ;
- if (source_n % 2) FreeAudioSourcePairs.add(source_n - 1) ;
- }
- // create master and stored local input channels
- if (!IsAudioInitialized) { ConfigureInitialChannels() ; IsAudioInitialized = true ; }
- }
- // set audio and status value holders for Config GUI
- Status = (isAudioEnabled) ? APP::LINJAM_STATUS_CONFIGPENDING :
- APP::LINJAM_STATUS_AUDIOERROR ;
- return isAudioEnabled ;
- }
- void LinJam::ConfigureInitialChannels()
- {
- if (IsAudioInitialized) return ;
- // add master and metro channel GUI mixers and configure NJClient master channels
- ValueTree master_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::MASTER_ID) ;
- ValueTree metro_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::METRO_ID) ;
- Gui->mixer->addChannel(GUI::MASTERS_GUI_ID , master_store) ;
- Gui->mixer->addChannel(GUI::MASTERS_GUI_ID , metro_store) ;
- ConfigureMasterChannel(CONFIG::CONFIG_INIT_ID) ;
- ConfigureMetroChannel( CONFIG::CONFIG_INIT_ID) ;
- DEBUG_TRACE_INITIAL_CHANNELS
- // add local Channel GUI mixers and configure NJClient input channels
- ValueTree channels = Config->localChannels ;
- for (int channel_n = 0 ; channel_n < channels.getNumChildren() ; ++channel_n)
- {
- ValueTree channel_store = Config->localChannels.getChild(channel_n) ;
- if (!AddLocalChannel(channel_store))
- {
- // destroy corrupted channel storage
- Config->removeChannel(Config->localChannels , channel_store) ;
- --channel_n ;
- }
- }
- }
- void LinJam::Shutdown()
- {
- // NJClient teardown
- JNL::close_socketlib() ;
- // LinJam teardown
- RoomSorter = nullptr ; delete Audio ; delete Config ;
- // Constants teardown
- // delete NETWORK::KNOWN_HOSTS ; delete NETWORK::KNOWN_BOTS ;
- DEBUG_TRACE_SHUTDOWN
- }
- /* NJClient callbacks */
- int LinJam::OnLicense(int user32 , char* license_text)
- {
- UNUSED(user32) ;
- if (!IsAgreed()) Gui->license->setLicenseText(CharPointer_UTF8(license_text)) ;
- DEBUG_TRACE_LICENSE
- return IsAgreed() ;
- }
- void LinJam::OnChatmsg(int user32 , NJClient* instance , const char** parms , int nparms)
- {
- UNUSED(user32) ; UNUSED(instance) ; UNUSED(nparms) ;
- if (!parms[0]) return ;
- String chat_type = String(CharPointer_UTF8(parms[CLIENT::CHATMSG_TYPE_IDX])) ;
- String chat_user = String(CharPointer_UTF8(parms[CLIENT::CHATMSG_USER_IDX]))
- .upToFirstOccurrenceOf(CONFIG::USER_IP_SPLIT_CHAR , false , false) ;
- String chat_text = String(CharPointer_UTF8(parms[CLIENT::CHATMSG_MSG_IDX])) ;
- bool is_topic_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_TOPIC)) ;
- bool is_bcast_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_MSG)) ;
- bool is_priv_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_PRIVMSG)) ;
- bool is_join_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_JOIN)) ;
- bool is_part_msg = (!chat_type.compare(CLIENT::CHATMSG_TYPE_PART)) ;
- DEBUG_TRACE_CHAT_IN
- if (is_topic_msg)
- {
- if (chat_text.isEmpty()) return ;
- Gui->chat->setTopic(chat_text) ;
- if (chat_user.isEmpty()) chat_text = GUI::TOPIC_TEXT + chat_text ;
- else chat_text = chat_user + GUI::SET_TOPIC_TEXT + chat_text ;
- chat_user = GUI::SERVER_NICK ;
- }
- else if (is_bcast_msg)
- {
- if (chat_text.isEmpty()) return ;
- if (chat_user.isEmpty()) chat_user = GUI::SERVER_NICK ;
- else if (chat_text.startsWith(CLIENT::CHATMSG_CMD_VOTE))
- {
- // customize voting messages
- StringArray tokens = StringArray::fromTokens(StringRef(chat_text) , false) ;
- String bpi_bpm_cmd = tokens[1] ;
- String bpi_bpm_val = tokens[2] ;
- bool is_bpi_msg = !bpi_bpm_cmd.compare(CLIENT::CHATMSG_CMD_BPI.substring(1).trim()) ;
- bool is_bpm_msg = !bpi_bpm_cmd.compare(CLIENT::CHATMSG_CMD_BPM.substring(1).trim()) ;
- if ((is_bpi_msg || is_bpm_msg) && bpi_bpm_val.containsOnly(NETWORK::DIGITS))
- {
- chat_text = chat_user + " votes to set " + bpi_bpm_cmd + " to " + bpi_bpm_val ;
- chat_user = GUI::SERVER_NICK ;
- }
- }
- }
- else if (is_priv_msg)
- {
- if (chat_user.isEmpty() || chat_text.isEmpty()) return ;
- chat_user += GUI::PM_TEXT ;
- }
- else if (is_join_msg || is_part_msg)
- {
- if (chat_user.isEmpty()) return ;
- chat_text = chat_user + GUI::JOINPART_TEXTa +
- ((is_join_msg)? GUI::JOIN_TEXT : GUI::PART_TEXT) + GUI::JOINPART_TEXTb ;
- chat_user = GUI::SERVER_NICK ;
- }
- Gui->chat->addChatLine(chat_user , chat_text) ;
- }
- void LinJam::OnSamples(float** input_buffer , int n_input_channels ,
- float** output_buffer , int n_output_channels ,
- int n_samples , int sample_rate )
- {
- if (Audio == nullptr)
- {
- // clear all output buffers
- size_t n_bytes = n_samples * sizeof(float) ;
- for (int channel_n = 0 ; channel_n < n_output_channels ; ++channel_n)
- memset(output_buffer[channel_n] , 0 , n_bytes) ;
- }
- else Client->AudioProc(input_buffer , n_input_channels ,
- output_buffer , n_output_channels ,
- n_samples , sample_rate ) ;
- }
- /* NJClient runtime routines */
- void LinJam::HandleTimer(int timer_id)
- {
- #ifdef DEBUG_EXIT_IMMEDIATELY
- DBG("[DEBUG]: DEBUG_EXIT_IMMEDIATELY defined - bailing") ; Client->quit() ;
- #endif // DEBUG_EXIT_IMMEDIATELY
- switch (timer_id)
- {
- case APP::CLIENT_TIMER_ID: PumpClient() ; break ;
- case APP::GUI_LO_TIMER_ID: UpdateGuiLowPriority() ; break ;
- // case APP::GUI_MD_TIMER_ID: /* unused */ break ;
- case APP::GUI_HI_TIMER_ID: UpdateGuiHighPriority() ; break ;
- case APP::AUDIO_INIT_TIMER_ID: InitializeAudio() ; break ;
- default: break ;
- }
- if (timer_id == APP::AUDIO_INIT_TIMER_ID) Timer->stopTimer(timer_id) ;
- }
- void LinJam::PumpClient()
- {
- UpdateStatus() ;
- if (Client->HasUserInfoChanged() ) HandleUserInfoChanged() ;
- if (Client->GetStatus() >= APP::NJC_STATUS_OK) while (!Client->Run()) ;
- }
- void LinJam::UpdateStatus()
- {
- // update status if not in an init, error, or hold state
- int status = int(Status.getValue()) ;
- bool is_ready = status >= APP::LINJAM_STATUS_READY ;
- if (is_ready) status = Client->GetStatus() ;
- String error_msg = CharPointer_UTF8(Client->GetErrorStr()) ;
- bool is_licence_pending = status == APP::NJC_STATUS_INVALIDAUTH && !IsAgreed() ;
- bool is_room_full = is_ready && !error_msg.compare(CLIENT::SERVER_FULL_ERROR) ;
- if (is_licence_pending) status = APP::LINJAM_STATUS_LICENSEPENDING ;
- else if (is_room_full ) status = APP::LINJAM_STATUS_ROOMFULL ;
- Status = status ;
- }
- void LinJam::HandleStatusChanged()
- {
- DEBUG_TRACE_STATUS_CHANGED
- APP::LinJamStatus status = (APP::LinJamStatus)int(Status.getValue()) ;
- // ignore sentinel value
- if (status == APP::LINJAM_STATUS_READY) return ;
- // set status indicator
- String host = Client->GetHostName() ;
- String status_text =
- (status == APP::LINJAM_STATUS_AUDIOERROR ) ? GUI::AUDIO_INIT_ERROR_MSG :
- (status == APP::LINJAM_STATUS_AUDIOINIT ) ? GUI::AUDIO_INIT_MSG :
- (status == APP::LINJAM_STATUS_CONFIGPENDING ) ? GUI::CONFIG_PENDING_MSG :
- (status == APP::LINJAM_STATUS_LICENSEPENDING) ? GUI::LICENSE_PENDING_TEXT :
- (status == APP::LINJAM_STATUS_ROOMFULL ) ? GUI::ROOM_FULL_TEXT :
- (status == APP::NJC_STATUS_DISCONNECTED ) ? GUI::DISCONNECTED_TEXT :
- (status == APP::NJC_STATUS_INVALIDAUTH ) ? GUI::INVALID_AUTH_TEXT :
- (status == APP::NJC_STATUS_CANTCONNECT ) ? GUI::FAILED_CONNECTION_TEXT :
- (status == APP::NJC_STATUS_OK ) ? GUI::CONNECTED_TEXT + host :
- (status == APP::NJC_STATUS_PRECONNECT ) ? GUI::IDLE_TEXT :
- Status.toString() ;
- Gui->statusbar->setStatusL(status_text) ;
- // set front-most GUI container
- Component* top_component =
- (status == APP::LINJAM_STATUS_AUDIOERROR ) ? Gui->config :
- (status == APP::LINJAM_STATUS_AUDIOINIT ) ? Gui->config :
- (status == APP::LINJAM_STATUS_CONFIGPENDING ) ? Gui->config :
- (status == APP::LINJAM_STATUS_LICENSEPENDING) ? Gui->license :
- (status == APP::LINJAM_STATUS_ROOMFULL ) ? Gui->login :
- (status == APP::NJC_STATUS_DISCONNECTED ) ? Gui->login :
- (status == APP::NJC_STATUS_INVALIDAUTH ) ? Gui->login :
- (status == APP::NJC_STATUS_CANTCONNECT ) ? Gui->login :
- (status == APP::NJC_STATUS_OK ) ? Gui->chat :
- (status == APP::NJC_STATUS_PRECONNECT ) ? Gui->login :
- (Component*)Gui->background ;
- top_component ->toFront(true) ;
- Gui->background->toBehind(top_component) ;
- // responses
- switch (status)
- {
- // retry login
- case APP::NJC_STATUS_INVALIDAUTH:
- case APP::NJC_STATUS_CANTCONNECT: // retry login (server occasionally rejects)
- if (RetryLogin-- > 0) Connect() ; break ;
- case APP::NJC_STATUS_OK: // store server credentials and present mixer GUI
- Config->storeServer() ;
- UpdateGuiLowPriority() ;
- Gui->mixer->toFront(false) ;
- Gui->loop ->toFront(false) ; break ;
- case APP::NJC_STATUS_PRECONNECT: // auto-join
- if (AutoJoinHost.isNotEmpty() && Gui->login->quickLogin(AutoJoinHost))
- Gui->background->toFront(true) ;
- AutoJoinHost = "" ; break ;
- default: break ;
- }
- }
- void LinJam::HandleUserInfoChanged()
- {
- #ifdef NO_UPDATE_REMOTES
- return ;
- #endif // NO_UPDATE_REMOTES
- Identifier host = LinJamConfig::MakeHostId(str(Config->server[CONFIG::HOST_ID])) ;
- DEBUG_TRACE_REMOTE_CHANNELS_VB
- // initialize dictionary for pruning parted users GUI elements
- ValueTree active_users = ValueTree("active-users") ;
- // fetch remote user states from server
- int user_idx = -1 ; String user_name ;
- while ((user_name = GetRemoteUserName(++user_idx)).isNotEmpty())
- {
- Identifier user_id = Config->MakeUserId(user_name) ;
- std::string nick = (user_name = STRING(user_id)).toStdString() ;
- bool is_ignored = !!Client->config_autosubscribe_userlist.count(nick) ;
- bool is_bot = str(NETWORK::KNOWN_BOTS.getProperty(host , "")) == user_name ;
- // cache bot user_idx for recording time updates
- if (is_bot) Config->server.setProperty(CONFIG::BOT_USERIDX_ID , user_idx , nullptr) ;
- // TODO: we may be able to bail now without storing bot userdata (issue #64)
- // get or create remote user storage
- ValueTree user_store = Config->getOrAddRemoteUser(user_name) ;
- if (!user_store.isValid()) continue ;
- // update stored remote user state
- Config->updateRemoteUserState(user_store , user_idx , !is_ignored) ;
- if (is_ignored) continue ;
- // create remote user GUI
- if (Gui->mixer->addRemoteUser(user_store))
- {
- // create remote master channel storage
- ValueTree master_store = Config->getOrAddRemoteChannel(user_id , CONFIG::MASTER_KEY) ;
- if (!master_store.isValid()) continue ;
- // create remote master channel GUI and restore stored NJClient user state
- if (Gui->mixer->addChannel(user_id , master_store))
- ConfigureRemoteChannel(user_store , master_store , CONFIG::CONFIG_INIT_ID) ;
- }
- // initialize array for pruning removed channels GUI elements
- Array<var> active_channels = Array<var>() ;
- int channel_n = -1 ; int channel_idx ;
- while (~(channel_idx = Client->EnumUserChannels(user_idx , ++channel_n)))
- {
- // get or create remote channel storage
- String channel_name = GetRemoteChannelClientName(user_idx , channel_idx) ;
- ValueTree channel_store = Config->getOrAddRemoteChannel(user_id , channel_name ,
- channel_idx ) ;
- String stored_name = GetStoredChannelName(channel_store) ;
- if (!channel_store.isValid()) continue ;
- // rename existing channel (and GUI asynchronously)
- if (channel_name.compare(stored_name))
- channel_store.setProperty(CONFIG::CHANNEL_NAME_ID , channel_name , nullptr) ;
- // update faux-stereo status (configures NJClient and GUI asynchronously)
- int stereo_status = Config->setRemoteStereo(user_store , channel_store , stored_name) ;
- // create remote channel GUI and restore stored NJClient remote channel state
- if (Gui->mixer->addChannel(user_id , channel_store))
- ConfigureRemoteChannel(user_store , channel_store , CONFIG::CONFIG_INIT_ID) ;
- // add channel to GUI prune list unless hidden stereo pair channel
- if (stereo_status != CONFIG::STEREO_R)
- active_channels.add(var(STRING(channel_store.getType()))) ;
- }
- // add user to GUI prune list
- active_users.setProperty(user_id , active_channels , nullptr) ;
- }
- // prune user and channel GUIs
- Gui->mixer->pruneRemotes(active_users) ;
- }
- void LinJam::UpdateGuiHighPriority() { UpdateLoopProgress() ; UpdateVuMeters() ; }
- void LinJam::UpdateGuiLowPriority() { UpdateRooms() ; UpdateRecordingTime() ; }
- void LinJam::UpdateLoopProgress()
- {
- #ifdef NO_UPDATE_LOOP_PROGRESS_GUI
- return ;
- #endif // NO_UPDATE_LOOP_PROGRESS_GUI
- // set loop progress to strobing effect when idle
- if (Status != APP::NJC_STATUS_OK) { Gui->loop->loopProgress = 1.0 ; return ; }
- // compute loop progress
- int sample_n , n_samples ; Client->GetPosition(&sample_n , &n_samples) ;
- int bpi = Client->GetBPI() ;
- float bpm = Client->GetActualBPM() ;
- double linear_progress = (sample_n + GuiBeatOffset) / n_samples ;
- int beat_n = ((int)(bpi * linear_progress) % bpi) + 1 ;
- double discrete_progress = (float)beat_n / bpi ;
- // update statusbar loop progress
- Gui->loop->updateBeatN(beat_n) ;
- Gui->loop->loopProgress = discrete_progress ; // linear_progress ;
- Gui->statusbar->setStatusR(String(bpi) + " bpi @ " + String(bpm) + " bpm") ;
- // sompute metro VU loop progress
- ValueTree metro_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::METRO_ID) ;
- double metro_is_muted = bool( metro_store[CONFIG::IS_MUTED_ID]) ;
- double metro_pan = double(metro_store[CONFIG::PAN_ID]) ;
- double metro_vu = (metro_is_muted) ? 0.0 : discrete_progress ;
- double metro_vu_l = (metro_pan < 0.0) ? metro_vu : metro_vu * (1.0 - metro_pan) ;
- double metro_vu_r = (metro_pan > 0.0) ? metro_vu : metro_vu * (1.0 + metro_pan) ;
- // update metro VU loop progress
- metro_store.setProperty(CONFIG::VU_LEFT_ID , metro_vu_l , nullptr) ;
- metro_store.setProperty(CONFIG::VU_RIGHT_ID , metro_vu_r , nullptr) ;
- }
- void LinJam::UpdateVuMeters()
- {
- #ifdef NO_UPDATE_VU_METERS_GUI
- return ;
- #endif // NO_UPDATE_VU_METERS_GUI
- double master_vu_l = 0.0 ; double master_vu_r = 0.0 ;
- /* update local VUs */
- int channel_n = -1 ; int channel_idx ;
- while (~(channel_idx = Client->EnumLocalChannels(++channel_n)))
- {
- ValueTree channel_store = Config->getChannelByIdx(Config->localChannels , channel_idx) ;
- double local_pan = double(channel_store[CONFIG::PAN_ID]) ;
- double local_vu_l = GetChannelDb(channel_idx) - GUI::VU_DB_MIN ;
- int stereo_status = int(channel_store[CONFIG::STEREO_ID]) ;
- if (!channel_store.isValid()) continue ;
- if (stereo_status == CONFIG::MONO)
- {
- channel_store.setProperty(CONFIG::VU_LEFT_ID , local_vu_l , nullptr) ;
- // compensate master VUs for local channel pan
- ScalePannedMonoVus(local_vu_l , local_pan , &master_vu_l , &master_vu_r) ; // mutates
- }
- else if (stereo_status == CONFIG::STEREO_L)
- {
- // ensure this faux-stereo channel has a matching faux-stereo pair channel
- int pair_idx = int(channel_store[CONFIG::PAIR_IDX_ID]) ;
- double local_vu_r = GetChannelDb(pair_idx) - GUI::VU_DB_MIN ;
- // compensate faux-stereo VUs for faux-stereo pan
- ComputePannedVus(local_pan , &local_vu_l , &local_vu_r) ; // mutates
- // update local channel VUs asynchronously
- channel_store.setProperty(CONFIG::VU_LEFT_ID , local_vu_l , nullptr) ;
- master_vu_l = AddDecibels(master_vu_l , local_vu_l) ;
- channel_store.setProperty(CONFIG::VU_RIGHT_ID , local_vu_r , nullptr) ;
- master_vu_r = AddDecibels(master_vu_r , local_vu_r) ;
- }
- }
- /* update remote VUs */
- int user_idx = -1 ; String user_name ;
- while ((user_name = GetRemoteUserName(++user_idx)).isNotEmpty())
- {
- Identifier user_id = Config->MakeUserId(user_name) ;
- ValueTree user_store = Config->getUserById(user_id) ;
- double remote_master_vu_l = 0.0 ;
- double remote_master_vu_r = 0.0 ; channel_n = -1 ; Identifier vu_id ;
- while (~(channel_idx = Client->EnumUserChannels(user_idx , ++channel_n)))
- {
- ValueTree channel_store = Config->getChannelByIdx(user_store , channel_idx) ;
- double remote_pan = double(channel_store[CONFIG::PAN_ID]) ;
- double remote_vu_l = GetChannelDb(user_idx , channel_idx) - GUI::VU_DB_MIN ;
- int stereo_status = int(channel_store[CONFIG::STEREO_ID]) ;
- if (!channel_store.isValid()) continue ;
- if (stereo_status == CONFIG::MONO)
- {
- channel_store.setProperty(CONFIG::VU_LEFT_ID , remote_vu_l , nullptr) ;
- // compensate remote master VUs for remote channel pan
- ScalePannedMonoVus( remote_vu_l , remote_pan ,
- &remote_master_vu_l , &remote_master_vu_r) ; // mutates
- }
- else if (stereo_status == CONFIG::STEREO_L)
- {
- // ensure this faux-stereo channel has a matching faux-stereo pair channel
- int pair_idx = int(channel_store[CONFIG::PAIR_IDX_ID]) ;
- double remote_vu_r = GetChannelDb(user_idx , pair_idx) - GUI::VU_DB_MIN ;
- // compensate faux-stereo VUs for faux-stereo pan
- ComputePannedVus(remote_pan , &remote_vu_l , &remote_vu_r) ; // mutates
- // update remote channel VUs asynchronously
- channel_store.setProperty(CONFIG::VU_LEFT_ID , remote_vu_l , nullptr) ;
- remote_master_vu_l = AddDecibels(remote_master_vu_l , remote_vu_l) ;
- channel_store.setProperty(CONFIG::VU_RIGHT_ID , remote_vu_r , nullptr) ;
- remote_master_vu_r = AddDecibels(remote_master_vu_r , remote_vu_r) ;
- }
- }
- // update remote master VUs asynchronously
- ValueTree remote_master_store = Config->getChannelById(user_id , CONFIG::MASTER_ID) ;
- double remote_master_pan = double(remote_master_store[CONFIG::PAN_ID]) ;
- if (!remote_master_store.isValid()) continue ;
- // compensate remote master VUs for remote master pan
- if (remote_master_pan > 0.0)
- ScalePannedMonoVus( remote_master_vu_l , remote_master_pan ,
- &remote_master_vu_l , &remote_master_vu_r) ; // mutates
- if (remote_master_pan < 0.0)
- ScalePannedMonoVus( remote_master_vu_r , remote_master_pan ,
- &remote_master_vu_l , &remote_master_vu_r) ; // mutates
- remote_master_store.setProperty(CONFIG::VU_LEFT_ID , remote_master_vu_l , nullptr) ;
- remote_master_store.setProperty(CONFIG::VU_RIGHT_ID , remote_master_vu_r , nullptr) ;
- master_vu_l = AddDecibels(master_vu_l , remote_master_vu_l) ;
- master_vu_r = AddDecibels(master_vu_r , remote_master_vu_r) ;
- }
- /* update master VU */
- ValueTree master_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::MASTER_ID) ;
- double master_pan = double(master_store[CONFIG::PAN_ID]) ;
- // compensate master VUs for master pan
- if (master_pan > 0.0)
- ScalePannedMonoVus(master_vu_l , master_pan , &master_vu_l , &master_vu_r) ; // mutates
- if (master_pan < 0.0)
- ScalePannedMonoVus(master_vu_r , master_pan , &master_vu_l , &master_vu_r) ; // mutates
- master_store.setProperty(CONFIG::VU_LEFT_ID , master_vu_l , nullptr) ;
- master_store.setProperty(CONFIG::VU_RIGHT_ID , master_vu_r , nullptr) ;
- }
- void LinJam::UpdateRooms()
- {
- #ifdef NO_UPDATE_ROOMS_GUI
- return ;
- #endif // NO_UPDATE_ROOMS_GUI
- SetPollUrl() ; // TODO: shuld be done elsewhere on some state changes
- String response = PollUrl.readEntireTextStream() ;
- StringArray rooms = APP::ParseLines(response) ;
- int userdata_idx = (Status == APP::NJC_STATUS_OK) ? rooms.size() - 1 : -1 ;
- String userdata = APP::Pluck(&rooms , userdata_idx) ;
- DEBUG_UPDATE_ROOMS_RESP
- DEBUG_UPDATE_ROOMS_USERDATA
- for (int room_n = 0 ; room_n < rooms.size() ; ++room_n)
- {
- StringArray nicks = APP::ParseCSV(rooms[room_n]) ;
- String host = APP::Pluck(&nicks , 0) ;
- ValueTree clients = ValueTree(CONFIG::CLIENTS_ID) ;
- nicks.trim() ; nicks.removeEmptyStrings() ;
- DEBUG_UPDATE_ROOMS_ROOMDATA
- while (nicks.size() > 0)
- {
- String nick = APP::Pluck(&nicks , 0) ;
- ValueTree nick_store = ValueTree(Config->MakeUserId(nick)) ;
- nick_store.setProperty(CONFIG::LOGIN_ID , var(nick) , nullptr) ;
- clients.addChild(nick_store , -1 , nullptr) ;
- }
- ValueTree clients_store = Config->getServer(host).getChildWithName(CONFIG::CLIENTS_ID) ;
- for (int client_n = 0 ; client_n < clients_store.getNumChildren() ; ++client_n)
- {
- ValueTree client_store = clients_store.getChild(client_n) ;
- if (!clients.getChildWithName(client_store.getType()).isValid())
- clients_store.removeChild(client_store , nullptr) ;
- }
- while (clients.getNumChildren() > 0)
- {
- ValueTree client = clients.getChild(0) ; clients.removeChild(client , nullptr) ;
- if (!clients_store.getChildWithName(client.getType()).isValid())
- clients_store.addChild(client , -1 , nullptr) ;
- }
- }
- // sort rooms by occupancy
- Config->servers.sort(*RoomSorter , nullptr , true) ;
- }
- void LinJam::UpdateRecordingTime()
- {
- #ifdef NO_UPDATE_RECORDING_TIME_GUI
- return ;
- #endif // NO_UPDATE_RECORDING_TIME_GUI
- if (Status != APP::NJC_STATUS_OK) return ;
- // NOTE: parsing recording time is somewhat brittle - (issue #64)
- // dependent on constants such as NETWORK::KNOWN_BOTS and CLIENT::BOT_CHANNELIDX
- // though these values are more conventional than canonical
- int bot_useridx = Config->server[CONFIG::BOT_USERIDX_ID] ;
- String host = String(Client->GetHostName()) ;
- String bpi = String(Client->GetBPI()) ;
- String bpm = String((int)Client->GetActualBPM()) ;
- String recording_time = String() ;
- if (~bot_useridx)
- {
- recording_time = GetRemoteChannelClientName(bot_useridx , CLIENT::BOT_CHANNELIDX) ;
- bool has_recording_time_changed = !!recording_time.compare(PrevRecordingTime) ;
- bool is_this_first_pass = PrevRecordingTime.isEmpty() ;
- bool should_show_time = (has_recording_time_changed && !is_this_first_pass) ;
- PrevRecordingTime = recording_time ;
- recording_time = (should_show_time)? " - " + recording_time : String() ;
- }
- Gui->setTitle(host + " - " + bpi + "bpi / " + bpm + "bpm" + recording_time) ;
- }
- /* NJClient configuration */
- void LinJam::ConfigureAudio()
- {
- // defer initialization of audio devices to accommodate flooding
- // e.g. per ConfigAudio::restoreDefults()
- Status = APP::LINJAM_STATUS_AUDIOINIT ;
- Timer->startTimer(APP::AUDIO_INIT_TIMER_ID , APP::AUDIO_INIT_DELAY) ;
- }
- void LinJam::ConfigureMasterChannel(Identifier a_key)
- {
- ValueTree master_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::MASTER_ID) ;
- if (!master_store.isValid()) return ;
- // load stored config for this channel
- float volume = float(master_store[CONFIG::VOLUME_ID ]) ;
- float pan = float(master_store[CONFIG::PAN_ID ]) ;
- bool is_muted = bool( master_store[CONFIG::IS_MUTED_ID]) ;
- // determine which NJClient channel params to modify
- bool should_init_all = (a_key == CONFIG::CONFIG_INIT_ID ) ;
- bool should_set_volume = (a_key == CONFIG::VOLUME_ID || should_init_all) ;
- bool should_set_pan = (a_key == CONFIG::PAN_ID || should_init_all) ;
- bool should_set_is_muted = (a_key == CONFIG::IS_MUTED_ID || should_init_all) ;
- // configure NJClient master channel
- if (should_set_volume ) Client->config_mastervolume = (float)DB2VAL(volume) ;
- if (should_set_pan ) Client->config_masterpan = pan ;
- if (should_set_is_muted) Client->config_mastermute = is_muted ;
- }
- void LinJam::ConfigureMetroChannel(Identifier a_key)
- {
- ValueTree metro_store = Config->getChannelById(CONFIG::MASTERS_ID , CONFIG::METRO_ID) ;
- if (!metro_store.isValid()) return ;
- // load stored config for this channel
- float volume = float(metro_store[CONFIG::VOLUME_ID ]) ;
- float pan = float(metro_store[CONFIG::PAN_ID ]) ;
- bool is_muted = bool( metro_store[CONFIG::IS_MUTED_ID]) ;
- int source_n = int( metro_store[CONFIG::SOURCE_N_ID]) ;
- bool is_stereo = int( metro_store[CONFIG::STEREO_ID ]) != CONFIG::MONO ;
- // determine which NJClient channel params to modify
- bool should_init_all = (a_key == CONFIG::CONFIG_INIT_ID ) ;
- bool should_set_volume = (a_key == CONFIG::VOLUME_ID || should_init_all) ;
- bool should_set_pan = (a_key == CONFIG::PAN_ID || should_init_all) ;
- bool should_set_is_muted = (a_key == CONFIG::IS_MUTED_ID || should_init_all) ;
- bool should_set_source_n = (a_key == CONFIG::SOURCE_N_ID || should_init_all) ;
- bool should_set_stereo = (a_key == CONFIG::STEREO_ID || should_init_all) ;
- // configure NJClient metro channel
- if (should_set_volume ) Client->config_metronome = (float)DB2VAL(volume) ;
- if (should_set_pan ) Client->config_metronome_pan = pan ;
- if (should_set_is_muted) Client->config_metronome_mute = is_muted ;
- if (should_set_source_n) Client->config_metronome_channel = source_n ;
- if (should_set_stereo ) Client->config_metronome_stereoout = is_stereo ;
- }
- void LinJam::ConfigureLocalChannel(ValueTree channel_store , Identifier a_key)
- {
- // filter uninteresting config params
- if (!channel_store.isValid() ||
- a_key == CONFIG::CHANNEL_IDX_ID || a_key == CONFIG::PAIR_IDX_ID ||
- a_key == CONFIG::VU_LEFT_ID || a_key == CONFIG::VU_RIGHT_ID ) return ;
- // determine which NJClient channel params to modify
- bool should_init_all = (a_key == CONFIG::CONFIG_INIT_ID ) ;
- bool should_set_name = (a_key == CONFIG::CHANNEL_NAME_ID || should_init_all) ;
- bool should_set_volume = (a_key == CONFIG::VOLUME_ID || should_init_all) ;
- bool should_set_pan = (a_key == CONFIG::PAN_ID ||
- a_key == CONFIG::STEREO_ID || should_init_all) ;
- bool should_set_is_xmit = (a_key == CONFIG::IS_XMIT_RCV_ID || should_init_all) ;
- bool should_set_is_muted = (a_key == CONFIG::IS_MUTED_ID || should_init_all) ;
- bool should_set_is_solo = (a_key == CONFIG::IS_SOLO_ID || should_init_all) ;
- bool should_set_source_n = (a_key == CONFIG::SOURCE_N_ID || should_init_all) ;
- bool should_set_bit_depth = (a_key == CONFIG::BIT_DEPTH_ID || should_init_all) ;
- bool should_set_is_stereo = (a_key == CONFIG::STEREO_ID || should_init_all) ;
- // load stored config for this channel
- String channel_name = str( channel_store[CONFIG::CHANNEL_NAME_ID]) ;
- int channel_idx = int( channel_store[CONFIG::CHANNEL_IDX_ID ]) ;
- int pair_idx = int( channel_store[CONFIG::PAIR_IDX_ID ]) ;
- float volume = float(channel_store[CONFIG::VOLUME_ID ]) ;
- float pan = float(channel_store[CONFIG::PAN_ID ]) ;
- bool is_xmit = bool( channel_store[CONFIG::IS_XMIT_RCV_ID ]) ;
- bool is_muted = bool( channel_store[CONFIG::IS_MUTED_ID ]) ;
- bool is_solo = bool( channel_store[CONFIG::IS_SOLO_ID ]) ;
- int source_n = int( channel_store[CONFIG::SOURCE_N_ID ]) ;
- int bit_depth = int( channel_store[CONFIG::BIT_DEPTH_ID ]) ;
- int stereo_status = int( channel_store[CONFIG::STEREO_ID ]) ;
- channel_name = Config->MakeStereoName(channel_name , stereo_status) ;
- DEBUG_TRACE_CONFIGURE_LOCAL_CHANNEL
- // handle channel name change
- const char* new_name = (should_set_name)? channel_name.toRawUTF8() : nullptr ;
- // handle faux-stereo panning
- if (should_set_pan) pan = ClientPan(pan , stereo_status) ;
- // configure NJClient local channel
- if (should_set_name || should_set_source_n || should_set_bit_depth || should_set_is_xmit)
- Client->SetLocalChannelInfo(channel_idx , new_name ,
- should_set_source_n , source_n ,
- should_set_bit_depth , bit_depth ,
- should_set_is_xmit , is_xmit ) ;
- if (should_set_volume || should_set_pan || should_set_is_muted || should_set_is_solo)
- Client->SetLocalChannelMonitoring(channel_idx ,
- should_set_volume , (float)DB2VAL(volume) ,
- should_set_pan , pan ,
- should_set_is_muted , is_muted ,
- should_set_is_solo , is_solo ) ;
- // configure faux-stereo pair implicitly
- if (stereo_status == CONFIG::STEREO_L)
- {
- if (should_set_is_stereo)
- {
- // handle mono->stereo conversion
- if (~(pair_idx = GetVacantLocalChannelIdx()))
- {
- a_key = CONFIG::CONFIG_INIT_ID ;
- channel_store.setProperty(CONFIG::PAIR_IDX_ID , pair_idx , nullptr) ;
- }
- else
- {
- channel_store.setProperty(CONFIG::STEREO_ID , CONFIG::MONO , nullptr) ;
- return ;
- }
- }
- // force mono conversion bypassing configuration of pair to be deleted (fires next case)
- if (a_key == CONFIG::SOURCE_N_ID && source_n % 2)
- channel_store.setProperty(CONFIG::STEREO_ID , CONFIG::MONO , nullptr) ;
- // configure unstored pair channel
- else
- {
- ValueTree pair_store = channel_store.createCopy() ;
- String pair_name = Config->MakeStereoName(channel_name , CONFIG::STEREO_R) ;
- int pair_source_n = source_n + 1 ;
- pair_store.setProperty(CONFIG::CHANNEL_NAME_ID , pair_name , nullptr) ;
- pair_store.setProperty(CONFIG::CHANNEL_IDX_ID , pair_idx , nullptr) ;
- pair_store.setProperty(CONFIG::SOURCE_N_ID , pair_source_n , nullptr) ;
- pair_store.setProperty(CONFIG::STEREO_ID , CONFIG::STEREO_R , nullptr) ;
- ConfigureLocalChannel(pair_store , a_key) ;
- }
- // update client channels names on mono->stereo conversion
- if (should_set_is_stereo)
- channel_store.setProperty(CONFIG::CHANNEL_NAME_ID , channel_name , nullptr) ;
- }
- // handle stereo->mono conversion
- else if (stereo_status == CONFIG::MONO && should_set_is_stereo)
- {
- Client->DeleteLocalChannel(pair_idx) ;
- channel_store.setProperty(CONFIG::PAIR_IDX_ID , CONFIG::DEFAULT_CHANNEL_IDX , nullptr) ;
- channel_store.setProperty(CONFIG::CHANNEL_NAME_ID , channel_name , nullptr) ;
- }
- Client->NotifyServerOfChannelChange() ;
- }
- void LinJam::ConfigureRemoteChannel(ValueTree user_store , ValueTree channel_store ,
- Identifier a_key )
- {
- // filter uninteresting config params
- if (!user_store.isValid() || !channel_store.isValid() ||
- a_key == CONFIG::CHANNEL_NAME_ID ||
- a_key == CONFIG::CHANNEL_IDX_ID || a_key == CONFIG::PAIR_IDX_ID ||
- a_key == CONFIG::VU_LEFT_ID || a_key == CONFIG::VU_RIGHT_ID ) return ;
- // determine which NJClient channel params to modify
- bool should_init_all = (a_key == CONFIG::CONFIG_INIT_ID ) ;
- bool should_set_volume = (a_key == CONFIG::VOLUME_ID || should_init_all) ;
- bool should_set_pan = (a_key == CONFIG::PAN_ID ||
- a_key == CONFIG::STEREO_ID || should_init_all) ;
- bool should_set_is_rcv = (a_key == CONFIG::IS_XMIT_RCV_ID || should_init_all) ;
- bool should_set_is_muted = (a_key == CONFIG::IS_MUTED_ID || should_init_all) ;
- bool should_set_is_solo = (a_key == CONFIG::IS_SOLO_ID || should_init_all) ;
- bool should_set_is_stereo = (a_key == CONFIG::STEREO_ID || should_init_all) ;
- // load stored config for this channel
- int user_idx = int( user_store [CONFIG::USER_IDX_ID ]) ;
- int channel_idx = int( channel_store[CONFIG::CHANNEL_IDX_ID]) ;
- int pair_idx = int( channel_store[CONFIG::PAIR_IDX_ID ]) ;
- float volume = float(channel_store[CONFIG::VOLUME_ID ]) ;
- float pan = float(channel_store[CONFIG::PAN_ID ]) ;
- bool is_rcv = bool( channel_store[CONFIG::IS_XMIT_RCV_ID]) ;
- bool is_muted = bool( channel_store[CONFIG::IS_MUTED_ID ]) ;
- bool is_solo = bool( channel_store[CONFIG::IS_SOLO_ID ]) ;
- int sink_n = 0 ; // TODO: not yet clear how to handle remote sink_n
- int stereo_status = int( channel_store[CONFIG::STEREO_ID ]) ; \
- bool is_pannable = true ;
- DEBUG_TRACE_CONFIGURE_REMOTE_CHANNEL
- if (channel_idx == CONFIG::MASTER_CHANNEL_IDX)
- {
- int channel_n = -1 ;
- // configure NJClient remote master channel
- if (should_set_volume || should_set_pan || should_set_is_muted)
- Client->SetUserState(user_idx ,
- should_set_volume , (float)DB2VAL(volume) ,
- should_set_pan , pan ,
- should_set_is_muted , is_muted ) ;
- // or apply user master pseudo rcv or solo control over all real user channels
- else if (should_set_is_rcv || should_set_is_solo)
- while (~(channel_idx = Client->EnumUserChannels(user_idx , ++channel_n)))
- {
- channel_store = Config->getChannelByIdx(user_store , channel_idx) ;
- ConfigureRemoteChannel(user_store , channel_store , a_key) ;
- }
- }
- else
- {
- // handle faux-stereo panning
- if (should_set_pan) pan = ClientPan(pan , stereo_status) ;
- // configure NJClient remote channel allowing master overrides
- ValueTree master_store = Config->getUserMasterChannel(user_store) ;
- bool is_master_rcv = bool(master_store[CONFIG::IS_XMIT_RCV_ID]) ;
- bool is_master_solo = bool(master_store[CONFIG::IS_SOLO_ID ]) ;
- Client->SetUserChannelState(user_idx , channel_idx ,
- should_set_is_rcv , is_rcv && is_master_rcv ,
- should_set_volume , (float)DB2VAL(volume) ,
- should_set_pan , pan ,
- should_set_is_muted , is_muted ,
- should_set_is_solo , is_solo || is_master_solo ,
- should_init_all , sink_n ,
- should_init_all , is_pannable ) ;
- // configure faux-stereo pair implicitly
- if (stereo_status == CONFIG::STEREO_L)
- {
- ValueTree pair_store = Config->getChannelByIdx(user_store , pair_idx) ;
- if (should_set_is_stereo || should_init_all)
- ConfigureRemoteChannel(user_store , pair_store , CONFIG::CONFIG_INIT_ID) ;
- else pair_store.setProperty(a_key , channel_store[a_key] , nullptr) ;
- }
- }
- }
- /* audio signal helpers */
- double LinJam::AddDecibels(double vu_l , double vu_r)
- {
- return 10 * log10(pow(10.0 , (vu_l / 10.0)) + pow(10.0 , (vu_r / 10.0))) ;
- }
- void LinJam::ComputePannedVus(double pan , double* vu_l , double* vu_r)
- {
- double vu_l_in = *vu_l ;
- double vu_r_in = *vu_r ;
- // scale master VU meters per master pan
- if (pan > 0.0) // pan right
- {
- // decrease left contribution to left vu
- *vu_l *= log10(10.0 * (1.0 - pan)) ;
- // increase left contribution to right vu
- *vu_r = AddDecibels(vu_r_in , vu_l_in * pan) ;
- }
- if (pan < 0.0) // pan left
- {
- // increase right contribution to left VU
- *vu_l = AddDecibels(vu_l_in , vu_r_in * -pan) ;
- // decrease right contribution to right VU
- *vu_r *= log10(10.0 * (1.0 + pan)) ;
- }
- // cap underflows
- if (isnan(*vu_l)) *vu_l = 0.0 ;
- if (isnan(*vu_r)) *vu_r = 0.0 ;
- if (vu_l_in == 0.0 && vu_r_in == 0.0) *vu_l = *vu_r = 0.0 ;
- }
- void LinJam::ScalePannedMonoVus(double vu_mono , double pan , double* vu_l , double* vu_r)
- {
- // compute panned mono channel contributions to left and right master VU meters
- *vu_l = (pan > 0.0) ? vu_mono * log10(10.0 * (1.0 - pan)) : vu_mono ; // pan_left
- *vu_r = (pan < 0.0) ? vu_mono * log10(10.0 * (1.0 + pan)) : vu_mono ; // pan right
- // cap underflows
- if (isnan(*vu_l)) *vu_l = 0.0 ;
- if (isnan(*vu_r)) *vu_r = 0.0 ;
- }
- float LinJam::ClientPan(float pan , int stereo_status)
- {
- // interpret faux-stereo pan for a real NJClient channel
- bool is_mono_channel = stereo_status == CONFIG::MONO ;
- bool is_pair_channel = stereo_status == CONFIG::STEREO_R ;
- return (is_mono_channel)? pan : (!is_pair_channel) ?
- ((pan <= 0.0f) ? -1.0f : -1.0f + (pan * 2.0f)) :
- ((pan >= 0.0f) ? +1.0f : +1.0f + (pan * 2.0f)) ;
- }
- /* NJClient config helpers */
- int LinJam::GetNumAudioSources()
- {
- return (Audio != nullptr)? Audio->getNInputChannels() : 0 ;
- }
- int LinJam::GetNumLocalChannels()
- {
- int n_occupied_slots = -1 ; while (~Client->EnumLocalChannels(++n_occupied_slots)) ;
- return n_occupied_slots ;
- }
- int LinJam::GetNumVacantChannels()
- {
- return (Audio != nullptr)? GetNumAudioSources() - GetNumLocalChannels() : 0 ;
- }
- int LinJam::GetVacantLocalChannelIdx()
- {
- // find the first vacant NJClient local channel slot index
- int channel_idx = -1 ; while (IsConfiguredChannel(++channel_idx)) ;
- bool is_vacant_slot = (channel_idx < GetNumAudioSources()) ;
- return (is_vacant_slot)? channel_idx : -1 ;
- }
- String LinJam::GetStoredChannelName(ValueTree channel_store)
- {
- return str(channel_store[CONFIG::CHANNEL_NAME_ID]) ;
- }
- String LinJam::GetLocalChannelClientName(int channel_idx)
- {
- return CharPointer_UTF8(Client->GetLocalChannelName(channel_idx)) ;
- }
- String LinJam::GetRemoteUserName(int user_idx)
- {
- return CharPointer_UTF8(Client->GetUserState(user_idx)) ;
- }
- String LinJam::GetRemoteChannelClientName(int user_idx , int channel_idx)
- {
- return CharPointer_UTF8(Client->GetUserChannelState(user_idx , channel_idx)) ;
- }
- bool LinJam::IsConfiguredChannel(int channel_idx)
- {
- return GetLocalChannelClientName(channel_idx).isNotEmpty() ;
- }
- double LinJam::GetChannelDb(int channel_idx)
- {
- return VAL2DB(Client->GetLocalChannelPeak(channel_idx)) ;
- }
- double LinJam::GetChannelDb(int user_idx , int channel_idx)
- {
- return VAL2DB(Client->GetUserChannelPeak(user_idx , channel_idx)) ;
- }
- /* signalling */
- void LinJam::SetPollUrl()
- {
- // TODO: server should handle empty params
- // String current_user = Client->GetUserName() ;
- // String current_user = Config->server[CONFIG::LOGIN_ID] ;
- String current_user = "sumdood" ;
- String current_host = String(Client->GetHostName()) ;
- StringPairArray poll_params = StringPairArray() ;
- poll_params.set(NETWORK::LOGIN_KEY , current_user) ;
- if (current_host.isNotEmpty()) poll_params.set (NETWORK::HOST_KEY , current_host) ;
- else poll_params.remove(StringRef(NETWORK::HOST_KEY)) ;
- PollUrl = NETWORK::POLL_URL.withParameters(poll_params) ;
- }
|