Merge pull request #36 from UniversityRadioYork/mstratford/linux-audio
Linux updates for Studio1PC
This commit is contained in:
commit
53b26e2366
11 changed files with 113 additions and 21 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -25,3 +25,5 @@ music-tmp/
|
||||||
|
|
||||||
|
|
||||||
presenter-build
|
presenter-build
|
||||||
|
|
||||||
|
node_modules/
|
|
@ -7,7 +7,7 @@ build_branch="$(git branch --show-current)"
|
||||||
echo "BUILD: str = \"$build_commit\"" > ../build.py
|
echo "BUILD: str = \"$build_commit\"" > ../build.py
|
||||||
echo "BRANCH: str = \"$build_branch\"" >> ../build.py
|
echo "BRANCH: str = \"$build_branch\"" >> ../build.py
|
||||||
|
|
||||||
apt install libportaudio2
|
sudo apt install libportaudio2
|
||||||
|
|
||||||
python3 -m venv ../venv
|
python3 -m venv ../venv
|
||||||
source ../venv/bin/activate
|
source ../venv/bin/activate
|
||||||
|
@ -19,6 +19,8 @@ pip3 install -e ../
|
||||||
|
|
||||||
python3 ./generate-build-exe-config.py
|
python3 ./generate-build-exe-config.py
|
||||||
|
|
||||||
|
chmod +x output/BAPSicle
|
||||||
|
|
||||||
python3 ./build-exe.py
|
python3 ./build-exe.py
|
||||||
|
|
||||||
bash ./build-exe-pyinstaller-command.sh
|
bash ./build-exe-pyinstaller-command.sh
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
wheel
|
wheel
|
||||||
pygame==2.0.1
|
pygame==2.0.2
|
||||||
sanic==21.3.4
|
sanic==21.3.4
|
||||||
sanic-Cors==1.0.0
|
sanic-Cors==1.0.0
|
||||||
syncer==1.3.0
|
syncer==1.3.0
|
||||||
|
|
24
dev/scripts/get_linux_outputs.py
Normal file
24
dev/scripts/get_linux_outputs.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
||||||
|
os.putenv('SDL_AUDIODRIVER', 'pulseaudio')
|
||||||
|
import pygame._sdl2 as sdl2
|
||||||
|
import pygame
|
||||||
|
from pygame import mixer
|
||||||
|
pygame.init()
|
||||||
|
import time
|
||||||
|
mixer.init(44100, -16, 2, 1024)
|
||||||
|
is_capture = 0 # zero to request playback devices, non-zero to request recording devices
|
||||||
|
num = sdl2.get_num_audio_devices(is_capture)
|
||||||
|
names = [str(sdl2.get_audio_device_name(i, is_capture), encoding="utf-8") for i in range(num)]
|
||||||
|
mixer.quit()
|
||||||
|
for i in names:
|
||||||
|
print(i)
|
||||||
|
mixer.init(44100, -16, 2, 1024, devicename=i)
|
||||||
|
print(mixer.get_init())
|
||||||
|
mixer.music.load("/home/mstratford/Downloads/managed_play.mp3")
|
||||||
|
mixer.music.play()
|
||||||
|
# my_song = mixer.Sound("/home/mstratford/Downloads/managed_play.mp3")
|
||||||
|
# my_song.play()
|
||||||
|
time.sleep(5)
|
||||||
|
pygame.quit()
|
|
@ -1,6 +1,13 @@
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
import sounddevice as sd
|
import sounddevice as sd
|
||||||
from helpers.os_environment import isLinux, isMacOS, isWindows
|
from helpers.os_environment import isLinux, isMacOS, isWindows
|
||||||
|
import os
|
||||||
|
|
||||||
|
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
||||||
|
if isLinux():
|
||||||
|
os.putenv('SDL_AUDIODRIVER', 'pulseaudio')
|
||||||
|
import pygame._sdl2 as sdl2
|
||||||
|
from pygame import mixer
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
if isWindows():
|
if isWindows():
|
||||||
|
@ -20,10 +27,10 @@ class DeviceManager:
|
||||||
return host_api
|
return host_api
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _getAudioDevices(cls) -> sd.DeviceList:
|
def _getSDAudioDevices(cls):
|
||||||
# To update the list of devices
|
# To update the list of devices
|
||||||
# Sadly this doesn't work on MacOS.
|
# Sadly this only works on Windows. Linux hangs, MacOS crashes.
|
||||||
if not isMacOS():
|
if isWindows():
|
||||||
sd._terminate()
|
sd._terminate()
|
||||||
sd._initialize()
|
sd._initialize()
|
||||||
devices: sd.DeviceList = sd.query_devices()
|
devices: sd.DeviceList = sd.query_devices()
|
||||||
|
@ -31,11 +38,13 @@ class DeviceManager:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getAudioOutputs(cls) -> Tuple[List[Dict]]:
|
def getAudioOutputs(cls) -> Tuple[List[Dict]]:
|
||||||
host_apis = sd.query_hostapis()
|
|
||||||
devices: sd.DeviceList = cls._getAudioDevices()
|
host_apis = list(sd.query_hostapis())
|
||||||
|
devices: sd.DeviceList = cls._getSDAudioDevices()
|
||||||
|
|
||||||
for host_api_id in range(len(host_apis)):
|
for host_api_id in range(len(host_apis)):
|
||||||
if isWindows() and host_apis[host_api_id]["name"] not in WINDOWS_APIS:
|
# Linux SDL uses PortAudio, which SoundDevice doesn't find. So mark all as unsable.
|
||||||
|
if (isWindows() and host_apis[host_api_id]["name"] not in WINDOWS_APIS) or (isLinux()):
|
||||||
host_apis[host_api_id]["usable"] = False
|
host_apis[host_api_id]["usable"] = False
|
||||||
else:
|
else:
|
||||||
host_apis[host_api_id]["usable"] = True
|
host_apis[host_api_id]["usable"] = True
|
||||||
|
@ -51,6 +60,15 @@ class DeviceManager:
|
||||||
|
|
||||||
return host_apis
|
return host_apis
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getAudioDevices(cls) -> List[str]:
|
||||||
|
mixer.init(44100, -16, 2, 1024)
|
||||||
|
is_capture = 0 # zero to request playback devices, non-zero to request recording devices
|
||||||
|
num = sdl2.get_num_audio_devices(is_capture)
|
||||||
|
names = [str(sdl2.get_audio_device_name(i, is_capture), encoding="utf-8") for i in range(num)]
|
||||||
|
mixer.quit()
|
||||||
|
return names
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getSerialPorts(cls) -> List[Optional[str]]:
|
def getSerialPorts(cls) -> List[Optional[str]]:
|
||||||
"""Lists serial port names
|
"""Lists serial port names
|
||||||
|
|
12
package-lock.json
generated
12
package-lock.json
generated
|
@ -1,5 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "bapsicle",
|
"name": "bapsicle",
|
||||||
"version": "3.0.0",
|
"version": "3.1.0",
|
||||||
"lockfileVersion": 1
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"yarn": {
|
||||||
|
"version": "1.22.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.15.tgz",
|
||||||
|
"integrity": "sha512-AzoEDxj256BOS/jqDXA3pjyhmi4FRBBUMgYoTHI4EIt2EhREkvH0soPVEtnD+DQIJfU5R9bKhcZ1H9l8zPWeoA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,5 +24,8 @@
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/universityradioyork/bapsicle/issues"
|
"url": "https://github.com/universityradioyork/bapsicle/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/universityradioyork/bapsicle#readme"
|
"homepage": "https://github.com/universityradioyork/bapsicle#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"yarn": "^1.22.15"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,11 @@
|
||||||
|
|
||||||
# Stop the Pygame Hello message.
|
# Stop the Pygame Hello message.
|
||||||
import os
|
import os
|
||||||
|
|
||||||
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
||||||
|
from helpers.os_environment import isLinux
|
||||||
|
# It's the only one we could get to work.
|
||||||
|
if isLinux():
|
||||||
|
os.putenv('SDL_AUDIODRIVER', 'pulseaudio')
|
||||||
|
|
||||||
from queue import Empty
|
from queue import Empty
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
|
@ -23,7 +23,7 @@ import json
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from helpers.os_environment import isMacOS
|
from helpers.os_environment import isLinux, isMacOS
|
||||||
|
|
||||||
if not isMacOS():
|
if not isMacOS():
|
||||||
# Rip, this doesn't like threading on MacOS.
|
# Rip, this doesn't like threading on MacOS.
|
||||||
|
@ -206,7 +206,9 @@ class BAPSicleServer:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def startServer(self):
|
def startServer(self):
|
||||||
if isMacOS():
|
# On MacOS, the default causes something to keep creating new processes.
|
||||||
|
# On Linux, this is needed to make pulseaudio initiate properly.
|
||||||
|
if isMacOS() or isLinux():
|
||||||
multiprocessing.set_start_method("spawn", True)
|
multiprocessing.set_start_method("spawn", True)
|
||||||
|
|
||||||
process_title = "startServer"
|
process_title = "startServer"
|
||||||
|
|
|
@ -33,6 +33,26 @@ Set for:
|
||||||
Default Audio Output
|
Default Audio Output
|
||||||
</code>
|
</code>
|
||||||
</p>
|
</p>
|
||||||
|
{% if data.sdl_direct %}
|
||||||
|
Linux (Pulse Audio)
|
||||||
|
<br>
|
||||||
|
<code>
|
||||||
|
{% for output in data.outputs %}
|
||||||
|
Set for:
|
||||||
|
{% for channel in data.channels %}
|
||||||
|
{% if not channel %}
|
||||||
|
Player {{loop.index0}}
|
||||||
|
{% elif channel.output == output %}
|
||||||
|
<strong>Player {{channel.channel}}</strong>
|
||||||
|
{% else %}
|
||||||
|
<a href="/player/{{channel.channel}}/output/{{output}}">Player {{channel.channel}}</a>
|
||||||
|
{% endif %}
|
||||||
|
/
|
||||||
|
{% endfor %}
|
||||||
|
{% if output %}{{output}}{% else %}System Default Output{% endif %}<br>
|
||||||
|
{% endfor %}
|
||||||
|
</code>
|
||||||
|
{% else %}
|
||||||
{% for host_api in data.outputs %}
|
{% for host_api in data.outputs %}
|
||||||
{{host_api.name}}
|
{{host_api.name}}
|
||||||
<br>
|
<br>
|
||||||
|
@ -54,4 +74,5 @@ Default Audio Output
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</code>
|
</code>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from helpers.os_environment import (
|
from helpers.os_environment import (
|
||||||
|
isLinux,
|
||||||
resolve_external_file_path,
|
resolve_external_file_path,
|
||||||
resolve_local_file_path,
|
resolve_local_file_path,
|
||||||
)
|
)
|
||||||
|
@ -171,11 +172,16 @@ def ui_config_player(request):
|
||||||
for i in range(server_state.get()["num_channels"]):
|
for i in range(server_state.get()["num_channels"]):
|
||||||
channel_states.append(status(i))
|
channel_states.append(status(i))
|
||||||
|
|
||||||
outputs = DeviceManager.getAudioOutputs()
|
outputs = None
|
||||||
|
if isLinux():
|
||||||
|
outputs = DeviceManager.getAudioDevices()
|
||||||
|
else:
|
||||||
|
outputs = DeviceManager.getAudioOutputs()
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"channels": channel_states,
|
"channels": channel_states,
|
||||||
"outputs": outputs,
|
"outputs": outputs,
|
||||||
|
"sdl_direct": isLinux(),
|
||||||
"ui_page": "config",
|
"ui_page": "config",
|
||||||
"ui_title": "Player Config",
|
"ui_title": "Player Config",
|
||||||
}
|
}
|
||||||
|
@ -545,9 +551,12 @@ def WebServer(player_to: List[Queue], player_from: List[Queue], state: StateMana
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
break
|
break
|
||||||
loop = asyncio.get_event_loop()
|
try:
|
||||||
if loop:
|
loop = asyncio.get_event_loop()
|
||||||
loop.close()
|
if loop:
|
||||||
if app:
|
loop.close()
|
||||||
app.stop()
|
if app:
|
||||||
del app
|
app.stop()
|
||||||
|
del app
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
Loading…
Reference in a new issue