# ---------------------------------------------------------------------------
#
# 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
#---------------------------------------------------------------------------
[docs]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
[docs] 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
#---------------------------------------------------------------------------
[docs]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)
[docs]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)
[docs]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
[docs]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)
[docs]def music_end():
#print "albow.music: music_end" ###
schedule(next_change_delay, jog_music)
[docs]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()
[docs]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
[docs]def get_music_enabled():
return music_enabled
[docs]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
[docs]class EnableMusicControl(CheckBox):
[docs] def get_value(self):
return get_music_enabled()
[docs] def set_value(self, x):
set_music_enabled(x)
[docs]class MusicVolumeControl(Widget):
def __init__(self, **kwds):
Widget.__init__(self, Rect((0, 0), (100, 20)), **kwds)
[docs] def draw(self, surf):
r = self.get_margin_rect()
r.width = int(round(music.get_volume() * r.width))
surf.fill(self.fg_color, r)
[docs] def mouse_down(self, e):
self.mouse_drag(e)
[docs] 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()
[docs]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()
[docs]def show_music_options_dialog():
dlog = MusicOptionsDialog()
dlog.present()