~jwiltshire/pogo/debian

« back to all changes in this revision

Viewing changes to src/media/audioplayer.py

  • Committer: Jonathan Wiltshire
  • Date: 2010-12-20 23:52:57 UTC
  • Revision ID: git-v1:e24ab7d692aa9fecd89514fbd769b83b9db2dd55
Tags: upstream/0.3
Imported Upstream version 0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# Author: Ingelrest François (Francois.Ingelrest@gmail.com)
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU Library General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
18
 
 
19
import gst
 
20
 
 
21
 
 
22
class AudioPlayer:
 
23
 
 
24
    def __init__(self, callbackEnded, usePlaybin2=True):
 
25
        """ Constructor """
 
26
        self.player        = None
 
27
        self.volume        = 1
 
28
        self.rgEnabled     = False
 
29
        self.eqzLevels     = None
 
30
        self.equalizer     = None
 
31
        self.eqzEnabled    = False
 
32
        self.usePlaybin2   = usePlaybin2
 
33
        self.cdReadSpeed   = 1
 
34
        self.callbackEnded = callbackEnded
 
35
 
 
36
 
 
37
    def __getPlayer(self):
 
38
        """ Construct and return the GStreamer player """
 
39
        self.__constructPlayer()
 
40
        self.__getPlayer = self.__getPlayer_post       # I love Python
 
41
 
 
42
        return self.player
 
43
 
 
44
 
 
45
    def __getPlayer_post(self):
 
46
        """ Return the GStreamer player """
 
47
        return self.player
 
48
 
 
49
 
 
50
    def __constructPlayer(self):
 
51
        """ Create the GStreamer pipeline """
 
52
        if self.usePlaybin2:
 
53
            self.player = gst.element_factory_make('playbin2', 'player')
 
54
            self.player.connect('about-to-finish', self.__onAboutToFinish)
 
55
        else:
 
56
            self.player = gst.element_factory_make('playbin', 'player')
 
57
 
 
58
        # No video
 
59
        self.player.set_property('video-sink', gst.element_factory_make('fakesink', 'fakesink'))
 
60
 
 
61
        # Restore volume
 
62
        self.player.set_property('volume', self.volume)
 
63
 
 
64
        # Change the audio sink to our own bin, so that an equalizer/replay gain element can be added later on if needed
 
65
        self.audiobin  = gst.Bin('audiobin')
 
66
        self.audiosink = gst.element_factory_make('autoaudiosink', 'audiosink')
 
67
 
 
68
        # Callback when the source of the playbin is changed
 
69
        self.player.connect('notify::source', self.__onNewPlaybinSource)
 
70
 
 
71
        self.audiobin.add(self.audiosink)
 
72
        self.audiobin.add_pad(gst.GhostPad('sink', self.audiosink.get_pad('sink')))
 
73
        self.player.set_property('audio-sink', self.audiobin)
 
74
 
 
75
        # Monitor messages generated by the player
 
76
        bus = self.player.get_bus()
 
77
        bus.add_signal_watch()
 
78
        bus.connect('message', self.__onGstMessage)
 
79
 
 
80
        # Add equalizer?
 
81
        if self.eqzEnabled:
 
82
            self.equalizer = gst.element_factory_make('equalizer-10bands', 'equalizer')
 
83
            self.audiobin.add(self.equalizer)
 
84
            self.audiobin.get_pad('sink').set_target(self.equalizer.get_pad('sink'))
 
85
            self.equalizer.link(self.audiosink)
 
86
 
 
87
            if self.eqzLevels is not None:
 
88
                self.setEqualizerLvls(self.eqzLevels)
 
89
 
 
90
        # Add replay gain?
 
91
        if self.rgEnabled:
 
92
            replaygain = gst.element_factory_make('rgvolume', 'replaygain')
 
93
 
 
94
            self.audiobin.add(replaygain)
 
95
            self.audiobin.get_pad('sink').set_target(replaygain.get_pad('sink'))
 
96
 
 
97
            if self.equalizer is None: replaygain.link(self.audiosink)
 
98
            else:                      replaygain.link(self.equalizer)
 
99
 
 
100
 
 
101
    def enableEqualizer(self):
 
102
        """ Add an equalizer to the audio chain """
 
103
        self.eqzEnabled = True
 
104
 
 
105
 
 
106
    def enableReplayGain(self):
 
107
        """ Add/Enable a replay gain element """
 
108
        self.rgEnabled = True
 
109
 
 
110
 
 
111
    def setEqualizerLvls(self, lvls):
 
112
        """ Set the level of the 10-bands of the equalizer (levels must be a list/tuple with 10 values lying between -24 and +12) """
 
113
        if len(lvls) == 10:
 
114
            self.eqzLevels = lvls
 
115
 
 
116
            if self.equalizer is not None:
 
117
                self.equalizer.set_property('band0', lvls[0])
 
118
                self.equalizer.set_property('band1', lvls[1])
 
119
                self.equalizer.set_property('band2', lvls[2])
 
120
                self.equalizer.set_property('band3', lvls[3])
 
121
                self.equalizer.set_property('band4', lvls[4])
 
122
                self.equalizer.set_property('band5', lvls[5])
 
123
                self.equalizer.set_property('band6', lvls[6])
 
124
                self.equalizer.set_property('band7', lvls[7])
 
125
                self.equalizer.set_property('band8', lvls[8])
 
126
                self.equalizer.set_property('band9', lvls[9])
 
127
 
 
128
 
 
129
    def __onNewPlaybinSource(self, playbin, params):
 
130
        """ Change the CR-ROM drive speed to 1 when applicable """
 
131
        source = self.__getPlayer().get_by_name('source')
 
132
 
 
133
        # Didn't find a way to determine the real class of source
 
134
        # So we use the 'paranoia-mode' property to determine whether it's indeed a CD we're playing
 
135
        try:
 
136
            source.get_property('paranoia-mode')
 
137
            source.set_property('read-speed', self.cdReadSpeed)
 
138
        except:
 
139
            pass
 
140
 
 
141
 
 
142
    def __onAboutToFinish(self, isLast):
 
143
        """ End of the track """
 
144
        self.callbackEnded(False)
 
145
 
 
146
 
 
147
    def __onGstMessage(self, bus, msg):
 
148
        """ A new message generated by the player """
 
149
        if msg.type == gst.MESSAGE_EOS:
 
150
            self.callbackEnded(False)
 
151
        elif msg.type == gst.MESSAGE_ERROR:
 
152
            self.stop()
 
153
            # It seems that the pipeline may not be able to play again any valid stream when an error occurs
 
154
            # We thus create a new one, even if that's quite a ugly solution
 
155
            self.__constructPlayer()
 
156
            self.callbackEnded(True)
 
157
 
 
158
        return True
 
159
 
 
160
 
 
161
    def setCDReadSpeed(self, speed):
 
162
        """ Set the CD-ROM drive read speed """
 
163
        self.cdReadSpeed = speed
 
164
 
 
165
 
 
166
    def setNextURI(self, uri):
 
167
        """ Set the next URI """
 
168
        self.__getPlayer().set_property('uri', uri.replace('%', '%25').replace('#', '%23'))
 
169
 
 
170
 
 
171
    def setVolume(self, level):
 
172
        """ Set the volume to the given level (0 <= level <= 1) """
 
173
        if level < 0:   self.volume = 0
 
174
        elif level > 1: self.volume = 1
 
175
        else:           self.volume = level
 
176
 
 
177
        if self.player is not None:
 
178
            self.player.set_property('volume', self.volume)
 
179
 
 
180
 
 
181
    def isPaused(self):
 
182
        """ Return whether the player is paused """
 
183
        return self.__getPlayer().get_state()[1] == gst.STATE_PAUSED
 
184
 
 
185
 
 
186
    def isPlaying(self):
 
187
        """ Return whether the player is paused """
 
188
        return self.__getPlayer().get_state()[1] == gst.STATE_PLAYING
 
189
 
 
190
 
 
191
    def setURI(self, uri):
 
192
        """ Play the given URI """
 
193
        self.__getPlayer().set_property('uri', uri.replace('%', '%25').replace('#', '%23'))
 
194
 
 
195
 
 
196
    def play(self):
 
197
        """ Play """
 
198
        self.__getPlayer().set_state(gst.STATE_PLAYING)
 
199
 
 
200
 
 
201
    def pause(self):
 
202
        """ Pause """
 
203
        self.__getPlayer().set_state(gst.STATE_PAUSED)
 
204
 
 
205
 
 
206
    def stop(self):
 
207
        """ Stop playing """
 
208
        self.__getPlayer().set_state(gst.STATE_NULL)
 
209
 
 
210
 
 
211
    def seek(self, where):
 
212
        """ Jump to the given location """
 
213
        self.__getPlayer().seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, where)
 
214
 
 
215
 
 
216
    def getPosition(self):
 
217
        """ Return the current position """
 
218
        try:    return self.__getPlayer().query_position(gst.FORMAT_TIME)[0]
 
219
        except: return 0
 
220
 
 
221
 
 
222
    def getDuration(self):
 
223
        """ Return the duration of the current stream """
 
224
        try:    return self.__getPlayer().query_duration(gst.FORMAT_TIME)[0]
 
225
        except: return 0