Pro-Ject Pre Box S2 Digital - Volume Control

Hi! Still new to Roon and high end audio general so bear with me.

Apart from my Stereo setup I also just got some Focal Elear Headphones that I’m using with the Pro-Ject Pre Box S2 Digital. The Pro-Ject is then connected (via USB) to a Pi running Ropieee.

So far I’ve been using Fixed Volume with the Elears and have been adjusting the volume either with the volume knob or the included remote. However, as with all my other endpoints, I’d like to move to Roon only for volume control.

I’d like to use ‘Device Volume’ and was wondering what the best settings regarding my Pro-Ject would be. Should I set it to 0db and then adjust the Volume from Roon only? I tried this, however the db indicator on the Pro-Ject didn’t change when I adjusted the volume inside Roon. I just want to prevent blasting the headphones at full volume on accident.

If anyone with general knowledge or a Pre Box S2 Digital specifically could explain to me (in layman’s terms) how to best achieve the setup I’m after that’d be greatly appreciated.

Thanks!

Anyone able to help?

There is no way for Roon to control this particular DAC’s internal (DAC chip) digital volume control.

Personally I would recommend you keep using the volume knob and/or remote control.

1 Like

Hi @Cudifying

I bought this same setup for my old man (dad) (Pro-Ject Pre Box S2 + Focal Elear) and he loves it.

I bought a cheap learning remote control which is easier to hold and has bigger buttons than the stock remote.

I bought a few actually in case he breaks any.

There are much cheaper options on eBay but this is exactly what it looks like:

It’s pretty complicated, but I have a system set up to control volume on this DAC from inside Roon. It involves a Roon extension written in Python that talks to a Broadlink RM Mini that sends IR to the DAC. If you’re programming-savvy, the code is below.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os.path
import signal
import sys
import time
from roon import RoonApi
import broadlink
import threading

CORE_IP = '192.168.1.40'
STARTING_VOLUME = -35



appinfo = {
                "extension_id": "PreBoxS2_Broadlink",
                "display_name": "Control Pro-Ject Pre Box S2 via Broadlink IR",
                "display_version": "1.0.0",
                "publisher": "joegratz",
                "email": "joe@joegratz.net"
        }

def volume_callback(control_key, event, data):
        global currentVolume
        global targetVolume
        global currentMuteState
        global targetMuteState
        if event == "set_mute":
                volumeLock.acquire()
                roonapi.update_volume_control(control_key, mute=data)
                targetMuteState = data
                volumeLock.release()

        elif event == "set_volume":
                roonapi.update_volume_control(control_key, volume=data)
                volumeLock.acquire()
                targetVolume = int(data)
                volumeLock.release()

def volumeUp():
        broadlinkDevice.send_data(bytearray.fromhex('260018001e1c3b371f1d3b1c1d1d1e1c1e1c1e381f1b1f1b3c000d05'))

def volumeDown():
        broadlinkDevice.send_data(bytearray.fromhex('260018001e1c3c381e1c3b1c1e1c1e1c1e373d1c1e1c1e1c1e000d05'))

def mute():
        broadlinkDevice.send_data(bytearray.fromhex('260018001f1b3c381e1c3c1b1f1b1f1b1f1b1e391e1c3b1c1e000d05'))

def volumeWorker():
        global currentVolume
        global targetVolume
        global currentMuteState
        global targetMuteState
        while True:
                time.sleep(0.03)
                volumeLock.acquire()
                difference = targetVolume - currentVolume
                if difference != 0:
                        if difference < 0:
                                volumeDown()
                                currentMuteState = False
                                currentVolume = currentVolume - 1
                        if difference > 0:
                                volumeUp()
                                currentMuteState = False
                                currentVolume = currentVolume + 1
                elif targetMuteState != currentMuteState:
                        mute()
                        currentMuteState = not currentMuteState
                volumeLock.release()

def calibrateVolume(postCalibrationValue):
        if postCalibrationValue < 0:
                postCalibrationValue = -1 * postCalibrationValue
        for x in range(80):
                volumeDown()
                print('Volume Down %s' % x)
                time.sleep(0.03)
        for x in range(80-postCalibrationValue):
                volumeUp()
                print('Volume Up %s' % x)
                time.sleep(0.03)
# cleanup handler to properly close the connection and save the token for later use
def cleanup(signum, frame):
        roonapi.stop()
        token = roonapi.token
        print("token: %s" % token)
        if token:
                with open("mytokenfile", "w") as f:
                        f.write(token)
        sys.exit(signum)

# signal handler
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]:
                signal.signal(sig, cleanup)

# initialize Roon api and register the callback for state changes
token = None
if os.path.isfile("mytokenfile"):
        with open("mytokenfile") as f:
                token = f.read()
roonapi = RoonApi(appinfo, token, host=CORE_IP)
broadlinkDevice = broadlink.discover(timeout=10)[0]
broadlinkDevice.auth()
calibrateVolume(STARTING_VOLUME)
global currentVolume
currentVolume = STARTING_VOLUME
global targetVolume
targetVolume = STARTING_VOLUME
global currentMuteState
currentMuteState = False
global targetMuteState
targetMuteState = False
roonapi.register_volume_control("IRVolume", "IR Volume", volume_callback, volume_type='db', volume_min=-80, volume_max=0, initial_volume=STARTING_VOLUME)
volumeLock = threading.Lock()
t = threading.Thread(target=volumeWorker)
t.daemon = True
t.start()

# keep it alive!
while True:
        time.sleep(1)
2 Likes

Nice!

This is why I should never start a sentence with “there’s no way”

I should have said there’s no way that’s supported by Roon and/or Pro-Ject.

But this is cool.

1 Like

I can’t tell you if it is the best setting, but yes, I do set the volume of both of my PreBox S2 Digital devices you 0dB and then I control volume through Roon with one and through my preamp with the other. In neither case is there any change to the volume level of 0dB set in the DACs. JCR

Hi Jeffrey, this sounds to be closest to what I’m trying to achieve. I will be receiving a Pro-Ject Stream Box Ultra later this week which will be replacing my Pi in this setup.

Have you set any volume limit inside Roon between 0-100? I also take it you disabled the setting “Force Max Volume At Playback Start” when the Pre Box is set to 0db and Roon to 100 because otherwise that’d probably blow your ears out :slight_smile:

The only additional question I have is if this will introduce quality loss in any shape or form. When I tried it for a minute it kept the purple light in the playback chain, does that mean it’s still lossless even though I’m controlling the volume through Roon and not directly from the Pre Box?

Thanks again.

No volume limit and yes, force max volume is disabled. Whenever you use the volume control within Roon, you will get the notice that your path isn’t lossless. No big deal. Using convolution filters will do the same thing. It’s what you need to do to use Roon comfortably and I think it sounds fine. JCR

Interestingly enough, I still get the purple light indicating it’s a lossless connection even though I am now controlling the volume via Roon as you instructed Jeffrey. I set a ‘comfort level’ at 50/100 and this has been working great throughout the day.

Once again, thanks!

Make sure you are using DSP volume control in Roon, rather than the USB-based device volume on the Pre Box S2 (which is different from the digitally-controlled analog volume control on the front panel!). There probably won’t be an appreciable difference in quality, but with the USB-based device volume you are doing the volume control in the XMOS chip in the Pre Box S2 (I think; at any rate you’re not using the digitally-controlled analog volume control in the device). When doing volume control with Roon DSP you are doing it in a 64-bit environment inside Roon, and it’s then sent to the Pre Box S2 at 32 bit, so there’s really no mathematical chance of quality loss in any reasonable case.

1 Like