forked from auracaster/pyalsaaudio
Restarting the capture device looks ok
This commit is contained in:
@@ -7,6 +7,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import struct
|
import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import errno
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from alsaaudio import (PCM, pcms, PCM_PLAYBACK, PCM_CAPTURE, PCM_NONBLOCK, Mixer,
|
from alsaaudio import (PCM, pcms, PCM_PLAYBACK, PCM_CAPTURE, PCM_NONBLOCK, Mixer,
|
||||||
@@ -98,9 +99,6 @@ class Loopback(object):
|
|||||||
self.silence_start = None
|
self.silence_start = None
|
||||||
self.reactor = None
|
self.reactor = None
|
||||||
|
|
||||||
self.queue = []
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_energy(data):
|
def compute_energy(data):
|
||||||
values = struct.unpack(f'{len(data)//2}h', data)
|
values = struct.unpack(f'{len(data)//2}h', data)
|
||||||
@@ -130,7 +128,8 @@ class Loopback(object):
|
|||||||
# start reading data
|
# start reading data
|
||||||
size, data = self.capture.read()
|
size, data = self.capture.read()
|
||||||
if size:
|
if size:
|
||||||
self.queue.append(data)
|
logging.warning(f'initial data discarded ({size} bytes)')
|
||||||
|
# self.queue.append(data)
|
||||||
|
|
||||||
self.state = LoopbackState.LISTENING
|
self.state = LoopbackState.LISTENING
|
||||||
|
|
||||||
@@ -164,7 +163,7 @@ class Loopback(object):
|
|||||||
period_size = self.playback.info()['period_size']
|
period_size = self.playback.info()['period_size']
|
||||||
logging.info(f'opened playback device with period_size {period_size}')
|
logging.info(f'opened playback device with period_size {period_size}')
|
||||||
except ALSAAudioError as e:
|
except ALSAAudioError as e:
|
||||||
logging.info('opening PCM playback device failed: %s', e)
|
logging.warning('opening PCM playback device failed: %s', e)
|
||||||
self.state = LoopbackState.DEVICE_BUSY
|
self.state = LoopbackState.DEVICE_BUSY
|
||||||
return self.state
|
return self.state
|
||||||
elif self.state == LoopbackState.DEVICE_BUSY:
|
elif self.state == LoopbackState.DEVICE_BUSY:
|
||||||
@@ -196,12 +195,12 @@ class Loopback(object):
|
|||||||
logging.warning(f'POLLERR for capture - reopening capture device: {state_names[self.capture.state()]}')
|
logging.warning(f'POLLERR for capture - reopening capture device: {state_names[self.capture.state()]}')
|
||||||
|
|
||||||
self.reactor.unregister(self.capture_pd)
|
self.reactor.unregister(self.capture_pd)
|
||||||
|
self.capture.drop()
|
||||||
self.capture.close()
|
self.capture.close()
|
||||||
|
|
||||||
self.capture = PCM(**self.capture_args)
|
self.capture = PCM(**self.capture_args)
|
||||||
self.capture_pd = PollDescriptor.from_alsa_object('capture', self.capture)
|
self.capture_pd = PollDescriptor.from_alsa_object('capture', self.capture)
|
||||||
self.reactor.register(self.capture_pd, self)
|
self.reactor.register(self.capture_pd, self)
|
||||||
return True
|
|
||||||
|
|
||||||
'''called when data is available for reading'''
|
'''called when data is available for reading'''
|
||||||
self.last_capture_event = datetime.now()
|
self.last_capture_event = datetime.now()
|
||||||
@@ -213,7 +212,6 @@ class Loopback(object):
|
|||||||
# the usecase is a USB capture device where we get perfect silence when it's idle
|
# the usecase is a USB capture device where we get perfect silence when it's idle
|
||||||
# compute the energy and go back to LISTENING if nothing is captured
|
# compute the energy and go back to LISTENING if nothing is captured
|
||||||
energy = self.compute_energy(data)
|
energy = self.compute_energy(data)
|
||||||
logging.debug(f'energy: {energy}')
|
|
||||||
if energy == 0:
|
if energy == 0:
|
||||||
if self.silence_start is None:
|
if self.silence_start is None:
|
||||||
self.silence_start = datetime.now()
|
self.silence_start = datetime.now()
|
||||||
@@ -228,22 +226,17 @@ class Loopback(object):
|
|||||||
if self.set_state(LoopbackState.PLAYING) != LoopbackState.PLAYING:
|
if self.set_state(LoopbackState.PLAYING) != LoopbackState.PLAYING:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.queue.append(data)
|
|
||||||
|
|
||||||
if len(self.queue) <= 2:
|
|
||||||
logging.info(f'buffering: {len(self.queue)}')
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = self.pop()
|
|
||||||
if data:
|
if data:
|
||||||
space = self.playback.avail()
|
space = self.playback.avail()
|
||||||
logging.debug(f'space: {space}')
|
|
||||||
if space > 0:
|
if space > 0:
|
||||||
written = self.playback.write(data)
|
written = self.playback.write(data)
|
||||||
logging.debug(f'wrote {written} bytes while space was {space}')
|
if written == -errno.EPIPE:
|
||||||
except ALSAAudioError:
|
logging.warning('playback underrun')
|
||||||
logging.error('underrun', exc_info=1)
|
self.playback.write(data)
|
||||||
|
silence = ''
|
||||||
|
if energy == 0:
|
||||||
|
silence = '(silence)'
|
||||||
|
logging.debug(f'wrote {written} bytes while space was {space} {silence}')
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -326,7 +319,7 @@ class Reactor(object):
|
|||||||
polldescriptor, handler = self.descriptors[fd]
|
polldescriptor, handler = self.descriptors[fd]
|
||||||
|
|
||||||
# very chatty - log all events
|
# very chatty - log all events
|
||||||
logging.debug(f'{polldescriptor.name}: {poll_desc(ev)} ({ev})')
|
# logging.debug(f'{polldescriptor.name}: {poll_desc(ev)} ({ev})')
|
||||||
|
|
||||||
handler(fd, ev, polldescriptor.name)
|
handler(fd, ev, polldescriptor.name)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user