PyDMXControl.audio._Player   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 18
eloc 69
dl 0
loc 118
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A Player.__play() 0 22 3
A Player.__init__() 0 16 1
A Player.__fade_volume() 0 10 3
A Player.__set_volume() 0 4 1
A Player.sleep_till_done() 0 4 2
A Player.stop() 0 2 1
A Player.unpause() 0 2 1
A Player.pause() 0 2 1
A Player.play() 0 4 1
A Player.sleep_till_interrupt() 0 11 3
A Player.set_volume() 0 6 1
1
"""
2
 *  PyDMXControl: A Python 3 module to control DMX using OpenDMX or uDMX.
3
 *                Featuring fixture profiles, built-in effects and a web control panel.
4
 *  <https://github.com/MattIPv4/PyDMXControl/>
5
 *  Copyright (C) 2018 Matt Cowley (MattIPv4) ([email protected])
6
"""
7
8
import contextlib
9
from threading import Thread
10
from time import sleep, time
11
12
with contextlib.redirect_stdout(None):  # PyGame please shut up
13
    import pygame
14
15
from ..utils.exceptions import AudioException
16
from .. import DEFAULT_INTERVAL
17
18
19
class Player:
20
21
    def __init__(self):
22
        # set up states
23
        self.__paused = False
24
        self.__done = True
25
26
        # set up mixer
27
        # https://www.daniweb.com/programming/software-development/threads/491663/how-to-open-and-play-mp3-file-in-python
28
        self.__freq = 44100  # audio CD quality
29
        self.__bitsize = -16  # unsigned 16 bit
30
        self.__channels = 2  # 1 is mono, 2 is stereo
31
        self.__buffer = 2048  # number of samples
32
        pygame.mixer.init(self.__freq, self.__bitsize, self.__channels, self.__buffer)
33
34
        # set up user preferences
35
        self.__volume = 0
36
        self.set_volume(0.8)
37
38
    def __set_volume(self, vol):
39
        vol = max(min(vol, 1), 0)  # clamp
40
        self.__volume = vol
41
        pygame.mixer.music.set_volume(vol)
42
43
    def __fade_volume(self, current, target, millis):
44
        start = time() * 1000.0
45
        gap = target - current
46
47
        if millis > 0:
48
            while (time() * 1000.0) - start <= millis:
49
                diff = gap * (((time() * 1000.0) - start) / millis)
50
                self.__set_volume(current + diff)
51
                sleep(0.000001)
52
        self.__set_volume(target)
53
54
    def set_volume(self, volume: float, milliseconds: int = 0):
55
        volume = max(min(volume, 1), 0)  # clamp
56
57
        # Create the thread and run loop
58
        thread = Thread(target=self.__fade_volume, args=(self.__volume, volume, milliseconds), daemon=True)
59
        thread.start()
60
61
    def __play(self, file, start_millis):
62
        # Stop anything previous
63
        self.stop()
64
65
        # Set states
66
        self.__paused = False
67
        self.__done = False
68
69
        # Get the file
70
        try:
71
            pygame.mixer.music.load(file)
72
        except pygame.error:
73
            self.__done = True
74
            raise AudioException("Error occurred loading '{}': {}".format(file, pygame.get_error()))
75
76
        # Play the file and tick until play completed
77
        pygame.mixer.music.play(start=start_millis / 1000)
78
        while pygame.mixer.music.get_busy():
79
            sleep(DEFAULT_INTERVAL)
80
81
        # Let everyone know play is done
82
        self.__done = True
83
84
    def play(self, file: str, start_millis: int = 0):
85
        # Play in the background so this isn't blocking
86
        thread = Thread(target=self.__play, args=[file, start_millis], daemon=True)
87
        thread.start()
88
89
    def pause(self):
90
        pygame.mixer.music.pause()
91
92
    def unpause(self):
93
        pygame.mixer.music.unpause()
94
95
    def stop(self):
96
        pygame.mixer.music.stop()
97
98
    def sleep_till_done(self):
99
        # Hold until the play method sets done
100
        while not self.__done:
101
            sleep(DEFAULT_INTERVAL)
102
103
    def sleep_till_interrupt(self):
104
        # This is very useful for playing song, stopping at a specific point and getting the timestamp
105
        # Probably not very useful in production but useful in development
106
107
        # Hold
108
        try:
109
            while True:
110
                sleep(DEFAULT_INTERVAL)
111
        except KeyboardInterrupt:
112
            print(pygame.mixer.music.get_pos())
113
            self.stop()
114
115
116
# Player can only have one instance due to how pygame works
117
the_player = Player()
118