According to the documentation Kivy 2.0.0 audio will likely not seek unless played first. However, I cannot get it to seek at all with this simple code on Windows 11. It always plays from the beginning and the position is always 0,
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
sound = SoundLoader.load('test.mp3')
def play():
sound.play()
sound.seek(5)
print(sound.get_pos())
if sound:
print("Sound found at %s" % sound.source)
print("Sound is %.3f seconds" % sound.length)
#Clock.schedule_once(play, .3)
play()
Also, I tried to schedule a delay using Clock and the audio does not play at all. What am I missing?
Edit:
It was suggested that this code needs to be wrapped in an app to work. That did fix the clock delay issue, but the player still will not seek ahead,
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
from kivy.app import App
from kivy.uix.button import Button
class MainApp(App):
sound = SoundLoader.load('test.mp3')
def build(self):
return Button(text='Play', on_press = self.clock)
def clock(self, evt):
Clock.schedule_once(self.play, 1)
def play(self, evt):
self.sound.play()
self.sound.seek(5)
#self.sound.play()
print(self.sound.get_pos())
MainApp().run()
After digging into the Kivy source code I found out why Kivy audio will not seek.
The default Kivy audio provider on Windows is SDL2 and when I looked at the source (kivy/core/audio/audio_sdl2.pyx) I found it does NOT have a seek method. Only "play" and "stop." In the init.py file the "seek" method returns "pass." However, the audio_ffpyplayer.py files DOES have a "seek" method. If one has ffpyplayer installed on his system Kivy will load it and seek will work, but with the default install it will not.
(It would be good if the Kivy Docs would reflect this. It would have saved me and others, I'm sure, a lot of "hair pulling.")
I tried ffpyplayer and it works good, but it has a lot of bloat. When packaged with pyinstaller there is around 40MB of added dlls. Since Windows already has the files needed to play audio, this bloat is redundant. While looking for a Python tool to play Windows audio using existing dlls, I found mp3play (https://github.com/michaelgundlach/mp3play). It is old but works flawlessly using only Window's native audio dlls and adds essentially no bloat. I think "playsound" will work as well.
Related
I'm making an app/game and the user is able to play a sound when pressing the sound button, and the same sound is played after the user finishes a level. The next level contains a new sound.
For some reason, my code doesn't act how I want it and the results aren't consistent: sometimes it works only once, sometimes it works twice or more. Sometimes the next level contains the new sound but most of the time it doesn't play anything.
It probably has something to do with the way I load/try to unload audio files because the code returns 'play' when printing out the audio state.
Here is a simplified version of the code:
import time
import kivy
from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.lang import Builder
from kivy.uix.widget import Widget
root_widget = Builder.load_file('app.kv')
class ExampleWidget(Widget):
def play_sound(self):
file = 'correct.wav'
print('playing ', file)
click_sound = SoundLoader.load(file)
click_sound.play()
time.sleep(2)
print(click_sound.state)
click_sound.stop()
print(click_sound.state)
#click_sound.unload() # Crashes the app
click_sound = ''
class MyApp(App):
def build(self):
return ExampleWidget()
MyApp().run()
app.kv:
<ExampleWidget>:
id: ew
GridLayout:
col: 2
rows: 3
size: root.size
Button:
text: 'play sound'
size: self.size
on_release: ew.play_sound()
I also tried to empty the click_sound variable by assigning it a None value, using 'del click_sound', tried emptying the variable at the start of the function, end or both and I also tried using the unload() function but that returns the following error:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
I'm also getting the following warning. I don't think it's a part of my problem but I'm adding it just in case:
[WARNING] [ffpyplayer ] [wav # 0x7fc738908e00] Discarding ID3 tags because more suitable tags were found.
Hopefully someone can help me out, I thank you in advance.
EDIT:
As ApuCoder said, the code works fine, just not on my macbook. Just tested it on a Windows pc and there were no problems. Does anybody know a way to fix this on Apple computers?
I am trying to pack an app that requires several imports, among those matplotlib.pyplot
The kivy app (simplified, but still working) is:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
import matplotlib.pyplot
Builder.load_string("""
<MyWidget>:
id: my_widget
FileChooserIconView:
id: filechooser
on_selection: my_widget.selected(filechooser.selection)
Image:
id: image
source: ""
""")
class MyWidget(BoxLayout):
def selected(self,filename):
self.ids.image.source = filename[0]
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
This app works perfectly in python using spyder.
However, when I try to pack it as an independent kivy app it gives me error maximum recursion depth exceeded.
I am surprised and I do not know what the problem is because:
1.There are no recursive functions in the app.
2.Works perfectly in python spyder while developing it and testing it, the only problem is during packaging.
3.I have tried multiple options, including commenting out several portions and, most surprising, when I comment out the import matplotlib.pyplot the app packages well. However I need matplotlib.pyplot for this app so taking it out is not an option.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
#import matplotlib.pyplot
Builder.load_string("""
<MyWidget>:
id: my_widget
FileChooserIconView:
id: filechooser
on_selection: my_widget.selected(filechooser.selection)
Image:
id: image
source: ""
""")
class MyWidget(BoxLayout):
def selected(self,filename):
self.ids.image.source = filename[0]
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
The above code works and packages well.
Is it that there is some limit of the size of the files one can import to a kivy app? I have already tried to increase the recursion limit with sys.setrecursionlimit(high numbers) but it is not a solution to this problem.
I am really lost. Any insight appreciated.
Thank you
Edit 2/4/2019:
It has been suggested that the question: pyinstaller creating EXE RuntimeError: maximum recursion depth exceeded while calling a Python object is a duplicate and answers this question. Although this is definitively a related question and is helpful, my error occurs in the first stage of the creation of the kivy package:
python -m PyInstaller --name touchtracer examples-path\demo\touchtracer\main.py
Thank you so MUCH to everybody who tried to help.
I found an answer and I hope it helps other people who tries to create a kivy package and there is a problem importing python module(s).
Once you have your main.py script ready to package:
1.Start with the instructions at
https://kivy.org/doc/stable/guide/packaging-windows.html
and do the first step:
python -m PyInstaller --name touchtracer examples-path\demo\touchtracer\main.py
This will give you the error of maximum recursion depth exceeded or whatever error this gives to you originally. No worries. The purpose of this step is to create an initial spec file.
2.Open the spec file and add all the extras that the kivy instructions give you at
https://kivy.org/doc/stable/guide/packaging-windows.html
that is:
from kivy.deps import sdl2, glew
Tree('examples-path\\demo\\touchtracer\\'),
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
3.In addition to that, add at the beginning of the spec file the following:
import sys
sys.setrecursionlimit(5000) # (or some big number)
4.Also add any imports in the hidden imports that you might need.
hiddenimports=[] # change to (example importing pandas and matplotlib) hiddenimports=['pandas', 'matplotlib']
5.Just follow the last step at
https://kivy.org/doc/stable/guide/packaging-windows.html
that is:
python -m PyInstaller touchtracer.spec
and get your app built
When we go into a recursion, there is a risk of stack overflow and the Cpython working under the hood does not take it upon itself to optimize tail recursion, so if you go too deep, you will move closer towards a stack overflow. Generally different Cpython/python flavors have different recursion permit depth, The version of python you have running locally has a more liberal depth limit (Generally because it is assumed developers have good enough computers that this can take place). But when you use tools to package your application they generally override the sys.setrecursionlimit to a more conservative value as they try to make sure you don't cause an stack overflow on systems with lower hardware.
Sadly there is no silver bullet for this problem, you can try to look into your specific manager and change the limit (Not recommended) or you can try to convert your recursive blocks into iterative blocks.
To trigger the acquisition of an image, I use the on_release-function of a kivy-button.
So, whenever this button is clicked (or pressed -- since using a touchscreen) a camera is triggered using gphoto2.
The issue:
From time to time, the function is executed multiple times (taking multiple images), while it was clearly pressed only a single time.
According to the logs, I'm confident, that it's a kivy-related issue (not related to the camera, etc.): Logging entries within the on_release-function appear multiple times within the logs.
I'm running an app with kivy (version 1.9.0) and python (version 2.7.6) under Ubuntu 14.04 LTS (64bit) using a touchscreen.
Any hint on how to debug or fix the issue is welcome.
I was stuck at the same problem for days!
Do you have this line in your code?
Config.set('input', 'mouse', 'mouse, multitouch_on_demand')
If yes, then remove it.
On touch devices, touch gets triggered multiple times.
Hope it helps someone with the same problem.
You could disable the button when the first release event starts, and in the end of the thread where the picture is taken, enable the button again. Then you will not have multiple events from that button executing, but still the main app thread is allowed to continue.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
import time
import threading
Builder.load_string('''
<MyLayout>:
Button:
text: 'Print to terminal'
on_release:
root.button_released(self)
''')
class MyLayout(BoxLayout):
def button_released(self,button):
button.disabled = True
print("{} pressed!".format(button))
threading.Thread(target=self.take_picture, args=(button,)).start()
def take_picture(self,button):
time.sleep(1) # taking picture or whatever
button.disabled = False
class MyApp(App):
def build(self):
return MyLayout()
MyApp().run()
I am new to Kivy and trying to find my way around. Whenever I create and run an app, it displays as a full-screen that I am unable to close without disconnecting the power (which I know is not ideal, but that's exactly why I am desperate to fix it!).
Shortcuts that are suggested to work (Esc, Ctrl+C, Ctrl+Alt+break) don't. I have attempted changing the config settings at the beginning of the script as follows:
from kivy.config import Config
Config.set('graphics', 'fullscreen', 0)
Config.write()
I've also tried variations on the theme - 0 as a string, 1 as both an integer and string (and trying to provide a width and height for the window) but with no perceivable change. Even if this did work, it would not be the ideal fix given that I would probably want to be able to run things full-screen in the end!
Given that each time I've tried changing something I've had to restart the pi by disconnecting the power, playing around has been quite time-consuming!
Does anybody have any suggestions about how I should proceed?
I'm currently using:
Raspberry Pi 2 Model B connected to normal TV (many people having problems have been using a touchscreen, but that is not true for me)
Raspbian Jessie, Linux 8
Python 2.7
I'm afraid I don't know how to check details about the Kivy module I have downloaded.
I'm very new to this, so apologies if I don't manage to provide all of the relevant information.
Full code I am trying to run (excluding the above config changes):
import kivy
kivy.require('1.9.2') #may be part of the problem - not 100% sure this is correct
from kivy.app import App
from kivy.uix.label import Label
class MyApp(App):
def build(self):
return Label(text='Hello world')
if __name__ == '__main__':
MyApp().run()
As a temporary workaround you could just do :
def build(self):
button = Button(text = 'Exit', size_hint = (.1, .05),
pos_hint = {'x':0, 'y':0})
button.bind(on_press = self.on_quit)
self.layout = FloatLayout()
self.layout.add_widget(button)
return self.layout
def on_quit(self):
exit()
Which would provide you with an exit button. For your fullscreen problem it's weird, can you provide some more code ?
EDIT:
Can you try this ? :
from kivy.config import Config
Config.set('graphics', 'borderless', 0)
Config.write()
To work around this issue, you can change the full screen to fake so kivy can exit on Ctrl+C.
from kivy.config import Config
Config.set('graphics', 'fullscreen', 'fake')
Config.write()
Also, try to run the code in command line prompt. Avoid raspberry pi's desktop environment while running kivy apps. This will free up pi's memory for running kivy.
I been trying all day to figure out the Qt's Phonon library with Python.
My long term goal is to see if I could get it to play a mms:// stream, but since I can't find an implementation of this done anywhere, I will figure that part out myself. (figured I'd put it out there if anyone knew more about this specifically, if not no big deal.)
Anyway, I figured I'd work backwards from a working example I found online. This launches a file browser and will play the mp3 file specified. I wanted to strip out the file browser stuff and get it down to the essentials of executing the script and having it play an Mp3 file with a hardcoded path.
I'm assuming my problem is a misunderstanding of setCurrentSource() and specifying the data types. (see: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/phonon-mediasource.html#fileName)
I'm keeping my question kind of broad because ANY help with understanding Phonon would be greatly appreciated.
import sys
from PyQt4.QtGui import QApplication, QMainWindow, QDirModel, QColumnView
from PyQt4.QtGui import QFrame
from PyQt4.QtCore import SIGNAL
from PyQt4.phonon import Phonon
class MainWindow(QMainWindow):
m_model = QDirModel()
def __init__(self):
QMainWindow.__init__(self)
self.m_fileView = QColumnView(self)
self.m_media = None
self.setCentralWidget(self.m_fileView)
self.m_fileView.setModel(self.m_model)
self.m_fileView.setFrameStyle(QFrame.NoFrame)
self.connect(self.m_fileView,
SIGNAL("updatePreviewWidget(const QModelIndex &)"), self.play)
def play(self, index):
self.delayedInit()
self.m_media.setCurrentSource(
Phonon.MediaSource(self.m_model.filePath(index)))
self.m_media.play()
def delayedInit(self):
if not self.m_media:
self.m_media = Phonon.MediaObject(self)
audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
Phonon.createPath(self.m_media, audioOutput)
def main():
app = QApplication(sys.argv)
QApplication.setApplicationName("Phonon Tutorial 2 (Python)")
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Phonon supports different audio file formats on different platforms, using the system's own support for media formats, so it could be that your system doesn't provide libraries for playing MP3 files. Certainly, MP3 is not supported out of the box on some Linux distributions. If you are using Linux, please take a look at the following page for information about enabling MP3 support:
http://doc.qt.io/qt-4.8/phonon-overview.html#linux
Another way to diagnose problems with Phonon's media formats is to run the Capabilities example provided with Qt:
http://doc.qt.io/qt-4.8///qt-phonon-capabilities-example.html
This should tell you which media formats are supported by your system.
In delayedInit method; create MediaObject like following:
def delayedInit(self):
if not self.m_media:
self.m_media = Phonon.createPlayer(Phonon.MusicCategory)
If Phonon is not outputting audio or video, but not throwing any errors. You might just have to sudo apt-get install phonon-backend-gstreamer and also maybe sudo apt-get install libphonon-dev
Phonon uses a backend of gstreamer or vlc silently, so when its not there, no errors but no functionality either.
after running those commands I was able to hear sound from phonon on my raspberry pi
Hopefully this will help someone in the future.