mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 06:52:21 -04:00
recording & noise profile support on linux
This commit is contained in:
parent
f515a6c5f9
commit
334d126237
6 changed files with 107 additions and 22 deletions
10
anki/deck.py
10
anki/deck.py
|
@ -1453,8 +1453,8 @@ where id = :id""", pending)
|
|||
# Progress info
|
||||
##########################################################################
|
||||
|
||||
def startProgress(self, title, min, max):
|
||||
runHook("startProgress", title, min, max)
|
||||
def startProgress(self, max=100, min=0, title=None):
|
||||
runHook("startProgress", max, min, title)
|
||||
|
||||
def updateProgress(self, label=None, value=None):
|
||||
runHook("updateProgress", label, value)
|
||||
|
@ -1641,7 +1641,7 @@ Return new path, relative to media dir."""
|
|||
|
||||
def fixIntegrity(self):
|
||||
"Responsibility of caller to call rebuildQueue()"
|
||||
self.startProgress(_("Check DB"), 0, 11)
|
||||
self.startProgress(11)
|
||||
self.updateProgress(_("Checking integrity..."))
|
||||
if self.s.scalar("pragma integrity_check") != "ok":
|
||||
return _("Database file damaged. Restore from backup.")
|
||||
|
@ -1873,8 +1873,8 @@ select sql from undoLog where
|
|||
seq > :s and seq <= :e order by seq desc""", s=start, e=end)
|
||||
mod = len(sql) / 35
|
||||
if mod:
|
||||
self.startProgress(_("Undo/Redo"), 0, 36)
|
||||
self.updateProgress(_("Applying changes..."))
|
||||
self.startProgress(36)
|
||||
self.updateProgress(_("Processing..."))
|
||||
newstart = self._latestUndoRow()
|
||||
for c, s in enumerate(sql):
|
||||
if mod and not c % mod:
|
||||
|
|
|
@ -59,11 +59,11 @@ class AnkiExporter(Exporter):
|
|||
self.includeSchedulingInfo = False
|
||||
|
||||
def exportInto(self, path):
|
||||
n = 4
|
||||
n = 3
|
||||
if not self.includeSchedulingInfo:
|
||||
n += 1
|
||||
self.deck.startProgress(_("Export"), 0, n)
|
||||
self.deck.updateProgress(_("Determining items..."))
|
||||
self.deck.startProgress(n)
|
||||
self.deck.updateProgress(_("Exporting..."))
|
||||
self.newDeck = DeckStorage.Deck(path)
|
||||
client = SyncClient(self.deck)
|
||||
server = SyncServer(self.newDeck)
|
||||
|
@ -74,12 +74,12 @@ class AnkiExporter(Exporter):
|
|||
# set up a custom change list and sync
|
||||
lsum = self.localSummary()
|
||||
rsum = server.summary(0)
|
||||
self.deck.updateProgress(_("Copying..."))
|
||||
self.deck.updateProgress()
|
||||
payload = client.genPayload((lsum, rsum))
|
||||
self.deck.updateProgress(_("Applying..."))
|
||||
self.deck.updateProgress()
|
||||
res = server.applyPayload(payload)
|
||||
if not self.includeSchedulingInfo:
|
||||
self.deck.updateProgress(_("Updating schedule..."))
|
||||
self.deck.updateProgress()
|
||||
self.newDeck.s.statement("""
|
||||
delete from reviewHistory""")
|
||||
self.newDeck.s.statement("""
|
||||
|
@ -121,7 +121,6 @@ delete from stats""")
|
|||
bulkClient.server = bulkServer
|
||||
bulkClient.sync()
|
||||
# need to save manually
|
||||
self.deck.updateProgress(_("Finalizing..."))
|
||||
self.newDeck.rebuildCounts()
|
||||
self.exportedCards = self.newDeck.cardCount
|
||||
self.newDeck.s.commit()
|
||||
|
|
|
@ -46,11 +46,11 @@ class Importer(object):
|
|||
|
||||
def doImport(self):
|
||||
"Import."
|
||||
self.deck.startProgress(_("Import"), 0, 6)
|
||||
self.deck.updateProgress(_("Reading source..."))
|
||||
self.deck.startProgress(6)
|
||||
self.deck.updateProgress(_("Importing..."))
|
||||
c = self.foreignCards()
|
||||
self.importCards(c)
|
||||
self.deck.updateProgress(_("Updating priorities..."))
|
||||
self.deck.updateProgress()
|
||||
self.deck.updateAllPriorities()
|
||||
self.deck.finishProgress()
|
||||
if c:
|
||||
|
@ -123,7 +123,7 @@ all but one card template."""))
|
|||
def addCards(self, cards):
|
||||
"Add facts in bulk from foreign cards."
|
||||
# add facts
|
||||
self.deck.updateProgress(_("Adding facts..."))
|
||||
self.deck.updateProgress()
|
||||
factIds = [genID() for n in range(len(cards))]
|
||||
self.deck.s.execute(factsTable.insert(),
|
||||
[{'modelId': self.model.id,
|
||||
|
@ -134,7 +134,7 @@ all but one card template."""))
|
|||
delete from factsDeleted
|
||||
where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
||||
# add all the fields
|
||||
self.deck.updateProgress(_("Adding fields..."))
|
||||
self.deck.updateProgress()
|
||||
for fm in self.model.fieldModels:
|
||||
try:
|
||||
index = self.mapping.index(fm)
|
||||
|
@ -150,7 +150,7 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
|||
self.deck.s.execute(fieldsTable.insert(),
|
||||
data)
|
||||
# and cards
|
||||
self.deck.updateProgress(_("Adding cards..."))
|
||||
self.deck.updateProgress()
|
||||
now = time.time()
|
||||
for cm in self.model.cardModels:
|
||||
self._now = now
|
||||
|
@ -165,7 +165,7 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
|||
'type': 2},cards[m]) for m in range(len(cards))]
|
||||
self.deck.s.execute(cardsTable.insert(),
|
||||
data)
|
||||
self.deck.updateProgress(_("Caching QA..."))
|
||||
self.deck.updateProgress()
|
||||
self.deck.updateCardsFromModel(self.model)
|
||||
self.deck.cardCount += len(cards)
|
||||
self.total = len(factIds)
|
||||
|
|
|
@ -18,7 +18,7 @@ class Anki10Importer(Importer):
|
|||
|
||||
def doImport(self):
|
||||
"Import."
|
||||
self.deck.startProgress(_("Import"), 0, 4)
|
||||
self.deck.startProgress(4)
|
||||
self.deck.updateProgress(_("Importing..."))
|
||||
src = DeckStorage.Deck(self.file)
|
||||
client = SyncClient(self.deck)
|
||||
|
|
|
@ -119,7 +119,7 @@ def rebuildMediaDir(deck, deleteRefs=False, dirty=True):
|
|||
unusedFileCount = 0
|
||||
missingFileCount = 0
|
||||
deck.mediaDir(create=True)
|
||||
deck.startProgress(_("Check Media DB"), 0, 16)
|
||||
deck.startProgress(16, 0, _("Check Media DB"))
|
||||
# rename all files to checksum versions, note non-renamed ones
|
||||
deck.updateProgress(_("Checksum files..."))
|
||||
files = os.listdir(unicode(deck.mediaDir()))
|
||||
|
|
|
@ -8,7 +8,7 @@ Sound support
|
|||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
import re, sys, threading, time, subprocess, os
|
||||
import re, sys, threading, time, subprocess, os, signal
|
||||
|
||||
# Shared utils
|
||||
##########################################################################
|
||||
|
@ -26,6 +26,30 @@ def hasSound(text):
|
|||
# External audio
|
||||
##########################################################################
|
||||
|
||||
# the amount of noise to cancel
|
||||
NOISE_AMOUNT = "0.1"
|
||||
# the amount of amplification
|
||||
NORM_AMOUNT = "-3"
|
||||
# the amount of bass
|
||||
BASS_AMOUNT = "+0"
|
||||
# the amount to fade at end
|
||||
FADE_AMOUNT = "0.2"
|
||||
|
||||
noiseProfile = ""
|
||||
|
||||
processingSrc = "tmp.wav"
|
||||
processingDst = "tmp.mp3"
|
||||
processingChain = []
|
||||
tmpFiles = ["tmp2.wav", "tmp3.wav"]
|
||||
|
||||
cmd = ["sox", processingSrc, "tmp2.wav"]
|
||||
processingChain = [
|
||||
None, # placeholder
|
||||
["sox", "tmp2.wav", "tmp3.wav", "norm", NORM_AMOUNT,
|
||||
"bass", BASS_AMOUNT, "fade", FADE_AMOUNT, "0"],
|
||||
["lame", "tmp3.wav", processingDst, "--noreplaygain"],
|
||||
]
|
||||
|
||||
queue = []
|
||||
manager = None
|
||||
|
||||
|
@ -33,8 +57,11 @@ if sys.platform.startswith("win32"):
|
|||
base = os.path.join(os.path.dirname(sys.argv[0]), "mplayer.exe")
|
||||
#base = "C:\mplayer.exe"
|
||||
externalPlayer = [base, "-ao", "win32", "-really-quiet"]
|
||||
externalRecorder = ["rec", processingSrc]
|
||||
else:
|
||||
externalPlayer = ["mplayer", "-really-quiet"]
|
||||
externalRecorder = ["ecasound", "-x", "-f:16,1,44100", "-i",
|
||||
"alsahw,1,0", "-o", processingSrc]
|
||||
|
||||
# don't show box on windows
|
||||
if sys.platform == "win32":
|
||||
|
@ -43,6 +70,29 @@ if sys.platform == "win32":
|
|||
else:
|
||||
si = None
|
||||
|
||||
# noise profiles
|
||||
##########################################################################
|
||||
|
||||
def checkForNoiseProfile():
|
||||
cmd = ["sox", processingSrc, "tmp2.wav"]
|
||||
if os.path.exists(noiseProfile):
|
||||
cmd = cmd + ["noisered", noiseProfile, NOISE_AMOUNT]
|
||||
processingChain[0] = cmd
|
||||
|
||||
def generateNoiseProfile(file):
|
||||
try:
|
||||
os.unlink(noiseProfile)
|
||||
except OSError:
|
||||
pass
|
||||
subprocess.Popen(["sox", processingSrc, tmpFiles[0], "trim", "1.5", "1.5"])
|
||||
subprocess.Popen(["sox", tmpFiles[0], tmpFiles[1],
|
||||
"noiseprof", noiseProfile]).wait()
|
||||
processingChain[0] = ["sox", processingSrc, "tmp2.wav",
|
||||
"noisered", noiseProfile, NOISE_AMOUNT]
|
||||
|
||||
# External playing
|
||||
##########################################################################
|
||||
|
||||
class QueueMonitor(threading.Thread):
|
||||
|
||||
def run(self):
|
||||
|
@ -68,6 +118,37 @@ def clearQueueExternal():
|
|||
global queue
|
||||
queue = []
|
||||
|
||||
# External recording
|
||||
##########################################################################
|
||||
|
||||
class _Recorder(object):
|
||||
|
||||
def postprocess(self):
|
||||
for c in processingChain:
|
||||
print c
|
||||
if subprocess.Popen(c, startupinfo=si).wait():
|
||||
raise Exception("problem with" + str(c))
|
||||
|
||||
class ExternalUnixRecorder(_Recorder):
|
||||
|
||||
def __init__(self):
|
||||
for t in tmpFiles + [processingSrc, processingDst]:
|
||||
try:
|
||||
os.unlink(t)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
self.proc = subprocess.Popen(
|
||||
externalRecorder, startupinfo=si)
|
||||
|
||||
def stop(self):
|
||||
os.kill(self.proc.pid, signal.SIGINT)
|
||||
self.proc.wait()
|
||||
|
||||
def file(self):
|
||||
return processingDst
|
||||
|
||||
# Mac audio support
|
||||
##########################################################################
|
||||
|
||||
|
@ -124,6 +205,11 @@ except ImportError:
|
|||
if sys.platform.startswith("darwin"):
|
||||
play = playOSX
|
||||
clearAudioQueue = clearQueueOSX
|
||||
Recorder = None
|
||||
else:
|
||||
play = playExternal
|
||||
clearAudioQueue = clearQueueExternal
|
||||
if sys.platform.startswith("win32"):
|
||||
Recorder = None
|
||||
else:
|
||||
Recorder = ExternalUnixRecorder
|
||||
|
|
Loading…
Reference in a new issue