twblue/src/sound_lib/output.py
2017-07-19 15:25:09 +02:00

191 lines
5.6 KiB
Python

from __future__ import division
from __future__ import absolute_import
from future.builtins import object
from functools import partial
import platform
from ctypes import c_char_p, c_float, pointer, string_at
from sound_lib.external.pybass import *
from . import config
from .main import bass_call, bass_call_0, EAX_ENVIRONMENTS, update_3d_system
_getter = lambda func, key, obj: func(obj)[key]
_setter = lambda func, kwarg, obj, val: func(obj, **{kwarg: val})
class Output (object):
def __init__(self, device=-1, frequency=44100, flags=0, window=0, clsid=None):
try:
self.use_default_device()
except:
pass
self._device = device
self.frequency = frequency
self.flags = flags
self.window = window
self.clsid = clsid
self.init_device(device=device, frequency=frequency, flags=flags, window=window, clsid=clsid)
self.config = config.BassConfig()
self.proxy = None
def init_device(self, device=None, frequency=None, flags=None, window=None, clsid=None):
if device is None:
device = self._device
self._device = device
if frequency is None:
frequency = self.frequency
self.frequency = frequency
if flags is None:
flags = self.flags
self.flags = flags
if window is None:
window = self.window
self.window = window
if clsid is None:
clsid = self.clsid
self.clsid = clsid
if platform.system() == 'Linux' and device == -1: #Bass wants default device set to 1 on linux
device = 1
bass_call(BASS_Init, device, frequency, flags, window, clsid)
def start(self):
return bass_call(BASS_Start)
def pause(self):
return bass_call(BASS_Pause)
def stop(self):
return bass_call(BASS_Stop)
def get_device(self):
return bass_call_0(BASS_GetDevice)
def set_device(self, device):
if device == self._device:
return
self.free()
self.init_device(device=device)
return bass_call(BASS_SetDevice, device)
device = property(fget=get_device, fset=set_device)
def get_volume (self):
volume = BASS_GetConfig(BASS_CONFIG_GVOL_STREAM)
if volume:
volume = volume / 100.0
return volume
def set_volume (self, volume):
#Pass in a float 0.0 to 100.0 and watch the volume magically change
return bass_call(BASS_SetConfig, BASS_CONFIG_GVOL_STREAM, int(round(volume*100, 2)))
volume = property(get_volume, set_volume)
@staticmethod
def free():
return bass_call(BASS_Free)
def get_proxy(self):
ptr = bass_call(BASS_GetConfigPtr, BASS_CONFIG_NET_PROXY)
return string_at(ptr)
def set_proxy(self, proxy):
self.proxy = c_char_p(proxy)
return bass_call(BASS_SetConfigPtr, BASS_CONFIG_NET_PROXY, self.proxy)
def use_default_device(self, use=True):
return bass_call(BASS_SetConfig, BASS_CONFIG_DEV_DEFAULT, use)
@staticmethod
def get_device_names():
"""Convenience method that returns a list of device names that are considered
valid by bass.
Parameters: none.
returns: list of devices, 0-indexed.
"""
result = [] # empty list to start.
info = BASS_DEVICEINFO()
count = 1
while BASS_GetDeviceInfo(count, ctypes.byref(info)):
if info.flags & BASS_DEVICE_ENABLED:
retrieved = info.name
if platform.system() == 'Windows':
retrieved = retrieved.decode('mbcs')
retrieved = retrieved.replace('(', '').replace(')', '').strip()
result.append(retrieved)
count += 1
return result
def find_device_by_name(self, name):
return self.get_device_names().index(name) + 1
def find_default_device(self):
try:
return self.get_device_names().index('Default')+1
except:
return -1
def find_user_provided_device(self, device_name):
try:
return self.find_device_by_name(device_name)
except ValueError:
return self.find_default_device()
class ThreeDOutput(Output):
def __init__(self, flags=BASS_DEVICE_3D, *args, **kwargs):
super(ThreeDOutput, self).__init__(flags=flags, *args, **kwargs)
def get_3d_factors(self):
res = {
'distance_factor': c_float(),
'rolloff': c_float(),
'doppler_factor': c_float()
}
bass_call(BASS_Get3DFactors, pointer(res['distance_factor']), pointer(res['rolloff']), pointer(res['doppler_factor']))
return {k: res[k].value for k in res}
@update_3d_system
def set_3d_factors(self, distance_factor=-1, rolloff=-1, doppler_factor=-1):
conversions = {
'meters': 1.0,
'yards': 0.9144,
'feet': 0.3048
}
if distance_factor in conversions:
distance_factor = conversions[distance_factor]
return bass_call(BASS_Set3DFactors, distance_factor, rolloff, doppler_factor)
distance_factor = property(fget=partial(_getter, get_3d_factors, 'distance_factor'), fset=partial(_setter, set_3d_factors, 'distance_factor'))
rolloff = property(fget=partial(_getter, get_3d_factors, 'rolloff'), fset=partial(_setter, set_3d_factors, 'rolloff'))
doppler_factor = property(fget=partial(_getter, get_3d_factors, 'doppler_factor'), fset=partial(_setter, set_3d_factors, 'doppler_factor'))
def set_eax_parameters(self, environment=None, volume=None, decay=None, damp=None):
def convert_arg(arg):
if arg is None:
arg = -1
return arg
environment = convert_arg(environment)
if isinstance(environment, str) and environment in EAX_ENVIRONMENTS:
environment = EAX_ENVIRONMENTS[environment]
volume = convert_arg(volume)
decay = convert_arg(decay)
damp = convert_arg(damp)
bass_call(BASS_SetEAXParameters, environment, volume, decay, damp)
def get_3d_algorithm(self):
return BASS_GetConfig(BASS_CONFIG_3DALGORITHM)
def set_3d_algorithm(self, algo):
replacements = {
'default': BASS_3DALG_DEFAULT,
'off': BASS_3DALG_OFF,
'full': BASS_3DALG_FULL,
'light': BASS_3DALG_LIGHT,
}
if algo in replacements:
algo = replacements[algo]
return BASS_SetConfig(BASS_CONFIG_3DALGORITHM, algo)