drop PyAudio support

I do not recall anyone reporting that it worked better than the Qt
implementation for them, and the lack of recent wheels on PyPI is a pain.
We can always add it back in the future if enough people come out of
the woodwork to report they were using it.
This commit is contained in:
Damien Elmes 2021-10-11 18:23:38 +10:00
parent caa76c8b96
commit 52642d693b
6 changed files with 8 additions and 175 deletions

View File

@ -107,13 +107,6 @@
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="recording_driver">
<property name="currentText">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="search_text_label">
<property name="text">
@ -616,7 +609,6 @@
<tabstop>paste_strips_formatting</tabstop>
<tabstop>nightMode</tabstop>
<tabstop>useCurrent</tabstop>
<tabstop>recording_driver</tabstop>
<tabstop>default_search_text</tabstop>
<tabstop>uiScale</tabstop>
<tabstop>showEstimates</tabstop>

View File

@ -33,7 +33,6 @@ except:
import PyQt5.QtSvg # type: ignore
import PyQt5.QtMultimedia # type: ignore
import socks
import pyaudio
# legacy compat
import anki.storage

View File

@ -9,7 +9,7 @@ from anki.collection import OpChanges
from anki.consts import newCardSchedulingLabels
from aqt import AnkiQt
from aqt.operations.collection import set_preferences
from aqt.profiles import RecordingDriver, VideoDriver
from aqt.profiles import VideoDriver
from aqt.qt import *
from aqt.utils import HelpPage, disable_help_button, openHelp, showInfo, showWarning, tr
@ -134,48 +134,13 @@ class Preferences(QDialog):
def setup_profile(self) -> None:
"Setup options stored in the user profile."
self.setup_recording_driver()
self.setup_network()
self.setup_backup()
def update_profile(self) -> None:
self.update_recording_driver()
self.update_network()
self.update_backup()
# Profile: recording driver
######################################################################
def setup_recording_driver(self) -> None:
self._recording_drivers = [
RecordingDriver.QtAudioInput,
RecordingDriver.PyAudio,
]
# The plan is to phase out PyAudio soon, so will hold off on
# making this string translatable for now.
self.form.recording_driver.addItems(
[
f"Voice recording driver: {driver.value}"
for driver in self._recording_drivers
]
)
self.form.recording_driver.setCurrentIndex(
self._recording_drivers.index(self.mw.pm.recording_driver())
)
def update_recording_driver(self) -> None:
new_audio_driver = self._recording_drivers[
self.form.recording_driver.currentIndex()
]
if self.mw.pm.recording_driver() != new_audio_driver:
self.mw.pm.set_recording_driver(new_audio_driver)
if new_audio_driver == RecordingDriver.PyAudio:
showInfo(
"""\
The PyAudio driver will likely be removed in a future update. If you find it works better \
for you than the default driver, please let us know on the Anki forums."""
)
# Profile: network
######################################################################

View File

@ -31,11 +31,6 @@ from aqt.utils import disable_help_button, showWarning, tr
# - Saves in sqlite rather than a flat file so the config can't be corrupted
class RecordingDriver(Enum):
PyAudio = "PyAudio"
QtAudioInput = "Qt"
class VideoDriver(Enum):
OpenGL = "auto"
ANGLE = "angle"
@ -556,18 +551,6 @@ create table if not exists profiles
def set_auto_sync_media_minutes(self, val: int) -> None:
self.profile["autoSyncMediaMinutes"] = val
def recording_driver(self) -> RecordingDriver:
if driver := self.profile.get("recordingDriver"):
try:
return RecordingDriver(driver)
except ValueError:
# revert to default
pass
return RecordingDriver.QtAudioInput
def set_recording_driver(self, driver: RecordingDriver) -> None:
self.profile["recordingDriver"] = driver.value
def show_browser_table_tooltips(self) -> bool:
return self.profile.get("browserTableTooltips", True)

View File

@ -96,9 +96,3 @@ class QtAudioInputRecorder(Recorder):
t.timeout.connect(on_stop_timer) # type: ignore
t.setSingleShot(True)
t.start(500)
def prompt_for_mic_permission() -> None:
from PyQt5.QtMultimedia import QAudioDeviceInfo
QAudioDeviceInfo.defaultInputDevice()

View File

@ -9,7 +9,6 @@ import platform
import re
import subprocess
import sys
import threading
import time
import wave
from abc import ABC, abstractmethod
@ -23,11 +22,9 @@ import aqt
from anki import hooks
from anki.cards import Card
from anki.sound import AV_REF_RE, AVTag, SoundOrVideoTag
from anki.types import assert_exhaustive
from anki.utils import isLin, isMac, isWin, namedtmp
from aqt import gui_hooks
from aqt.mpv import MPV, MPVBase, MPVCommandError
from aqt.profiles import RecordingDriver
from aqt.qt import *
from aqt.taskman import TaskManager
from aqt.utils import (
@ -546,16 +543,6 @@ class Recorder(ABC):
##########################################################################
def prompt_for_mic_permission() -> None:
if qtmajor == 5 and isMac:
from .qt5 import prompt_for_mic_permission
prompt_for_mic_permission()
else:
# no longer seems to be required, perhaps due to newer macOS sdk?
pass
class QtAudioInputRecorder(Recorder):
def __init__(self, output_path: str, mw: aqt.AnkiQt, parent: QWidget) -> None:
super().__init__(output_path)
@ -625,87 +612,6 @@ class QtAudioInputRecorder(Recorder):
t.start(500)
# PyAudio recording
##########################################################################
try:
import pyaudio
except:
pyaudio = None
PYAU_CHANNELS = 1
PYAU_INPUT_INDEX: int | None = None
class PyAudioThreadedRecorder(threading.Thread):
def __init__(self, output_path: str, startup_delay: float) -> None:
threading.Thread.__init__(self)
self._output_path = output_path
self._startup_delay = startup_delay
self.finish = False
# though we're using pyaudio here, we rely on Qt to trigger
# the permission prompt
prompt_for_mic_permission()
def run(self) -> None:
chunk = 1024
p = pyaudio.PyAudio()
rate = int(p.get_default_input_device_info()["defaultSampleRate"])
PYAU_FORMAT = pyaudio.paInt16
stream = p.open(
format=PYAU_FORMAT,
channels=PYAU_CHANNELS,
rate=rate,
input=True,
input_device_index=PYAU_INPUT_INDEX,
frames_per_buffer=chunk,
)
# swallow the first 300ms to allow audio device to quiesce
wait = int(rate * self._startup_delay)
stream.read(wait, exception_on_overflow=False)
# read data in a loop until self.finish is set
data = b""
while not self.finish:
data += stream.read(chunk, exception_on_overflow=False)
# write out the wave file
stream.close()
p.terminate()
wf = wave.open(self._output_path, "wb")
wf.setnchannels(PYAU_CHANNELS)
wf.setsampwidth(p.get_sample_size(PYAU_FORMAT))
wf.setframerate(rate)
wf.writeframes(data)
wf.close()
class PyAudioRecorder(Recorder):
def __init__(self, mw: aqt.AnkiQt, output_path: str) -> None:
super().__init__(output_path)
self.mw = mw
def start(self, on_done: Callable[[], None]) -> None:
self.thread = PyAudioThreadedRecorder(self.output_path, self.STARTUP_DELAY)
self.thread.start()
super().start(on_done)
def stop(self, on_done: Callable[[str], None]) -> None:
# ensure at least a second captured
while self.duration() < 1:
time.sleep(0.1)
def func(fut: Future) -> None:
Recorder.stop(self, on_done)
self.thread.finish = True
self.mw.taskman.run_in_background(self.thread.join, func)
# Recording dialog
##########################################################################
@ -760,10 +666,6 @@ class RecordDialog(QDialog):
saveGeom(self, "audioRecorder2")
def _start_recording(self) -> None:
driver = self.mw.pm.recording_driver()
if driver is RecordingDriver.PyAudio:
self._recorder = PyAudioRecorder(self.mw, namedtmp("rec.wav"))
elif driver is RecordingDriver.QtAudioInput:
if qtmajor > 5:
self._recorder = QtAudioInputRecorder(
namedtmp("rec.wav"), self.mw, self._parent
@ -772,8 +674,6 @@ class RecordDialog(QDialog):
from .qt5 import QtAudioInputRecorder as Qt5Recorder
self._recorder = Qt5Recorder(namedtmp("rec.wav"), self.mw, self._parent)
else:
assert_exhaustive(driver)
self._recorder.start(self._start_timer)
def _start_timer(self) -> None: