Example #1
0
    def on_modified(self, event):

        global source, scope

        path = event.src_path

        # Check if preamp input has changed, then RESET
        if STATE_PATH in path:
            new_source = read_state_from_disk()['input']
            if source != new_source:
                source = new_source
                self.meter.reset()
                sleep(.25)  # anti bouncing

        # Check if metadata album or title has changed, then RESET
        if PLAYER_META_PATH in path:
            md = read_metadata_from_disk()
            if not md:
                return
            # Ignore if scope is not a metadata field name
            if not scope in ('album', 'track'):
                return
            # (i) 'track' is named 'title' in pe.audio.sys metadata fields
            md_key = scope if (scope != 'track') else 'title'
            if md[md_key] != self.last_album_track:
                self.last_album_track = md[md_key]
                self.meter.reset()
                sleep(.25)  # anti bouncing
Example #2
0
def start_brutefir():
    """ runs Brutefir, connects to pream_in_loop and resets
        .state file with extra_delay = 0 ms
        (bool)
    """
    result = core.bf.restart_and_reconnect(
        ['pre_in_loop:output_1', 'pre_in_loop:output_2'], delay=0.0)
    # Ensuring that .state keeps extra_delay = 0 ms from start
    tmp_state = read_state_from_disk()
    tmp_state["extra_delay"] = 0.0
    force_to_flush_file(STATE_PATH, json_dumps(tmp_state))
    return result
Example #3
0
def manage_amp_switch(mode):
    def get_amp_state():
        result = 'n/a'
        try:
            with open(f'{AMP_STATE_PATH}', 'r') as f:
                tmp = f.read().strip()
            if tmp.lower() in ('0', 'off'):
                result = 'off'
            elif tmp.lower() in ('1', 'on'):
                result = 'on'
        except:
            pass
        return result

    def set_amp_state(mode):
        if 'amp_manager' in CONFIG:
            AMP_MANAGER = CONFIG['amp_manager']
        else:
            return '(aux) amp_manager not configured'
        print(f'(aux) running \'{AMP_MANAGER.split("/")[-1]} {mode}\'')
        Popen(f'{AMP_MANAGER} {mode}', shell=True)
        sleep(1)
        return get_amp_state()

    cur_state = get_amp_state()
    new_state = ''

    if mode == 'state':
        result = cur_state

    elif mode == 'toggle':
        # if unknown state, this switch defaults to 'on'
        new_state = {'on': 'off', 'off': 'on'}.get(cur_state, 'on')

    elif mode in ('on', 'off'):
        new_state = mode

    else:
        result = '(aux) bad amp_switch option'

    if new_state:
        result = set_amp_state(new_state)

    # Optionally will stop the current player as per CONFIG
    if new_state == 'off':
        if 'amp_off_stops_player' in CONFIG and CONFIG['amp_off_stops_player']:
            curr_input = read_state_from_disk()['input']
            if not curr_input.startswith('remote'):
                send_cmd('player pause', timeout=1)

    return result
Example #4
0
def get_meta():
    """ Returns a dictionary with the current track metadata
        including the involved source player
    """
    md = METATEMPLATE.copy()

    source = read_state_from_disk()['input']

    if 'librespot' in source or 'spotify' in source.lower():
        if SPOTIFY_CLIENT == 'desktop':
            md = spotify_meta(md)
        elif SPOTIFY_CLIENT == 'librespot':
            md = librespot_meta(md)
        # source is spotify like but no client running has been detected:
        else:
            md['player'] = 'Spotify'

    elif source == 'mpd':
        md = mpd_meta(md)

    elif source == 'istreams':
        md = mplayer_get_meta(md, service='istreams')

    elif source == 'tdt' or 'dvb' in source:
        md = mplayer_get_meta(md, service='dvb')

    elif 'cd' in source:
        md = mplayer_get_meta(md, service='cdda')

    elif source.startswith('remote'):
        # For a 'remote.....' named source, it is expected to have
        # an IP address kind of in its jack_pname field:
        #   jack_pname:  X.X.X.X:PPPP
        # so this way we can query metadata from the remote address.
        host = SOURCES[source]["jack_pname"].split(':')[0]
        port = SOURCES[source]["jack_pname"].split(':')[-1]
        if is_IP(host):
            if not port.isdigit():
                port = 9990
            md = remote_get_meta(host, port)

    # If there is no artist or album information, let's use the source name
    if md['artist'] == '-' and md['album'] == '-':
        md['artist'] = f'- {source.upper()} -'

    return md
Example #5
0
def playback_control(cmd, arg=''):
    """ Controls the playback, depending on the involved source player.
        returns: 'stop' | 'play' | 'pause'
    """

    result = 'stop'
    source = read_state_from_disk()['input']

    if source == 'mpd':
        result = mpd_control(cmd, arg)

    elif source.lower() == 'spotify':
        if SPOTIFY_CLIENT == 'desktop':
            result = spotify_control(cmd, arg)
        elif SPOTIFY_CLIENT == 'librespot':
            result = librespot_control(cmd)
        else:
            result = 'stop'

    elif 'tdt' in source or 'dvb' in source:
        result = mplayer_control(cmd=cmd, service='dvb')

    elif source in ['istreams', 'iradio']:
        result = mplayer_control(cmd=cmd, service='istreams')

    elif source == 'cd':
        result = mplayer_control(cmd=cmd, arg=arg, service='cdda')

    elif source.startswith('remote'):
        # For a 'remote.....' named source, it is expected to have
        # an IP address kind of in its jack_pname field:
        #   jack_pname:  X.X.X.X:PPPP
        # so this way we can query metadata from the remote address.
        host = SOURCES[source]["jack_pname"].split(':')[0]
        port = SOURCES[source]["jack_pname"].split(':')[-1]

        # (i) we assume that the remote pe.audio.sys listen at standard 9990 port
        if is_IP(host):
            if not port.isdigit():
                port = 9990
            result = remote_player_control(cmd=cmd,
                                           arg=arg,
                                           host=host,
                                           port=port)

    return result
Example #6
0
def random_control(arg):
    """ Controls the random/shuffle playback mode
        (i) Currently only works with: MPD
    """
    result = 'n/a'
    source = read_state_from_disk()['input']

    if source == 'mpd':
        result = mpd_control('random', arg)

    elif source == 'spotify':

        if SPOTIFY_CLIENT == 'desktop':
            result = spotify_control('random', arg)
        elif SPOTIFY_CLIENT == 'librespot':
            result = librespot_control('random', arg)

    return result
Example #7
0
def playlists_control(cmd, arg):
    """ Manage playlists.
        (i) Currently only works with: Spotify Desktop, MPD.
    """
    result = []
    source = read_state_from_disk()['input']

    if source == 'mpd':
        result = mpd_playlists(cmd, arg)

    elif source == 'spotify':

        if SPOTIFY_CLIENT == 'desktop':
            result = spotify_playlists(cmd, arg)

    elif source == 'cd':
        result = mplayer_playlists(cmd=cmd, arg=arg, service='cdda')

    return result
def main_loop(alertdB=CFG['alertdB'], beep=CFG['beep']):

    level_ups = False
    beeped = False

    while True:

        # Reading the mouse
        ev = getMouseEvent()

        # Sending the order to pe.audio.sys
        if ev == 'buttonLeftDown':
            # Level --
            send_cmd(f'level -{CFG["STEPdB"]} add',
                     sender='mouse_volume',
                     verbose=True)
            level_ups = False

        elif ev == 'buttonRightDown':
            # Level ++
            send_cmd(f'level +{CFG["STEPdB"]} add',
                     sender='mouse_volume',
                     verbose=True)
            level_ups = True

        elif ev == 'buttonMid':
            # Mute toggle
            send_cmd('mute toggle', sender='mouse_volume', verbose=True)

        # Alert if crossed the headroom threshold
        if level_ups:
            level = read_state_from_disk()['level']
            if (level + CFG['STEPdB']) >= alertdB:
                if not beeped and beep:
                    print('(mouse_volume_daemon) BEEEEEEP, BEEEEEP')
                    beeps()
                    beeped = True
            else:
                beeped = False
Example #9
0
    def __init__(self):

        # The available inputs
        self.inputs = CONFIG["sources"]
        # The state dictionary
        self.state = read_state_from_disk()
        self.state["convolver_runs"] = bf.is_running()
        # will add some informative values:
        self.state["loudspeaker"] = CONFIG["loudspeaker"]
        self.state["loudspeaker_ref_SPL"] = CONFIG["refSPL"]
        self.state["peq_set"] = get_peq_in_use()
        self.state["fs"] = jack.get_samplerate()
        # The target curves available under the 'eq' folder
        self.target_sets = self._find_target_sets()
        # The available span for tone curves
        self.bass_span   = int( (EQ_CURVES["bass_mag"].shape[0] - 1) / 2 )
        self.treble_span = int( (EQ_CURVES["treb_mag"].shape[0] - 1) / 2 )
        # Max authorised gain
        self.gain_max    = float(CONFIG["gain_max"])
        # Max authorised balance
        self.balance_max = float(CONFIG["balance_max"])
        # Initiate brutefir input connected ports (used from switch_convolver)
        self.bf_sources = bf.get_in_connections()

        # Powersave
        #   State file info
        self.state["powersave"] = False
        #
        #   Powersave loop: breaking flag
        self.ps_end = threading.Event()
        #
        #   Powersave loop:reset elapsed low level detected counter flag
        self.ps_reset_elapsed = threading.Event()
        #
        #   Convolver driving events
        def wait_PS_convolver_off():
            """ Event handler for convolver switch off requests
            """
            while True:
                # waiting ...
                self.ps_convolver_off.wait()
                print(f'(core) Thread \'waits for convolver OFF\' received event')
                self.ps_convolver_off.clear()
                self.switch_convolver('off')
        #
        def wait_PS_convolver_on():
            """ Event handler for convolver switch on requests
            """
            while True:
                # waiting ...
                self.ps_convolver_on.wait()
                print(f'(core) Thread \'waits for convolver ON\' received event')
                self.ps_convolver_on.clear()
                self.switch_convolver('on')
        #
        self.ps_convolver_off = threading.Event()
        self.ps_convolver_on  = threading.Event()
        t1 = threading.Thread( name='waits for convolver OFF',
                               target=wait_PS_convolver_off )
        t2 = threading.Thread( name='waits for convolver ON',
                               target=wait_PS_convolver_on )
        t1.start()
        t2.start()
        self._print_threads()
Example #10
0
def update_lcd_state(scr='scr_1'):
    """ Reads system .state file, then updates the LCD """
    # http://lcdproc.sourceforge.net/docs/lcdproc-0-5-5-user.html

    global state

    def show_state(priority="info"):

        ws = Widgets()

        global equal_loudness

        for key, value in state.items():

            # The LU bar disables displaying bass and treble
            if LCD_CONFIG["LUmon_bar"]:
                if key in ('bass', 'treble'):
                    continue

            # some state dict keys cannot have its
            # correspondence into the widgets_state dict
            if key not in ws.state:
                continue

            pos = ws.state[key]['pos']  # pos ~> position
            lbl = ws.state[key]['val']  # lbl ~> label

            # When booleans (equal_loudness, muted)
            # will leave the defalul widget value or will supress it
            if type(value) == bool:
                if not value:
                    lbl = ''
                # Update global to be accesible outside from auxiliary
                if key == 'equal_loudness':
                    equal_loudness = value

            # Special case: lu_offset will be rounded to integer
            #               or void if no equal_loudness
            elif key == 'lu_offset':
                if state['equal_loudness']:
                    lbl += str(int(round(value, 0))).rjust(3)
                else:
                    lbl = 'LOUDc off'

            # Special case: tone will be rounded to integer
            elif key == 'bass' or key == 'treble':
                lbl += str(int(round(value, 0))).rjust(2)

            # Special case: midside (formerly 'mono')
            elif key == 'midside':
                if state['midside'] == 'off':
                    lbl = '>ST<'
                elif state['midside'] == 'mid':
                    lbl = 'MONO'
                elif state['midside'] == 'side':
                    lbl = 'SIDE'
                else:
                    lbl = ''
                # Special usage mono label to display if convolver is off
                if 'convolver_runs' in state and not state['convolver_runs']:
                    lbl = ' zzz'  # brutefir is sleeping

            # Any else key:
            else:
                lbl += str(value)

            # sintax for string widgets:
            #   widget_set screen widget coordinate "text"
            cmd = f'widget_set {scr} {key} {pos} "{lbl}"'
            LCD.send(cmd)

        # The big screen to display the level value
        #lcdbig.show_level( str(state['level']) )
        pass

    # Reading state
    try:
        new_state = read_state_from_disk()
    except:
        return

    # If changed
    if new_state != state:

        # update global state
        state = new_state

        # refreshing the LU monitor bar in LCD if needed
        if LCD_CONFIG["LUmon_bar"]:
            update_lcd_loudness_monitor()

        # refresh state items in LCD
        show_state()
Example #11
0
            # (i) Only import LU_meter when 'start' because it takes a long time,
            # so it can trouble the stop pkill (can be too much delayed).
            from audiotools.loudness_meter import LU_meter

        else:
            print(__doc__)
            sys.exit()
    else:
        print(__doc__)
        sys.exit()

    # Initialize the scope of the measurements (input, album or track)
    scope = get_configured_scope()

    # Initialize current preamp source
    source = read_state_from_disk()['input']

    # Starts a LU_meter instance with relevant parameters:
    # M_threshold = 10.0   To avoid stress saving values to disk, because this
    #                      measure only serves as a rough signal detector.
    # I_threshold = 1.0    LU-[I]ntegrated values are relatively stable.
    meter = LU_meter(device='pre_in_loop',
                     display=False,
                     M_threshold=10.0,
                     I_threshold=1.0)
    meter.start()
    print(f'(loudness_monitor) spawn PortAudio ports in JACK')

    # Threading the fifo listening loop for controlling this module
    prepare_control_fifo(LDCTRL_PATH)
    control = threading.Thread(target=control_fifo_read_loop,