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
|
||||
|
||||
node_modules/
|
|
@ -7,7 +7,7 @@ build_branch="$(git branch --show-current)"
|
|||
echo "BUILD: str = \"$build_commit\"" > ../build.py
|
||||
echo "BRANCH: str = \"$build_branch\"" >> ../build.py
|
||||
|
||||
apt install libportaudio2
|
||||
sudo apt install libportaudio2
|
||||
|
||||
python3 -m venv ../venv
|
||||
source ../venv/bin/activate
|
||||
|
@ -19,6 +19,8 @@ pip3 install -e ../
|
|||
|
||||
python3 ./generate-build-exe-config.py
|
||||
|
||||
chmod +x output/BAPSicle
|
||||
|
||||
python3 ./build-exe.py
|
||||
|
||||
bash ./build-exe-pyinstaller-command.sh
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
wheel
|
||||
pygame==2.0.1
|
||||
pygame==2.0.2
|
||||
sanic==21.3.4
|
||||
sanic-Cors==1.0.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
|
||||
import sounddevice as sd
|
||||
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
|
||||
|
||||
if isWindows():
|
||||
|
@ -20,10 +27,10 @@ class DeviceManager:
|
|||
return host_api
|
||||
|
||||
@classmethod
|
||||
def _getAudioDevices(cls) -> sd.DeviceList:
|
||||
def _getSDAudioDevices(cls):
|
||||
# To update the list of devices
|
||||
# Sadly this doesn't work on MacOS.
|
||||
if not isMacOS():
|
||||
# Sadly this only works on Windows. Linux hangs, MacOS crashes.
|
||||
if isWindows():
|
||||
sd._terminate()
|
||||
sd._initialize()
|
||||
devices: sd.DeviceList = sd.query_devices()
|
||||
|
@ -31,11 +38,13 @@ class DeviceManager:
|
|||
|
||||
@classmethod
|
||||
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)):
|
||||
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
|
||||
else:
|
||||
host_apis[host_api_id]["usable"] = True
|
||||
|
@ -51,6 +60,15 @@ class DeviceManager:
|
|||
|
||||
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
|
||||
def getSerialPorts(cls) -> List[Optional[str]]:
|
||||
"""Lists serial port names
|
||||
|
|
12
package-lock.json
generated
12
package-lock.json
generated
|
@ -1,5 +1,13 @@
|
|||
{
|
||||
"name": "bapsicle",
|
||||
"version": "3.0.0",
|
||||
"lockfileVersion": 1
|
||||
"version": "3.1.0",
|
||||
"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": {
|
||||
"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.
|
||||
import os
|
||||
|
||||
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
|
||||
import multiprocessing
|
||||
|
|
|
@ -23,7 +23,7 @@ import json
|
|||
from setproctitle import setproctitle
|
||||
import psutil
|
||||
|
||||
from helpers.os_environment import isMacOS
|
||||
from helpers.os_environment import isLinux, isMacOS
|
||||
|
||||
if not isMacOS():
|
||||
# Rip, this doesn't like threading on MacOS.
|
||||
|
@ -206,7 +206,9 @@ class BAPSicleServer:
|
|||
time.sleep(1)
|
||||
|
||||
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)
|
||||
|
||||
process_title = "startServer"
|
||||
|
|
|
@ -33,6 +33,26 @@ Set for:
|
|||
Default Audio Output
|
||||
</code>
|
||||
</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 %}
|
||||
{{host_api.name}}
|
||||
<br>
|
||||
|
@ -54,4 +74,5 @@ Default Audio Output
|
|||
{% endfor %}
|
||||
</code>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -17,6 +17,7 @@ import json
|
|||
import os
|
||||
|
||||
from helpers.os_environment import (
|
||||
isLinux,
|
||||
resolve_external_file_path,
|
||||
resolve_local_file_path,
|
||||
)
|
||||
|
@ -171,11 +172,16 @@ def ui_config_player(request):
|
|||
for i in range(server_state.get()["num_channels"]):
|
||||
channel_states.append(status(i))
|
||||
|
||||
outputs = None
|
||||
if isLinux():
|
||||
outputs = DeviceManager.getAudioDevices()
|
||||
else:
|
||||
outputs = DeviceManager.getAudioOutputs()
|
||||
|
||||
data = {
|
||||
"channels": channel_states,
|
||||
"outputs": outputs,
|
||||
"sdl_direct": isLinux(),
|
||||
"ui_page": "config",
|
||||
"ui_title": "Player Config",
|
||||
}
|
||||
|
@ -545,9 +551,12 @@ def WebServer(player_to: List[Queue], player_from: List[Queue], state: StateMana
|
|||
)
|
||||
except Exception:
|
||||
break
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop:
|
||||
loop.close()
|
||||
if app:
|
||||
app.stop()
|
||||
del app
|
||||
except Exception:
|
||||
pass
|
||||
|
|
Loading…
Reference in a new issue