221 lines
6.2 KiB
Python
221 lines
6.2 KiB
Python
#---------------------------------------------------------------------------
|
|
#
|
|
# Albow - Music
|
|
#
|
|
#---------------------------------------------------------------------------
|
|
|
|
from __future__ import division
|
|
import os
|
|
from random import randrange
|
|
|
|
try:
|
|
from pygame.mixer import music
|
|
except ImportError:
|
|
music = None
|
|
print "Music not available"
|
|
|
|
if music:
|
|
import root
|
|
music.set_endevent(root.MUSIC_END_EVENT)
|
|
|
|
from resource import resource_path
|
|
from root import schedule
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
fadeout_time = 1 # Time over which to fade out music (sec)
|
|
change_delay = 2 # Delay between end of one item and starting the next (sec)
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
music_enabled = True
|
|
current_music = None
|
|
current_playlist = None
|
|
next_change_delay = 0
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
class PlayList(object):
|
|
"""A collection of music filenames to be played sequentially or
|
|
randomly. If random is true, items will be played in a random order.
|
|
If repeat is true, the list will be repeated indefinitely, otherwise
|
|
each item will only be played once."""
|
|
|
|
def __init__(self, items, random=False, repeat=False):
|
|
self.items = list(items)
|
|
self.random = random
|
|
self.repeat = repeat
|
|
|
|
def next(self):
|
|
"""Returns the next item to be played."""
|
|
items = self.items
|
|
if items:
|
|
if self.random:
|
|
n = len(items)
|
|
if self.repeat:
|
|
n = (n + 1) // 2
|
|
i = randrange(n)
|
|
else:
|
|
i = 0
|
|
item = items.pop(i)
|
|
if self.repeat:
|
|
items.append(item)
|
|
return item
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
def get_music(*names, **kwds):
|
|
"""Return the full pathname of a music file from the "music" resource
|
|
subdirectory."""
|
|
prefix = kwds.pop('prefix', "music")
|
|
return resource_path(prefix, *names)
|
|
|
|
|
|
def get_playlist(*names, **kwds):
|
|
prefix = kwds.pop('prefix', "music")
|
|
dirpath = get_music(*names, **{'prefix': prefix})
|
|
items = [os.path.join(dirpath, filename)
|
|
for filename in os.listdir(dirpath)
|
|
if not filename.startswith(".")]
|
|
items.sort()
|
|
return PlayList(items, **kwds)
|
|
|
|
|
|
def change_playlist(new_playlist):
|
|
"""Fade out any currently playing music and start playing from the given
|
|
playlist."""
|
|
#print "albow.music: change_playlist" ###
|
|
global current_music, current_playlist, next_change_delay
|
|
if music and new_playlist is not current_playlist:
|
|
current_playlist = new_playlist
|
|
if music_enabled:
|
|
music.fadeout(fadeout_time * 1000)
|
|
next_change_delay = max(0, change_delay - fadeout_time)
|
|
jog_music()
|
|
else:
|
|
current_music = None
|
|
|
|
|
|
def change_music(new_music, repeat=False):
|
|
"""Fade out any currently playing music and start playing the given
|
|
music file."""
|
|
#print "albow.music: change_music" ###
|
|
if music and new_music is not current_music:
|
|
if new_music:
|
|
new_playlist = PlayList([new_music], repeat=repeat)
|
|
else:
|
|
new_playlist = None
|
|
change_playlist(new_playlist)
|
|
|
|
|
|
def music_end():
|
|
#print "albow.music: music_end" ###
|
|
schedule(next_change_delay, jog_music)
|
|
|
|
|
|
def jog_music():
|
|
"""If no music is currently playing, start playing the next item
|
|
from the current playlist."""
|
|
if music_enabled and not music.get_busy():
|
|
start_next_music()
|
|
|
|
|
|
def start_next_music():
|
|
"""Start playing the next item from the current playlist immediately."""
|
|
#print "albow.music: start_next_music" ###
|
|
global current_music, next_change_delay
|
|
if music_enabled and current_playlist:
|
|
next_music = current_playlist.next()
|
|
if next_music:
|
|
print "albow.music: loading", repr(next_music) ###
|
|
music.load(next_music)
|
|
music.play()
|
|
next_change_delay = change_delay
|
|
current_music = next_music
|
|
|
|
|
|
def get_music_enabled():
|
|
return music_enabled
|
|
|
|
|
|
def set_music_enabled(state):
|
|
global music_enabled
|
|
if music_enabled != state:
|
|
music_enabled = state
|
|
if state:
|
|
# Music pausing doesn't always seem to work.
|
|
#music.unpause()
|
|
if current_music:
|
|
# After stopping and restarting currently loaded music,
|
|
# fadeout no longer works.
|
|
#print "albow.music: reloading", repr(current_music) ###
|
|
music.load(current_music)
|
|
music.play()
|
|
else:
|
|
jog_music()
|
|
else:
|
|
#music.pause()
|
|
music.stop()
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
from pygame import Rect
|
|
from albow.widget import Widget
|
|
from albow.controls import Label, Button, CheckBox
|
|
from albow.layout import Row, Column, Grid
|
|
from albow.dialogs import Dialog
|
|
|
|
|
|
class EnableMusicControl(CheckBox):
|
|
|
|
def get_value(self):
|
|
return get_music_enabled()
|
|
|
|
def set_value(self, x):
|
|
set_music_enabled(x)
|
|
|
|
|
|
class MusicVolumeControl(Widget):
|
|
|
|
def __init__(self, **kwds):
|
|
Widget.__init__(self, Rect((0, 0), (100, 20)), **kwds)
|
|
|
|
def draw(self, surf):
|
|
r = self.get_margin_rect()
|
|
r.width = int(round(music.get_volume() * r.width))
|
|
surf.fill(self.fg_color, r)
|
|
|
|
def mouse_down(self, e):
|
|
self.mouse_drag(e)
|
|
|
|
def mouse_drag(self, e):
|
|
m = self.margin
|
|
w = self.width - 2 * m
|
|
x = max(0.0, min(1.0, (e.local[0] - m) / w))
|
|
music.set_volume(x)
|
|
self.invalidate()
|
|
|
|
|
|
class MusicOptionsDialog(Dialog):
|
|
|
|
def __init__(self):
|
|
Dialog.__init__(self)
|
|
emc = EnableMusicControl()
|
|
mvc = MusicVolumeControl()
|
|
controls = Grid([
|
|
[Label("Enable Music"), emc],
|
|
[Label("Music Volume"), mvc],
|
|
])
|
|
buttons = Button("OK", self.ok)
|
|
contents = Column([controls, buttons], align='r', spacing=20)
|
|
contents.topleft = (20, 20)
|
|
self.add(contents)
|
|
self.shrink_wrap()
|
|
|
|
|
|
def show_music_options_dialog():
|
|
dlog = MusicOptionsDialog()
|
|
dlog.present()
|