Ajout du GUI
This commit is contained in:
244
kivy/core/audio/__init__.py
Normal file
244
kivy/core/audio/__init__.py
Normal file
@@ -0,0 +1,244 @@
|
||||
'''
|
||||
Audio
|
||||
=====
|
||||
|
||||
Load an audio sound and play it with::
|
||||
|
||||
from kivy.core.audio import SoundLoader
|
||||
|
||||
sound = SoundLoader.load('mytest.wav')
|
||||
if sound:
|
||||
print("Sound found at %s" % sound.source)
|
||||
print("Sound is %.3f seconds" % sound.length)
|
||||
sound.play()
|
||||
|
||||
You should not use the Sound class directly. The class returned by
|
||||
:func:`SoundLoader.load` will be the best sound provider for that particular
|
||||
file type, so it might return different Sound classes depending the file type.
|
||||
|
||||
Event dispatching and state changes
|
||||
-----------------------------------
|
||||
|
||||
Audio is often processed in parallel to your code. This means you often need to
|
||||
enter the Kivy :func:`eventloop <kivy.base.EventLoopBase>` in order to allow
|
||||
events and state changes to be dispatched correctly.
|
||||
|
||||
You seldom need to worry about this as Kivy apps typically always
|
||||
require this event loop for the GUI to remain responsive, but it is good to
|
||||
keep this in mind when debugging or running in a
|
||||
`REPL <https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop>`_
|
||||
(Read-eval-print loop).
|
||||
|
||||
.. versionchanged:: 1.10.0
|
||||
The pygst and gi providers have been removed.
|
||||
|
||||
.. versionchanged:: 1.8.0
|
||||
There are now 2 distinct Gstreamer implementations: one using Gi/Gst
|
||||
working for both Python 2+3 with Gstreamer 1.0, and one using PyGST
|
||||
working only for Python 2 + Gstreamer 0.10.
|
||||
|
||||
.. note::
|
||||
|
||||
The core audio library does not support recording audio. If you require
|
||||
this functionality, please refer to the
|
||||
`audiostream <https://github.com/kivy/audiostream>`_ extension.
|
||||
|
||||
'''
|
||||
|
||||
__all__ = ('Sound', 'SoundLoader')
|
||||
|
||||
from kivy.logger import Logger
|
||||
from kivy.event import EventDispatcher
|
||||
from kivy.core import core_register_libs
|
||||
from kivy.resources import resource_find
|
||||
from kivy.properties import StringProperty, NumericProperty, OptionProperty, \
|
||||
AliasProperty, BooleanProperty, BoundedNumericProperty
|
||||
from kivy.utils import platform
|
||||
from kivy.setupconfig import USE_SDL2
|
||||
|
||||
from sys import float_info
|
||||
|
||||
|
||||
class SoundLoader:
|
||||
'''Load a sound, using the best loader for the given file type.
|
||||
'''
|
||||
|
||||
_classes = []
|
||||
|
||||
@staticmethod
|
||||
def register(classobj):
|
||||
'''Register a new class to load the sound.'''
|
||||
Logger.debug('Audio: register %s' % classobj.__name__)
|
||||
SoundLoader._classes.append(classobj)
|
||||
|
||||
@staticmethod
|
||||
def load(filename):
|
||||
'''Load a sound, and return a Sound() instance.'''
|
||||
rfn = resource_find(filename)
|
||||
if rfn is not None:
|
||||
filename = rfn
|
||||
ext = filename.split('.')[-1].lower()
|
||||
if '?' in ext:
|
||||
ext = ext.split('?')[0]
|
||||
for classobj in SoundLoader._classes:
|
||||
if ext in classobj.extensions():
|
||||
return classobj(source=filename)
|
||||
Logger.warning('Audio: Unable to find a loader for <%s>' %
|
||||
filename)
|
||||
return None
|
||||
|
||||
|
||||
class Sound(EventDispatcher):
|
||||
'''Represents a sound to play. This class is abstract, and cannot be used
|
||||
directly.
|
||||
|
||||
Use SoundLoader to load a sound.
|
||||
|
||||
:Events:
|
||||
`on_play`: None
|
||||
Fired when the sound is played.
|
||||
`on_stop`: None
|
||||
Fired when the sound is stopped.
|
||||
'''
|
||||
|
||||
source = StringProperty(None)
|
||||
'''Filename / source of your audio file.
|
||||
|
||||
.. versionadded:: 1.3.0
|
||||
|
||||
:attr:`source` is a :class:`~kivy.properties.StringProperty` that defaults
|
||||
to None and is read-only. Use the :meth:`SoundLoader.load` for loading
|
||||
audio.
|
||||
'''
|
||||
|
||||
volume = NumericProperty(1.)
|
||||
'''Volume, in the range 0-1. 1 means full volume, 0 means mute.
|
||||
|
||||
.. versionadded:: 1.3.0
|
||||
|
||||
:attr:`volume` is a :class:`~kivy.properties.NumericProperty` and defaults
|
||||
to 1.
|
||||
'''
|
||||
|
||||
pitch = BoundedNumericProperty(1., min=float_info.epsilon)
|
||||
'''Pitch of a sound. 2 is an octave higher, .5 one below. This is only
|
||||
implemented for SDL2 audio provider yet.
|
||||
|
||||
.. versionadded:: 1.10.0
|
||||
|
||||
:attr:`pitch` is a :class:`~kivy.properties.NumericProperty` and defaults
|
||||
to 1.
|
||||
'''
|
||||
|
||||
state = OptionProperty('stop', options=('stop', 'play'))
|
||||
'''State of the sound, one of 'stop' or 'play'.
|
||||
|
||||
.. versionadded:: 1.3.0
|
||||
|
||||
:attr:`state` is a read-only :class:`~kivy.properties.OptionProperty`.'''
|
||||
|
||||
loop = BooleanProperty(False)
|
||||
'''Set to True if the sound should automatically loop when it finishes.
|
||||
|
||||
.. versionadded:: 1.8.0
|
||||
|
||||
:attr:`loop` is a :class:`~kivy.properties.BooleanProperty` and defaults to
|
||||
False.'''
|
||||
|
||||
#
|
||||
# deprecated
|
||||
#
|
||||
def _get_status(self):
|
||||
return self.state
|
||||
status = AliasProperty(
|
||||
_get_status, None, bind=('state', ), deprecated=True)
|
||||
'''
|
||||
.. deprecated:: 1.3.0
|
||||
Use :attr:`state` instead.
|
||||
'''
|
||||
|
||||
def _get_filename(self):
|
||||
return self.source
|
||||
filename = AliasProperty(
|
||||
_get_filename, None, bind=('source', ), deprecated=True)
|
||||
'''
|
||||
.. deprecated:: 1.3.0
|
||||
Use :attr:`source` instead.
|
||||
'''
|
||||
|
||||
__events__ = ('on_play', 'on_stop')
|
||||
|
||||
def on_source(self, instance, filename):
|
||||
self.unload()
|
||||
if filename is None:
|
||||
return
|
||||
self.load()
|
||||
|
||||
def get_pos(self):
|
||||
'''
|
||||
Returns the current position of the audio file.
|
||||
Returns 0 if not playing.
|
||||
|
||||
.. versionadded:: 1.4.1
|
||||
'''
|
||||
return 0
|
||||
|
||||
def _get_length(self):
|
||||
return 0
|
||||
|
||||
length = property(lambda self: self._get_length(),
|
||||
doc='Get length of the sound (in seconds).')
|
||||
|
||||
def load(self):
|
||||
'''Load the file into memory.'''
|
||||
pass
|
||||
|
||||
def unload(self):
|
||||
'''Unload the file from memory.'''
|
||||
pass
|
||||
|
||||
def play(self):
|
||||
'''Play the file.'''
|
||||
self.state = 'play'
|
||||
self.dispatch('on_play')
|
||||
|
||||
def stop(self):
|
||||
'''Stop playback.'''
|
||||
self.state = 'stop'
|
||||
self.dispatch('on_stop')
|
||||
|
||||
def seek(self, position):
|
||||
'''Go to the <position> (in seconds).
|
||||
|
||||
.. note::
|
||||
Most sound providers cannot seek when the audio is stopped.
|
||||
Play then seek.
|
||||
'''
|
||||
pass
|
||||
|
||||
def on_play(self):
|
||||
pass
|
||||
|
||||
def on_stop(self):
|
||||
pass
|
||||
|
||||
|
||||
# Little trick here, don't activate gstreamer on window
|
||||
# seem to have lot of crackle or something...
|
||||
audio_libs = []
|
||||
if platform == 'android':
|
||||
audio_libs += [('android', 'audio_android')]
|
||||
elif platform in ('macosx', 'ios'):
|
||||
audio_libs += [('avplayer', 'audio_avplayer')]
|
||||
try:
|
||||
from kivy.lib.gstplayer import GstPlayer # NOQA
|
||||
audio_libs += [('gstplayer', 'audio_gstplayer')]
|
||||
except ImportError:
|
||||
pass
|
||||
audio_libs += [('ffpyplayer', 'audio_ffpyplayer')]
|
||||
if USE_SDL2:
|
||||
audio_libs += [('sdl2', 'audio_sdl2')]
|
||||
else:
|
||||
audio_libs += [('pygame', 'audio_pygame')]
|
||||
|
||||
libs_loaded = core_register_libs('audio', audio_libs)
|
||||
BIN
kivy/core/audio/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
kivy/core/audio/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
kivy/core/audio/__pycache__/audio_android.cpython-310.pyc
Normal file
BIN
kivy/core/audio/__pycache__/audio_android.cpython-310.pyc
Normal file
Binary file not shown.
BIN
kivy/core/audio/__pycache__/audio_avplayer.cpython-310.pyc
Normal file
BIN
kivy/core/audio/__pycache__/audio_avplayer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
kivy/core/audio/__pycache__/audio_ffpyplayer.cpython-310.pyc
Normal file
BIN
kivy/core/audio/__pycache__/audio_ffpyplayer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
kivy/core/audio/__pycache__/audio_gstplayer.cpython-310.pyc
Normal file
BIN
kivy/core/audio/__pycache__/audio_gstplayer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
kivy/core/audio/__pycache__/audio_pygame.cpython-310.pyc
Normal file
BIN
kivy/core/audio/__pycache__/audio_pygame.cpython-310.pyc
Normal file
Binary file not shown.
104
kivy/core/audio/audio_android.py
Normal file
104
kivy/core/audio/audio_android.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
AudioAndroid: Kivy audio implementation for Android using native API
|
||||
"""
|
||||
|
||||
__all__ = ("SoundAndroidPlayer", )
|
||||
|
||||
from jnius import autoclass, java_method, PythonJavaClass
|
||||
from android import api_version
|
||||
from kivy.core.audio import Sound, SoundLoader
|
||||
|
||||
|
||||
MediaPlayer = autoclass("android.media.MediaPlayer")
|
||||
AudioManager = autoclass("android.media.AudioManager")
|
||||
if api_version >= 21:
|
||||
AudioAttributesBuilder = autoclass("android.media.AudioAttributes$Builder")
|
||||
|
||||
|
||||
class OnCompletionListener(PythonJavaClass):
|
||||
__javainterfaces__ = ["android/media/MediaPlayer$OnCompletionListener"]
|
||||
__javacontext__ = "app"
|
||||
|
||||
def __init__(self, callback, **kwargs):
|
||||
super(OnCompletionListener, self).__init__(**kwargs)
|
||||
self.callback = callback
|
||||
|
||||
@java_method("(Landroid/media/MediaPlayer;)V")
|
||||
def onCompletion(self, mp):
|
||||
self.callback()
|
||||
|
||||
|
||||
class SoundAndroidPlayer(Sound):
|
||||
@staticmethod
|
||||
def extensions():
|
||||
return ("mp3", "mp4", "aac", "3gp", "flac", "mkv", "wav", "ogg", "m4a",
|
||||
"gsm", "mid", "xmf", "mxmf", "rtttl", "rtx", "ota", "imy")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._mediaplayer = None
|
||||
self._completion_listener = None
|
||||
super(SoundAndroidPlayer, self).__init__(**kwargs)
|
||||
|
||||
def load(self):
|
||||
self.unload()
|
||||
self._mediaplayer = MediaPlayer()
|
||||
if api_version >= 21:
|
||||
self._mediaplayer.setAudioAttributes(
|
||||
AudioAttributesBuilder()
|
||||
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
|
||||
.build())
|
||||
else:
|
||||
self._mediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC)
|
||||
self._mediaplayer.setDataSource(self.source)
|
||||
self._completion_listener = OnCompletionListener(
|
||||
self._completion_callback
|
||||
)
|
||||
self._mediaplayer.setOnCompletionListener(self._completion_listener)
|
||||
self._mediaplayer.prepare()
|
||||
|
||||
def unload(self):
|
||||
if self._mediaplayer:
|
||||
self._mediaplayer.release()
|
||||
self._mediaplayer = None
|
||||
|
||||
def play(self):
|
||||
if not self._mediaplayer:
|
||||
return
|
||||
self._mediaplayer.start()
|
||||
super(SoundAndroidPlayer, self).play()
|
||||
|
||||
def stop(self):
|
||||
if not self._mediaplayer:
|
||||
return
|
||||
self._mediaplayer.stop()
|
||||
self._mediaplayer.prepare()
|
||||
|
||||
def seek(self, position):
|
||||
if not self._mediaplayer:
|
||||
return
|
||||
self._mediaplayer.seekTo(float(position) * 1000)
|
||||
|
||||
def get_pos(self):
|
||||
if self._mediaplayer:
|
||||
return self._mediaplayer.getCurrentPosition() / 1000.
|
||||
return super(SoundAndroidPlayer, self).get_pos()
|
||||
|
||||
def on_volume(self, instance, volume):
|
||||
if self._mediaplayer:
|
||||
volume = float(volume)
|
||||
self._mediaplayer.setVolume(volume, volume)
|
||||
|
||||
def _completion_callback(self):
|
||||
super(SoundAndroidPlayer, self).stop()
|
||||
|
||||
def _get_length(self):
|
||||
if self._mediaplayer:
|
||||
return self._mediaplayer.getDuration() / 1000.
|
||||
return super(SoundAndroidPlayer, self)._get_length()
|
||||
|
||||
def on_loop(self, instance, loop):
|
||||
if self._mediaplayer:
|
||||
self._mediaplayer.setLooping(loop)
|
||||
|
||||
|
||||
SoundLoader.register(SoundAndroidPlayer)
|
||||
72
kivy/core/audio/audio_avplayer.py
Normal file
72
kivy/core/audio/audio_avplayer.py
Normal file
@@ -0,0 +1,72 @@
|
||||
'''
|
||||
AudioAvplayer: implementation of Sound using pyobjus / AVFoundation.
|
||||
Works on iOS / OSX.
|
||||
'''
|
||||
|
||||
__all__ = ('SoundAvplayer', )
|
||||
|
||||
from kivy.core.audio import Sound, SoundLoader
|
||||
from pyobjus import autoclass
|
||||
from pyobjus.dylib_manager import load_framework, INCLUDE
|
||||
|
||||
load_framework(INCLUDE.AVFoundation)
|
||||
AVAudioPlayer = autoclass("AVAudioPlayer")
|
||||
NSURL = autoclass("NSURL")
|
||||
NSString = autoclass("NSString")
|
||||
|
||||
|
||||
class SoundAvplayer(Sound):
|
||||
@staticmethod
|
||||
def extensions():
|
||||
# taken from https://goo.gl/015kvU
|
||||
return ("aac", "adts", "aif", "aiff", "aifc", "caf", "mp3", "mp4",
|
||||
"m4a", "snd", "au", "sd2", "wav")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._avplayer = None
|
||||
super(SoundAvplayer, self).__init__(**kwargs)
|
||||
|
||||
def load(self):
|
||||
self.unload()
|
||||
fn = NSString.alloc().initWithUTF8String_(self.source)
|
||||
url = NSURL.alloc().initFileURLWithPath_(fn)
|
||||
self._avplayer = AVAudioPlayer.alloc().initWithContentsOfURL_error_(
|
||||
url, None)
|
||||
|
||||
def unload(self):
|
||||
self.stop()
|
||||
self._avplayer = None
|
||||
|
||||
def play(self):
|
||||
if not self._avplayer:
|
||||
return
|
||||
self._avplayer.play()
|
||||
super(SoundAvplayer, self).play()
|
||||
|
||||
def stop(self):
|
||||
if not self._avplayer:
|
||||
return
|
||||
self._avplayer.stop()
|
||||
super(SoundAvplayer, self).stop()
|
||||
|
||||
def seek(self, position):
|
||||
if not self._avplayer:
|
||||
return
|
||||
self._avplayer.playAtTime_(float(position))
|
||||
|
||||
def get_pos(self):
|
||||
if self._avplayer:
|
||||
return self._avplayer.currentTime
|
||||
return super(SoundAvplayer, self).get_pos()
|
||||
|
||||
def on_volume(self, instance, volume):
|
||||
if self._avplayer:
|
||||
self._avplayer.volume = float(volume)
|
||||
|
||||
def _get_length(self):
|
||||
if self._avplayer:
|
||||
return self._avplayer.duration
|
||||
return super(SoundAvplayer, self)._get_length()
|
||||
|
||||
|
||||
SoundLoader.register(SoundAvplayer)
|
||||
185
kivy/core/audio/audio_ffpyplayer.py
Normal file
185
kivy/core/audio/audio_ffpyplayer.py
Normal file
@@ -0,0 +1,185 @@
|
||||
'''
|
||||
FFmpeg based audio player
|
||||
=========================
|
||||
|
||||
To use, you need to install ffpyplayer and have a compiled ffmpeg shared
|
||||
library.
|
||||
|
||||
https://github.com/matham/ffpyplayer
|
||||
|
||||
The docs there describe how to set this up. But briefly, first you need to
|
||||
compile ffmpeg using the shared flags while disabling the static flags (you'll
|
||||
probably have to set the fPIC flag, e.g. CFLAGS=-fPIC). Here's some
|
||||
instructions: https://trac.ffmpeg.org/wiki/CompilationGuide. For Windows, you
|
||||
can download compiled GPL binaries from http://ffmpeg.zeranoe.com/builds/.
|
||||
Similarly, you should download SDL.
|
||||
|
||||
Now, you should a ffmpeg and sdl directory. In each, you should have a include,
|
||||
bin, and lib directory, where e.g. for Windows, lib contains the .dll.a files,
|
||||
while bin contains the actual dlls. The include directory holds the headers.
|
||||
The bin directory is only needed if the shared libraries are not already on
|
||||
the path. In the environment define FFMPEG_ROOT and SDL_ROOT, each pointing to
|
||||
the ffmpeg, and SDL directories, respectively. (If you're using SDL2,
|
||||
the include directory will contain a directory called SDL2, which then holds
|
||||
the headers).
|
||||
|
||||
Once defined, download the ffpyplayer git and run
|
||||
|
||||
python setup.py build_ext --inplace
|
||||
|
||||
Finally, before running you need to ensure that ffpyplayer is in python's path.
|
||||
|
||||
..Note::
|
||||
|
||||
When kivy exits by closing the window while the audio is playing,
|
||||
it appears that the __del__method of SoundFFPy
|
||||
is not called. Because of this the SoundFFPy object is not
|
||||
properly deleted when kivy exits. The consequence is that because
|
||||
MediaPlayer creates internal threads which do not have their daemon
|
||||
flag set, when the main threads exists it'll hang and wait for the other
|
||||
MediaPlayer threads to exit. But since __del__ is not called to delete the
|
||||
MediaPlayer object, those threads will remain alive hanging kivy. What this
|
||||
means is that you have to be sure to delete the MediaPlayer object before
|
||||
kivy exits by setting it to None.
|
||||
'''
|
||||
|
||||
__all__ = ('SoundFFPy', )
|
||||
|
||||
try:
|
||||
import ffpyplayer
|
||||
from ffpyplayer.player import MediaPlayer
|
||||
from ffpyplayer.tools import set_log_callback, get_log_callback, formats_in
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.logger import Logger
|
||||
from kivy.core.audio import Sound, SoundLoader
|
||||
from kivy.weakmethod import WeakMethod
|
||||
import time
|
||||
|
||||
try:
|
||||
Logger.info(
|
||||
'SoundFFPy: Using ffpyplayer {}'.format(ffpyplayer.__version__))
|
||||
except:
|
||||
Logger.info('SoundFFPy: Using ffpyplayer {}'.format(ffpyplayer.version))
|
||||
|
||||
|
||||
logger_func = {'quiet': Logger.critical, 'panic': Logger.critical,
|
||||
'fatal': Logger.critical, 'error': Logger.error,
|
||||
'warning': Logger.warning, 'info': Logger.info,
|
||||
'verbose': Logger.debug, 'debug': Logger.debug}
|
||||
|
||||
|
||||
def _log_callback(message, level):
|
||||
message = message.strip()
|
||||
if message:
|
||||
logger_func[level]('ffpyplayer: {}'.format(message))
|
||||
|
||||
|
||||
class SoundFFPy(Sound):
|
||||
|
||||
@staticmethod
|
||||
def extensions():
|
||||
return formats_in
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._ffplayer = None
|
||||
self.quitted = False
|
||||
self._log_callback_set = False
|
||||
self._state = ''
|
||||
self.state = 'stop'
|
||||
|
||||
if not get_log_callback():
|
||||
set_log_callback(_log_callback)
|
||||
self._log_callback_set = True
|
||||
|
||||
super(SoundFFPy, self).__init__(**kwargs)
|
||||
|
||||
def __del__(self):
|
||||
self.unload()
|
||||
if self._log_callback_set:
|
||||
set_log_callback(None)
|
||||
|
||||
def _player_callback(self, selector, value):
|
||||
if self._ffplayer is None:
|
||||
return
|
||||
if selector == 'quit':
|
||||
def close(*args):
|
||||
self.quitted = True
|
||||
self.unload()
|
||||
Clock.schedule_once(close, 0)
|
||||
elif selector == 'eof':
|
||||
Clock.schedule_once(self._do_eos, 0)
|
||||
|
||||
def load(self):
|
||||
self.unload()
|
||||
ff_opts = {'vn': True, 'sn': True} # only audio
|
||||
self._ffplayer = MediaPlayer(self.source,
|
||||
callback=self._player_callback,
|
||||
loglevel='info', ff_opts=ff_opts)
|
||||
player = self._ffplayer
|
||||
player.set_volume(self.volume)
|
||||
player.toggle_pause()
|
||||
self._state = 'paused'
|
||||
# wait until loaded or failed, shouldn't take long, but just to make
|
||||
# sure metadata is available.
|
||||
s = time.perf_counter()
|
||||
while (player.get_metadata()['duration'] is None and
|
||||
not self.quitted and time.perf_counter() - s < 10.):
|
||||
time.sleep(0.005)
|
||||
|
||||
def unload(self):
|
||||
if self._ffplayer:
|
||||
self._ffplayer = None
|
||||
self._state = ''
|
||||
self.state = 'stop'
|
||||
self.quitted = False
|
||||
|
||||
def play(self):
|
||||
if self._state == 'playing':
|
||||
super(SoundFFPy, self).play()
|
||||
return
|
||||
if not self._ffplayer:
|
||||
self.load()
|
||||
self._ffplayer.toggle_pause()
|
||||
self._state = 'playing'
|
||||
self.state = 'play'
|
||||
super(SoundFFPy, self).play()
|
||||
self.seek(0)
|
||||
|
||||
def stop(self):
|
||||
if self._ffplayer and self._state == 'playing':
|
||||
self._ffplayer.toggle_pause()
|
||||
self._state = 'paused'
|
||||
self.state = 'stop'
|
||||
super(SoundFFPy, self).stop()
|
||||
|
||||
def seek(self, position):
|
||||
if self._ffplayer is None:
|
||||
return
|
||||
self._ffplayer.seek(position, relative=False)
|
||||
|
||||
def get_pos(self):
|
||||
if self._ffplayer is not None:
|
||||
return self._ffplayer.get_pts()
|
||||
return 0
|
||||
|
||||
def on_volume(self, instance, volume):
|
||||
if self._ffplayer is not None:
|
||||
self._ffplayer.set_volume(volume)
|
||||
|
||||
def _get_length(self):
|
||||
if self._ffplayer is None:
|
||||
return super(SoundFFPy, self)._get_length()
|
||||
return self._ffplayer.get_metadata()['duration']
|
||||
|
||||
def _do_eos(self, *args):
|
||||
if not self.loop:
|
||||
self.stop()
|
||||
else:
|
||||
self.seek(0.)
|
||||
|
||||
|
||||
SoundLoader.register(SoundFFPy)
|
||||
101
kivy/core/audio/audio_gstplayer.py
Normal file
101
kivy/core/audio/audio_gstplayer.py
Normal file
@@ -0,0 +1,101 @@
|
||||
'''
|
||||
Audio Gstplayer
|
||||
===============
|
||||
|
||||
.. versionadded:: 1.8.0
|
||||
|
||||
Implementation of a VideoBase with Kivy :class:`~kivy.lib.gstplayer.GstPlayer`
|
||||
This player is the preferred player, using Gstreamer 1.0, working on both
|
||||
Python 2 and 3.
|
||||
'''
|
||||
|
||||
from kivy.lib.gstplayer import GstPlayer, get_gst_version
|
||||
from kivy.core.audio import Sound, SoundLoader
|
||||
from kivy.logger import Logger
|
||||
from kivy.compat import PY2
|
||||
from kivy.clock import Clock
|
||||
from os.path import realpath
|
||||
|
||||
if PY2:
|
||||
from urllib import pathname2url
|
||||
else:
|
||||
from urllib.request import pathname2url
|
||||
|
||||
Logger.info('AudioGstplayer: Using Gstreamer {}'.format(
|
||||
'.'.join(map(str, get_gst_version()))))
|
||||
|
||||
|
||||
def _on_gstplayer_message(mtype, message):
|
||||
if mtype == 'error':
|
||||
Logger.error('AudioGstplayer: {}'.format(message))
|
||||
elif mtype == 'warning':
|
||||
Logger.warning('AudioGstplayer: {}'.format(message))
|
||||
elif mtype == 'info':
|
||||
Logger.info('AudioGstplayer: {}'.format(message))
|
||||
|
||||
|
||||
class SoundGstplayer(Sound):
|
||||
|
||||
@staticmethod
|
||||
def extensions():
|
||||
return ('wav', 'ogg', 'mp3', 'm4a', 'flac', 'mp4')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.player = None
|
||||
super(SoundGstplayer, self).__init__(**kwargs)
|
||||
|
||||
def _on_gst_eos_sync(self):
|
||||
Clock.schedule_once(self._on_gst_eos, 0)
|
||||
|
||||
def _on_gst_eos(self, *dt):
|
||||
if self.loop:
|
||||
self.player.stop()
|
||||
self.player.play()
|
||||
else:
|
||||
self.stop()
|
||||
|
||||
def load(self):
|
||||
self.unload()
|
||||
uri = self._get_uri()
|
||||
self.player = GstPlayer(uri, None, self._on_gst_eos_sync,
|
||||
_on_gstplayer_message)
|
||||
self.player.load()
|
||||
|
||||
def play(self):
|
||||
# we need to set the volume everytime, it seems that stopping + playing
|
||||
# the sound reset the volume.
|
||||
self.player.set_volume(self.volume)
|
||||
self.player.play()
|
||||
super(SoundGstplayer, self).play()
|
||||
|
||||
def stop(self):
|
||||
self.player.stop()
|
||||
super(SoundGstplayer, self).stop()
|
||||
|
||||
def unload(self):
|
||||
if self.player:
|
||||
self.player.unload()
|
||||
self.player = None
|
||||
|
||||
def seek(self, position):
|
||||
self.player.seek(position / self.length)
|
||||
|
||||
def get_pos(self):
|
||||
return self.player.get_position()
|
||||
|
||||
def _get_length(self):
|
||||
return self.player.get_duration()
|
||||
|
||||
def on_volume(self, instance, volume):
|
||||
self.player.set_volume(volume)
|
||||
|
||||
def _get_uri(self):
|
||||
uri = self.source
|
||||
if not uri:
|
||||
return
|
||||
if '://' not in uri:
|
||||
uri = 'file:' + pathname2url(realpath(uri))
|
||||
return uri
|
||||
|
||||
|
||||
SoundLoader.register(SoundGstplayer)
|
||||
127
kivy/core/audio/audio_pygame.py
Normal file
127
kivy/core/audio/audio_pygame.py
Normal file
@@ -0,0 +1,127 @@
|
||||
'''
|
||||
AudioPygame: implementation of Sound with Pygame
|
||||
|
||||
.. warning::
|
||||
|
||||
Pygame has been deprecated and will be removed in the release after Kivy
|
||||
1.11.0.
|
||||
'''
|
||||
|
||||
__all__ = ('SoundPygame', )
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.utils import platform, deprecated
|
||||
from kivy.core.audio import Sound, SoundLoader
|
||||
|
||||
_platform = platform
|
||||
try:
|
||||
if _platform == 'android':
|
||||
try:
|
||||
import android.mixer as mixer
|
||||
except ImportError:
|
||||
# old python-for-android version
|
||||
import android_mixer as mixer
|
||||
else:
|
||||
from pygame import mixer
|
||||
except:
|
||||
raise
|
||||
|
||||
# init pygame sound
|
||||
mixer.pre_init(44100, -16, 2, 1024)
|
||||
mixer.init()
|
||||
mixer.set_num_channels(32)
|
||||
|
||||
|
||||
class SoundPygame(Sound):
|
||||
|
||||
# XXX we don't set __slots__ here, to automatically add
|
||||
# a dictionary. We need that to be able to use weakref for
|
||||
# SoundPygame object. Otherwise, it failed with:
|
||||
# TypeError: cannot create weak reference to 'SoundPygame' object
|
||||
# We use our clock in play() method.
|
||||
# __slots__ = ('_data', '_channel')
|
||||
_check_play_ev = None
|
||||
|
||||
@staticmethod
|
||||
def extensions():
|
||||
if _platform == 'android':
|
||||
return ('wav', 'ogg', 'mp3', 'm4a')
|
||||
return ('wav', 'ogg')
|
||||
|
||||
@deprecated(
|
||||
msg='Pygame has been deprecated and will be removed after 1.11.0')
|
||||
def __init__(self, **kwargs):
|
||||
self._data = None
|
||||
self._channel = None
|
||||
super(SoundPygame, self).__init__(**kwargs)
|
||||
|
||||
def _check_play(self, dt):
|
||||
if self._channel is None:
|
||||
return False
|
||||
if self._channel.get_busy():
|
||||
return
|
||||
if self.loop:
|
||||
def do_loop(dt):
|
||||
self.play()
|
||||
Clock.schedule_once(do_loop)
|
||||
else:
|
||||
self.stop()
|
||||
return False
|
||||
|
||||
def play(self):
|
||||
if not self._data:
|
||||
return
|
||||
self._data.set_volume(self.volume)
|
||||
self._channel = self._data.play()
|
||||
self.start_time = Clock.time()
|
||||
# schedule event to check if the sound is still playing or not
|
||||
self._check_play_ev = Clock.schedule_interval(self._check_play, 0.1)
|
||||
super(SoundPygame, self).play()
|
||||
|
||||
def stop(self):
|
||||
if not self._data:
|
||||
return
|
||||
self._data.stop()
|
||||
# ensure we don't have anymore the callback
|
||||
if self._check_play_ev is not None:
|
||||
self._check_play_ev.cancel()
|
||||
self._check_play_ev = None
|
||||
self._channel = None
|
||||
super(SoundPygame, self).stop()
|
||||
|
||||
def load(self):
|
||||
self.unload()
|
||||
if self.source is None:
|
||||
return
|
||||
self._data = mixer.Sound(self.source)
|
||||
|
||||
def unload(self):
|
||||
self.stop()
|
||||
self._data = None
|
||||
|
||||
def seek(self, position):
|
||||
if not self._data:
|
||||
return
|
||||
if _platform == 'android' and self._channel:
|
||||
self._channel.seek(position)
|
||||
|
||||
def get_pos(self):
|
||||
if self._data is not None and self._channel:
|
||||
if _platform == 'android':
|
||||
return self._channel.get_pos()
|
||||
return Clock.time() - self.start_time
|
||||
return 0
|
||||
|
||||
def on_volume(self, instance, volume):
|
||||
if self._data is not None:
|
||||
self._data.set_volume(volume)
|
||||
|
||||
def _get_length(self):
|
||||
if _platform == 'android' and self._channel:
|
||||
return self._channel.get_length()
|
||||
if self._data is not None:
|
||||
return self._data.get_length()
|
||||
return super(SoundPygame, self)._get_length()
|
||||
|
||||
|
||||
SoundLoader.register(SoundPygame)
|
||||
BIN
kivy/core/audio/audio_sdl2.cpython-310-x86_64-linux-gnu.so
Executable file
BIN
kivy/core/audio/audio_sdl2.cpython-310-x86_64-linux-gnu.so
Executable file
Binary file not shown.
Reference in New Issue
Block a user