build.rsudp.c_alertsound   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 143
Duplicated Lines 0 %

Test Coverage

Coverage 71.43%

Importance

Changes 0
Metric Value
wmc 20
eloc 89
dl 0
loc 143
ccs 60
cts 84
cp 0.7143
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A AlertSound._load_sound() 0 20 3
A AlertSound._play_quiet() 0 7 1
A AlertSound._play() 0 8 3
A AlertSound._write_wav() 0 10 2
B AlertSound.run() 0 16 6
A AlertSound.__init__() 0 28 2
A AlertSound._init_sound() 0 14 3
1 1
import sys, os
2 1
from rsudp.raspberryshake import ConsumerThread
3 1
from rsudp import printM, printW, printE
4 1
from rsudp.test import TEST
5 1
import subprocess
6 1
try:
7 1
	from pydub.playback import play
8 1
	from pydub import AudioSegment, utils
9 1
	pydub_exists = True
10
	# avoids import error that arises between pydub 0.23.1 and 0.25.1
11
	global PLAYER
12 1
	PLAYER = utils.get_player_name()
13 1
	TEST['d_pydub'][1] = True
14
except ImportError as e:
15
	global ERR
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ERR does not seem to be defined.
Loading history...
16
	ERR = e
17
	pydub_exists = False
18
19
20 1
class AlertSound(ConsumerThread):
21
	"""
22
	.. _pydub.AudioSegment: https://github.com/jiaaro/pydub/blob/master/API.markdown#audiosegment
23
24
	A consumer class that plays an alert sound when an ``ALARM`` message arrives on the queue.
25
	``rsudp.c_alertsound.AlertSound.sound`` is a pydub.AudioSegment_ object and is passed from the client.
26
27
	:param sta: short term average (STA) duration in seconds.
28
	:type sta: bool or pydub.AudioSegment_ 
29
	:param queue.Queue q: queue of data and messages sent by :class:`rsudp.c_consumer.Consumer`.
30
31
	"""
32
33 1
	def _load_sound(self):
34
		'''
35
		Loads MP3 sound if possible, then writes to wav.
36
		Catches a ``FileNotFoundError`` when no player can be loaded.
37
		'''
38 1
		try:
39 1
			soundloc = self.sound
40 1
			self.sound = AudioSegment.from_file(self.sound, format="mp3")
41 1
			printM('Loaded %.2f sec alert sound from %s' % (len(self.sound)/1000., soundloc), sender=self.sender)
42 1
			self.wavloc = '%s.wav' % os.path.splitext(soundloc)[0]
43 1
			if 'ffplay' in PLAYER:
44 1
				self._write_wav()
45
		except FileNotFoundError as e:
46
			printE('Error loading player - %s' % (e), sender=self.sender)
47
			printW("You have chosen to play a sound, but don't have ffmpeg or libav installed.", sender=self.sender)
48
			printW('Sound playback requires one of these dependencies.', sender=self.sender, spaces=True)
49
			printW("To install either dependency, follow the instructions at:", sender=self.sender, spaces=True)
50
			printW('https://github.com/jiaaro/pydub#playback', sender=self.sender, spaces=True)
51
			printW('The program will now continue without sound playback.', sender=self.sender, spaces=True)
52
			self.sound = False
53
54 1
	def _init_sound(self):
55 1
		if pydub_exists:
56 1
			if os.path.exists(self.sound):
57 1
				self._load_sound()
58
			else:
59
				printW("The file %s could not be found." % (self.sound), sender=self.sender)
60
				printW('The program will now continue without sound playback.', sender=self.sender, spaces=True)
61
				self.sound = False
62
		else:
63
			printE('Error importing pydub - %s' % ERR, sender=self.sender)
0 ignored issues
show
introduced by
The variable ERR does not seem to be defined for all execution paths.
Loading history...
64
			printW("You don't have pydub installed, so no sound will play.", sender=self.sender)
65
			printW('To install pydub, follow the instructions at:', sender=self.sender, spaces=True)
66
			printW('https://github.com/jiaaro/pydub#installation', sender=self.sender, spaces=True)
67
			printW('Sound playback also requires you to install either ffmpeg or libav.', sender=self.sender, spaces=True)
68
69 1
	def _write_wav(self):
70
		'''
71
		FFPlay can only play raw wav sounds without verbosity, so to support
72
		non-verbose mode we must export to .wav prior to playing a sound.
73
		This function checks for an existing wav file and if it does not
74
		exist, writes a new one.
75
		'''
76 1
		if not os.path.isfile(self.wavloc):
77 1
			self.sound.export(self.wavloc, format="wav")
78 1
			printM('Wrote wav version of sound file %s' % (self.wavloc), self.sender)
79
80
81 1
	def __init__(self, testing=False, soundloc=False, q=False):
82
		"""
83
		.. _pydub.AudioSegment: https://github.com/jiaaro/pydub/blob/master/API.markdown#audiosegment
84
85
		Initializes the alert sound listener thread.
86
		Needs a pydub.AudioSegment_ to play and a :class:`queue.Queue` to listen on.
87
88
		"""
89 1
		super().__init__()
90 1
		self.sender = 'AlertSound'
91 1
		self.alive = True
92 1
		self.testing = testing
93
94 1
		self.sound = soundloc
95 1
		self.devnull = open(os.devnull, 'w')
96
		
97 1
		self._init_sound()
98
99 1
		if q:
100 1
			self.queue = q
101
		else:
102
			printE('no queue passed to the consumer thread! We will exit now!',
103
				   self.sender)
104
			sys.stdout.flush()
105
			self.alive = False
106
			sys.exit()
107
108 1
		printM('Starting.', self.sender)
109
110 1
	def _play_quiet(self):
111
		'''
112
		if FFPlay is the player, suppress printed output.
113
		'''
114 1
		self._write_wav()
115 1
		subprocess.call([PLAYER,"-nodisp", "-autoexit", "-hide_banner",
116
						self.wavloc], stdout=self.devnull, stderr=self.devnull)
117
118 1
	def _play(self):
119 1
		printM('Playing alert sound...', sender=self.sender)
120 1
		if 'ffplay' in PLAYER:
121 1
			self._play_quiet()
122
		else:
123
			play(self.sound)
124 1
		if self.testing:
125 1
			TEST['c_play'][1] = True
126
127 1
	def run(self):
128
		"""
129
		Reads data from the queue and plays self.sound if it sees an ``ALARM`` message.
130
		Quits if it sees a ``TERM`` message.
131
		"""
132 1
		while True:
133 1
			d = self.queue.get()
134 1
			self.queue.task_done()
135 1
			if 'TERM' in str(d):
136 1
				self.alive = False
137 1
				self.devnull.close()
138 1
				printM('Exiting.', self.sender)
139 1
				sys.exit()
140 1
			elif 'ALARM' in str(d):
141 1
				if self.sound and pydub_exists:
142
					self._play()
143