Ajout du GUI

This commit is contained in:
thatscringebro
2022-08-08 16:31:52 -04:00
parent db362ccdca
commit abd15f28b6
851 changed files with 99957 additions and 1 deletions

244
kivy/core/audio/__init__.py Normal file
View 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)

Binary file not shown.

View 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)

View 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)

View 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)

View 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)

View 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)

Binary file not shown.