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.
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)
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
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?
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.
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.