function wrk_mpdconf($outpath, $db) { // extract mpd.conf from SQLite datastore $dbh = cfgdb_connect($db); $query_cfg = "SELECT param,value_player FROM cfg_mpd WHERE value_player!=''"; $mpdcfg = sdbquery($query_cfg, $dbh); $dbh = null; // set mpd.conf file header $output = "###################################\n"; $output .= "# Auto generated mpd.conf file\n"; $output .= "# please DO NOT edit it manually!\n"; $output .= "# Use player-UI MPD config section\n"; $output .= "###################################\n"; $output .= "\n"; // parse DB output foreach ($mpdcfg as $cfg) { if ($cfg['param'] == 'audio_output_format' && $cfg['value_player'] == 'disabled') { $output .= ''; } else { if ($cfg['param'] == 'dsd_usb') { $dsd = $cfg['value_player']; } else { if ($cfg['param'] == 'device') { $device = $cfg['value_player']; var_export($device); // $output .= ''; } else { if ($cfg['param'] == 'mixer_type' && $cfg['value_player'] == 'hardware') { // $hwmixer['device'] = 'hw:0'; $hwmixer['control'] = alsa_findHwMixerControl($device); // $hwmixer['index'] = '1'; } else { $output .= $cfg['param'] . " \t\"" . $cfg['value_player'] . "\"\n"; } } } } } // format audio input / output interfaces $output .= "max_connections \"20\"\n"; $output .= "\n"; $output .= "decoder {\n"; $output .= "\t\tplugin \"ffmpeg\"\n"; $output .= "\t\tenabled \"yes\"\n"; $output .= "}\n"; $output .= "\n"; $output .= "input {\n"; $output .= "\t\tplugin \"curl\"\n"; $output .= "}\n"; $output .= "\n"; $output .= "audio_output {\n\n"; $output .= "\t\t type \t\t\"alsa\"\n"; $output .= "\t\t name \t\t\"Output\"\n"; $output .= "\t\t device \t\"hw:" . $device . ",0\"\n"; if (isset($hwmixer)) { //$output .= "\t\t mixer_device \t\"".$hwmixer['device']."\"\n"; $output .= "\t\t mixer_control \t\"" . $hwmixer['control'] . "\"\n"; $output .= "\t\t mixer_device \t\"hw:" . $device . "\"\n"; $output .= "\t\t mixer_index \t\"0\"\n"; //$output .= "\t\t mixer_index \t\"".$hwmixer['index']."\"\n"; } $output .= "\t\t dsd_usb \t\"" . $dsd . "\"\n"; $output .= "\n}\n"; // write mpd.conf file $fh = fopen($outpath . "/mpd.conf", 'w'); fwrite($fh, $output); fclose($fh); }
function wrk_mpdconf($redis, $action, $args = null, $jobID = null) { // set mpd.conf file header $header = "###################################\n"; $header .= "# Auto generated mpd.conf file\n"; $header .= "# please DO NOT edit it manually!\n"; $header .= "# Use RuneUI MPD config section\n"; $header .= "###################################\n"; $header .= "\n"; switch ($action) { case 'reset': // default MPD config $redis->hSet('mpdconf', 'zeroconf_enabled', 'yes'); $redis->hSet('mpdconf', 'zeroconf_name', 'runeaudio'); $redis->hSet('mpdconf', 'log_level', 'none'); $redis->hSet('mpdconf', 'bind_to_address', 'any'); $redis->hSet('mpdconf', 'port', '6600'); $redis->hSet('mpdconf', 'max_connections', '20'); $redis->hSet('mpdconf', 'user', 'mpd'); $redis->hSet('mpdconf', 'db_file', '/var/lib/mpd/mpd.db'); $redis->hSet('mpdconf', 'sticker_file', '/var/lib/mpd/sticker.sql'); $redis->hSet('mpdconf', 'pid_file', '/var/run/mpd/pid'); $redis->hSet('mpdconf', 'music_directory', '/mnt/MPD'); $redis->hSet('mpdconf', 'playlist_directory', '/var/lib/mpd/playlists'); $redis->hSet('mpdconf', 'state_file', '/var/lib/mpd/mpdstate'); $redis->hSet('mpdconf', 'follow_outside_symlinks', 'yes'); $redis->hSet('mpdconf', 'follow_inside_symlinks', 'yes'); $redis->hSet('mpdconf', 'auto_update', 'no'); $redis->hSet('mpdconf', 'filesystem_charset', 'UTF-8'); $redis->hSet('mpdconf', 'id3v1_encoding', 'UTF-8'); $redis->hSet('mpdconf', 'volume_normalization', 'no'); $redis->hSet('mpdconf', 'audio_buffer_size', '2048'); $redis->hSet('mpdconf', 'buffer_before_play', '10%'); $redis->hSet('mpdconf', 'gapless_mp3_playback', 'yes'); $redis->hSet('mpdconf', 'mixer_type', 'software'); $redis->hSet('mpdconf', 'curl', 'yes'); $redis->hSet('mpdconf', 'ffmpeg', 'yes'); $redis->hSet('mpdconf', 'log_file', '/var/log/runeaudio/mpd.log'); wrk_mpdconf($redis, 'writecfg'); break; case 'writecfg': $mpdcfg = $redis->hGetAll('mpdconf'); $current_out = $redis->Get('ao'); // if (!$redis->hExists('acards', $current_out)) { if (!$redis->hExists('acards', $current_out) && $redis->Get('i2smodule') === 'none') { $stored_acards = $redis->hKeys('acards'); // debug runelog('force audio output', $stored_acards[0]); // force first output available if the current interface does not exists $redis->Set('ao', $stored_acards[0]); $redis->Save(); } $output = null; // --- log settings --- if ($mpdcfg['log_level'] === 'none') { $redis->hDel('mpdconf', 'log_file'); } else { $output .= "log_level\t\"" . $mpdcfg['log_level'] . "\"\n"; $output .= "log_file\t\"/var/log/runeaudio/mpd.log\"\n"; $redis->hSet('mpdconf', 'log_file', '/var/log/runeaudio/mpd.log'); } unset($mpdcfg['log_level']); unset($mpdcfg['log_file']); // --- state file --- if ($mpdcfg['state_file'] === 'no') { $redis->hDel('mpdconf', 'state_file'); } else { $output .= "state_file\t\"/var/lib/mpd/mpdstate\"\n"; $redis->hSet('mpdconf', 'state_file', '/var/lib/mpd/mpdstate'); } unset($mpdcfg['state_file']); // --- general settings --- foreach ($mpdcfg as $param => $value) { if ($param === 'audio_output_interface' or $param === 'dsd_usb') { continue; } if ($param === 'mixer_type') { if ($value === 'software' or $value === 'hardware') { $redis->set('volume', 1); if ($value === 'hardware') { $hwmixer = 1; continue; } } else { $redis->set('volume', 0); } } if ($param === 'user' && $value === 'mpd') { $output .= $param . " \t\"" . $value . "\"\n"; $output .= "group \t\"audio\"\n"; continue; } if ($param === 'user' && $value === 'root') { $output .= $param . " \t\"" . $value . "\"\n"; $output .= "group \t\"root\"\n"; continue; } if ($param === 'bind_to_address') { $output .= "bind_to_address \"/run/mpd.sock\"\n"; } if ($param === 'ffmpeg') { // --- decoder plugin --- $output .= "\n"; $output .= "decoder {\n"; $output .= "plugin \t\"ffmpeg\"\n"; $output .= "enabled \"" . $value . "\"\n"; $output .= "}\n"; continue; } if ($param === 'curl') { // --- input plugin --- $output .= "\n"; $output .= "input {\n"; $output .= "plugin \t\"curl\"\n"; if ($redis->hget('proxy', 'enable') === '1') { $output .= "proxy \t\"" . $redis->hget('proxy', 'host') . "\"\n"; if ($redis->hget('proxy', 'user') !== '') { $output .= "proxy_user \t\"" . $redis->hget('proxy', 'user') . "\"\n"; $output .= "proxy_password \t\"" . $redis->hget('proxy', 'pass') . "\"\n"; } } $output .= "}\n"; continue; } $output .= $param . " \t\"" . $value . "\"\n"; } $output = $header . $output; // --- audio output --- $acards = $redis->hGetAll('acards'); // debug runelog('detected ACARDS ', $acards, __FUNCTION__); $ao = $redis->Get('ao'); $sub_count = 0; foreach ($acards as $card) { $card_decoded = new stdClass(); $card_decoded = json_decode($card); // debug runelog('decoded ACARD ', $card_decoded, __FUNCTION__); if (isset($card_decoded->integrated_sub) && $card_decoded->integrated_sub === 1) { // record UI audio output name $current_card = $card_decoded->name; if ($sub_count >= 1) { continue; } $card_decoded = json_decode($card_decoded->real_interface); runelog('current AO ----> ', $ao, __FUNCTION__); // var_dump($ao); runelog('current card_name ----> ', $card_decoded->name, __FUNCTION__); // var_dump($card_decoded->name); // var_dump(strpos($ao, $card_decoded->name)); if (strpos($ao, $card_decoded->name) === true or strpos($ao, $card_decoded->name) === 0) { $sub_interface_selected = 1; } // debug if (isset($sub_interface_selected)) { runelog('sub_card_selected ? >>>> ' . $sub_interface_selected); } // debug runelog('this is a sub_interface', __FUNCTION__); $sub_interface = 1; $sub_count++; runelog('sub_count', $sub_count, __FUNCTION__); } $output .= "\n"; $output .= "audio_output {\n"; $output .= "name \t\t\"" . $card_decoded->name . "\"\n"; $output .= "type \t\t\"" . $card_decoded->type . "\"\n"; $output .= "device \t\t\"" . $card_decoded->device . "\"\n"; if (isset($hwmixer)) { $output .= "mixer_type \t\"hardware\"\n"; if (isset($card_decoded->mixer_device)) { $output .= "mixer_device \t\"" . $card_decoded->mixer_device . "\"\n"; } else { $output .= "mixer_device \t\"hw:" . $card_decoded->device . "\"\n"; } if (isset($card_decoded->mixer_control)) { $output .= "mixer_control \t\"" . $card_decoded->mixer_control . "\"\n"; } else { $output .= "mixer_control \t\"" . alsa_findHwMixerControl(substr($card_decoded->device, 4, 1)) . ",0\"\n"; } $output .= "mixer_index \t\"0\"\n"; "\t\t \t\"0\"\n"; } if ($mpdcfg['dsd_usb'] === 'yes') { $output .= "dsd_usb \t\"yes\"\n"; } $output .= "auto_resample \t\"no\"\n"; $output .= "auto_format \t\"no\"\n"; if (isset($sub_interface_selected)) { // use UI selector name to enable a sub_interface (ex. switch between AnalogOut / HDMI on RaspberryPI) $output .= "enabled \t\"yes\"\n"; } else { // normal condition if ($ao === $card_decoded->name) { $output .= "enabled \t\"yes\"\n"; } } $output .= "}\n"; // unset($current_card); // unset($sub_interface); // unset($card_decoded); // debug runelog('conf output (in loop)', $output, __FUNCTION__); } $output .= "\n"; // debug runelog('raw mpd.conf', $output, __FUNCTION__); // write mpd.conf file $fh = fopen('/etc/mpd.conf', 'w'); fwrite($fh, $output); fclose($fh); // update hash $redis->set('mpdconfhash', md5_file('/etc/mpd.conf')); break; case 'update': foreach ($args as $param => $value) { $redis->hSet('mpdconf', $param, $value); } wrk_mpdconf($redis, 'writecfg'); break; case 'switchao': // record current interface selection $redis->set('ao', $args); $mpdout = $args; // get interface details $interface_details = $redis->hGet('acards', $args); $interface_details = json_decode($interface_details); // check for "special" sub_interfaces if (isset($interface_details->integrated_sub)) { // execute special internal route command sysCmd($interface_details->route_cmd); // TODO: improove this function sysCmd('amixer -c 0 set PCM unmute'); $mpdout = $interface_details->sysname; } wrk_mpdconf($redis, 'writecfg'); wrk_shairport($redis, $args); // toggle playback state if (wrk_mpdPlaybackStatus() === 'playing') { syscmd('mpc toggle'); $recover_state = 1; // debug runelog('switchao (set recover state):', $recover_state); } // switch interface // debug runelog('switchao (switch AO):', $mpdout); syscmd('mpc enable only "' . $mpdout . '"'); // restore playback state if (isset($recover_state)) { // debug runelog('switchao (RECOVER STATE!)'); syscmd('mpc toggle'); } // set notify label if (isset($interface_details->extlabel)) { $interface_label = $interface_details->extlabel; } else { $interface_label = $args; } // notify UI ui_notify_async('Audio output switched', "Current active output:\n" . $interface_label, $jobID); break; case 'refresh': wrk_audioOutput($redis, 'refresh'); wrk_mpdconf($redis, 'writecfg'); wrk_mpdconf($redis, 'restart'); break; case 'start': $activePlayer = $redis->get('activePlayer'); if ($activePlayer === 'MPD') { sysCmd('systemctl start mpd'); if ($redis->get('mpd_playback_status') === 'playing') { syscmd('mpc play'); } // restart mpdscribble if ($redis->hGet('lastfm', 'enable') === '1') { sysCmd('systemctl restart mpdscribble'); } // restart upmpdcli if ($redis->hGet('dlna', 'enable') === '1') { sysCmd('systemctl restart upmpdcli'); } // set process priority sysCmdAsync('sleep 1 && rune_prio nice'); } break; case 'stop': $redis->set('mpd_playback_status', wrk_mpdPlaybackStatus()); $mpd = openMpdSocket('/run/mpd.sock'); sendMpdCommand($mpd, 'kill'); closeMpdSocket($mpd); sleep(1); sysCmd('systemctl stop mpd'); break; case 'restart': wrk_mpdconf($redis, 'stop'); sleep(1); wrk_mpdconf($redis, 'start'); break; } }