6 from transitions
.extensions
import HierarchicalMachine
as Machine
8 class MusicFile(Machine
):
9 def __init__(self
, filename
, lock
, channel_id
, name
= None, gain
= 1):
14 { 'name': 'loaded', 'children': ['stopped', 'playing', 'paused'] }
17 { 'trigger': 'load', 'source': 'initial', 'dest': 'loading'}
,
18 { 'trigger': 'fail', 'source': 'loading', 'dest': 'failed'}
,
19 { 'trigger': 'success', 'source': 'loading', 'dest': 'loaded_stopped'}
,
20 #{ 'trigger': 'play', 'source': 'loaded_stopped', 'dest': 'loaded_playing'},
21 #{ 'trigger': 'pause', 'source': 'loaded_playing', 'dest': 'loaded_paused'},
22 #{ 'trigger': 'stop', 'source': ['loaded_playing','loaded_paused'], 'dest': 'loaded_stopped'}
25 Machine
.__init
__(self
, states
=states
, transitions
=transitions
, initial
='initial')
27 self
.filename
= filename
28 self
.channel_id
= channel_id
29 self
.name
= name
or filename
33 self
.flag_paused
= False
34 threading
.Thread(name
= "MSMusicLoad", target
= self
.load
, kwargs
= {'lock': lock}
).start()
36 def on_enter_loading(self
, lock
=None):
39 print("Loading « {} »".format(self
.name
))
40 volume_factor
= 20 * math
.log10(self
.gain
)
41 audio_segment
= pydub
.AudioSegment
.from_file(self
.filename
).set_frame_rate(44100).apply_gain(volume_factor
)
42 self
.sound_duration
= audio_segment
.duration_seconds
43 self
.raw_data
= audio_segment
.raw_data
44 except Exception as e
:
45 print("failed to load « {} »: {}".format(self
.name
, e
))
46 self
.loading_error
= e
50 print("Loaded « {} »".format(self
.name
))
54 def check_is_loaded(self
):
55 return self
.state
.startswith('loaded_')
58 return self
.channel().get_busy()
61 return self
.flag_paused
64 def sound_position(self
):
65 if self
.is_playing() and not self
.is_paused():
66 return min(time
.time() - self
.started_at
, self
.sound_duration
)
67 elif self
.is_playing() and self
.is_paused():
68 return min(self
.paused_at
- self
.started_at
, self
.sound_duration
)
72 def play(self
, fade_in
= 0, volume
= 100, start_at
= 0):
73 self
.set_volume(volume
)
76 raw_data_length
= len(self
.raw_data
)
77 start_offset
= int((raw_data_length
/ self
.sound_duration
) * start_at
)
78 start_offset
= start_offset
- (start_offset
% 2)
79 sound
= pygame
.mixer
.Sound(self
.raw_data
[start_offset
:])
81 sound
= pygame
.mixer
.Sound(self
.raw_data
)
83 self
.started_at
= time
.time()
84 self
.channel().play(sound
, fade_ms
= int(fade_in
* 1000))
85 self
.flag_paused
= False
88 self
.paused_at
= time
.time()
89 self
.channel().pause()
90 self
.flag_paused
= True
93 self
.started_at
+= (time
.time() - self
.paused_at
)
94 self
.channel().unpause()
95 self
.flag_paused
= False
97 def stop(self
, fade_out
= 0):
99 self
.channel().fadeout(int(fade_out
* 1000))
101 self
.channel().stop()
103 def set_volume(self
, value
):
108 self
.channel().set_volume(value
/ 100)
114 return pygame
.mixer
.Channel(self
.channel_id
)