I am trying to build a simple media tool in Pyglet, which requires a seek feature. Files are loaded, paused, and then told to seek to a specific time; however, the file does not seek when Player.seek() is called. Below is the test code I am using:
import os
import pyglet
from os.path import abspath, isfile
def loadsong(filename):
# check for file
print("Attempting to load "+filename)
filename = abspath(filename)
if not ( isfile(filename) ):
raise Exception(filename+" not found.")
# create a player for this file
song = pyglet.media.load(filename)
source = song.play()
source.eos_action = source.EOS_LOOP
source.pause()
return source
music = loadsong("test.mp3")
music.seek(57)
music.play()
pyglet.app.run()
What am I doing wrong here? I am using Python 3.5.2, Pyglet 1.2 alpha 1, and AVBin 11 alpha 4.
First of all, the error you're getting is quite important.
But I'll assume it's a Segmentation fault (core dumped) on the row:
music.seek(12)
Another quick note, fix your indentation! You're using 3 spaces and whether you are a space guy or a tab guy - 3 spaces is just odd -
The reason for you getting a segmentation fault when trying to seek is most likely because of AVbin, it's an old (and dead afaik) project.
I hacked together something more similar to a music player and here's an example on how you can use Seek with wav files:
import pyglet
from pyglet.gl import *
from os.path import abspath, isfile
pyglet.options['audio'] = ('pulseaudio', 'alsa', 'openal', 'silent')
pyglet.have_avbin=False
key = pyglet.window.key
def loadsong(filename):
# check for file
filename = abspath(filename)
# create a player for this file
player = pyglet.media.Player()
player.queue(pyglet.media.load(filename, streaming=False))
#player.play()
#song.eos_action = song.EOS_LOOP
#song.pause()
return player
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(300, 300, fullscreen = False)
self.alive = 1
self.player = loadsong('./test.wav')
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
elif symbol == key.SPACE:
if self.player.playing:
self.player.pause()
else:
self.player.play()
elif symbol == key.RIGHT:
print('Skipping to:',self.player.time+2)
self.player.source.seek(self.player.time+2)
elif symbol == key.LEFT:
print('Rewinding to:',self.player.time-2)
self.player.source.seek(self.player.time-2)
def render(self):
self.clear()
#source = pyglet.text.Label(str(self.player.source.info.title.decode('UTF-8')), x=20, y=300-30)
volume = pyglet.text.Label(str(self.player.volume*100)+'% volume', x=20, y=40)
p_time = pyglet.text.Label(str(self.player.time), x=20, y=20)
#source.draw()
volume.draw()
p_time.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
x = main()
x.run()
A few important notes:
pyglet.have_avbin=False
This will turn off AVbin completely, there is probably a way to turn it off for individual sources.. But since I rarely play around with it I honestly have no idea of how to.. So off it goes :)
Secondly:
streaming=False
On the media.load() call is quite important, otherwise you might get weird artifacts in your sound and or no sound at all. I got instance to a super high pitched scratching noise that almost made me deaf without that flag.
Other than that, the code is quite straight forward.
self.player.source.seek(<time>)
Is called on the player object that is a instance of pyglet.media.Player(). And it works brilliantly.
Another work-around would be to manually install AVbin 7 which appears to be working better, but I'm reluctant to install it on this machine just for testing purposes.. But the overall info i've gathered over the years is that that old library works better with Mp3 files.
Please ensure Player.playing is True before invoking Player.seek().
In the end of loadsong() function, you invoked the pause() that will set the Player.playing to False.
you could try:
music.play()
music.seek(57)
instead of:
music.seek(57)
music.play()
The following example uses pyglet 1.5.6, the seek() function works on my macbook pro, but negative on my windows 10:
import pyglet
from pyglet.window import key
source = pyglet.media.load(VIDEO_FILE_PATH)
fmt = source.video_format
player = pyglet.media.Player()
player.queue(source)
player.play()
window = pyglet.window.Window(width=fmt.width, height=fmt.height)
#window.event
def on_draw():
player.get_texture().blit(0, 0)
#window.event
def on_key_press(symbol, modifiers):
if symbol == key.LEFT:
player.seek(player.time - 3.5)
elif symbol == key.RIGHT:
player.seek(player.time + 3.5)
elif symbol == key.SPACE:
if player.playing:
player.pause()
else:
player.play()
pyglet.app.run()
Related
I have designed an application which have two buttons i.e CAL and SAV.
Accordingly that I have two functions but the issue is sometimes production line Operator by mistake presses SAV button. So that attribute error arises and program stuck.
How to overcome this issue? Please guide me.
Here is my code:
class ADS1x15:
"""Base functionality for ADS1x15 analog to digital converters."""
class ADS1115(ADS1x15):
"""Class for the ADS1115 16 bit ADC."""
class AnalogIn:
"""AnalogIn Mock Implementation for ADC Reads."""
import RPi.GPIO as GPIO
import tkinter as tk
GPIO.setmode(GPIO.BCM)
GPIO.setup(12,GPIO.IN) #Save Button
GPIO.setup(5,GPIO.IN) #Cal Button
root=tk.Tk()
root.geometry("1000x600")
file = open("/home/pi/data_log.txt", "r")
f = file.read().split(',')
rangeh = int(f[3])
offset = int(f[4])
fullScale = int(f[5])
chan=AnalogIn(ads,P0,P1)
def cal(channel):
global Dsel,cal_c,rangeh,offset,fullScale,chan
cal_c = cal_c + 1
if cal_c == 1:
root.L1 = tk.Label(root,text="Put Zero Weight and Press CAL btn",fg="brown",font="bold")
root.L1.pack()
root.L1.place(x=1,y=1)
elif cal_c == 2:
root.L1.destroy()
offset = chan.value
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh)+","+str(offset)+","+str(fullScale))
file.flush()
root.L2 = tk.Label(root,text="Put Full Weight and Press SAV btn",fg="brown",font="bold")
root.L2.pack()
root.L2.place(x=1,y=1)
def sav(channel):
global rangeh,offset,fullScale
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh)+","+str(offset)+","+str(fullScale))
file.flush()
root.L2.destroy()
def update():
""" function for continuous show value in every 500ms in tkinter window"""
GPIO.add_event_detect(5,GPIO.RISING,callback=cal,bouncetime=1000)
GPIO.add_event_detect(12,GPIO.RISING,callback=sav,bouncetime=1000)
root.after(500,update)
root.mainloop()
This error generated due to root.L2.destroy() this line.
Can I block or disable this sav function, so that without call of cal function, it shouldn't execute?
A brute force solution would be to check whether root has an L2 attribute or not
from tkinter import messagebox
def sav(channel):
if hasattr(root, 'L2'):
global rangeh, offset, fullScale
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh) + "," + str(offset) + "," + str(fullScale))
file.flush()
root.L2.destroy()
else:
messagebox.showinfo('Unable to save', 'No data was generated yet')
A more elegant approach would be to disable the save button on startup and only enable it after the cal function has been executed.
I am not very familiar with Raspberry Pi implementations, so this is only a rough sketch on how to achieve the button disabling:
By the looks of it, the buttons are "wired in" via the GPIO.add_event_detect functions.
So i would remove the sav-callback from the main script and dynamically add it after the cal script, something like that:
# [...] beginning of your script [...]
def cal(channel):
# [...] original body of cal function [...]
activate_save_button()
def activate_save_button():
GPIO.add_event_detect(12, GPIO.RISING, callback=sav, bouncetime=1000)
def deactivate_save_button():
GPIO.remove_event_detect(12)
def sav(channel):
# [...] original body of sav function [...]
# remove save button functionality after saving
deactivate_save_button()
def update():
""" function for continuous show value in every 500ms in tkinter window"""
GPIO.add_event_detect(5, GPIO.RISING, callback=cal, bouncetime=1000)
# line with callback=sav is deleted here
root.after(500, update)
root.mainloop()
This is my code that I can't get to work. It is saying that my object doesn't have a score attribute when I try to get the player1 and player2 score with player1.score. I checked for indenting errors and couldn't find any. I am not sure why the score is missing as I have given it self. I brought in the class object with player1 = Player(PLAYER1, 1, DEFAULT_SETS) with the PLAYER1 and the DEFAULT_SETS being variables I setup in a settings file. The error is at the #points part of the code in the Game class.
class Game:
#Init function to start the game variables
def __init__(self):
pg.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
pg.key.set_repeat(500,100)
#self.screen_scenario = WELCOME
self.load_data()
self.Game_Deuce = False
self.Game_Tiebreaker = False
#self.Game_Winner = False
self.sets = DEFAULT_SETS
self.serve = random.choice([1,2])
#Function to start a new game
def new(self):
self.paused = False
self.screen.fill(DARKGREEN)
player1 = Player(PLAYER1, 1, DEFAULT_SETS)
player2 = Player(PLAYER2, 0, DEFAULT_SETS)
#Function to run the scoreboard
def run(self):
self.playing = True
#pg.mixer.music.play(loops=-1)
#pg.mixer.music.set_volume(BG_MUSIC_VOLUME)
while self.playing:
self.dt = self.clock.tick(FPS) / 1000
self.events()
if not self.paused:
self.update()
self.draw()
#Points
self.draw_text(str(POINTS[player1.score]), self.points_font, 180, YELLOW, POINTS_BOX_X_2, POINTS_BOX_Y_2)
self.draw_text(str(POINTS[player2.score]), self.points_font, 180, YELLOW, POINTS_BOX_X_1, POINTS_BOX_Y_1)
This is in a seperate file but has been imported correctly :
class Player:
def __init__(self, name, serving, sets):
self.name = name
sets = sets
self.score = int(0)
self.games = int(0)
self.sets = int(0)
self.advantage = False
if serving == 1:
self.serving = True
#mathtick
"Make sure to restart the python process to check for re-import errors. If you are running this as a script it shouldn't be the issues"
and
#Matthias
"In the function new you set player1 = Player(PLAYER1, 1, DEFAULT_SETS). player1 is local to that function, so you can't use it in the function run"
I added self to the variables that Matthias pointed out and restarted Python and that fixed it
And UPDATE/HINTS:
If you really want to "cheat" importlib.reload is your frenemy. I generally put this in my IPython onstart.py :
from importlib import reload
And then in IPython I live in this human-dev-loop:
In [29]: import my.lib as m
In [30]: # edit code
In [31]: reload(m)
Keep in mind this does not reload all modules. If you have other files you need reload them too in the right order. Use up arrows in IPython REPL. There are autoreload libraries but I don't trust them to always get things right and you will go mad. Remember reload is a dodgy dev hack, not everything always works. Use it if to cheat, but restart if things get weird. Survive and repeat.
I was trying to play a song using Pyglet, but encountered this error
NotImplementedError: Static sources not supported for video yet.
But the file is mp3 format. I have AVbin11-win64.exe installed (avbin64.dll) which is copied in 'C:\Windows\SysWOW64' Folder, downloaded from https://github.com/AVbin/AVbin/downloads.
Here is the script I am using :
import pyglet
player = pyglet.media.Player()
source = pyglet.media.load(r'C:\Users\MANDAV\Desktop\New folder (2)\Diamond-
Platnumz-All-The-Way-Up-v2.mp3', streaming=False)
player.play()
player.app.run()
Your code contains a few issues.
The first being player.app.run(), player doesn't have .app - you're probably looking for pyglet.app.run().
The second thing is that you try to call player.play(), but you never did player.queue(source). So either do source.play() or player.queue(source) (both does exactly the same thing, playing the source directly will create a player in the background.)
The third thing being streaming=False. avbin (10 at least) have issues decoding mp3 as a static source. Not quite sure why, but the project has been dead for soon to be a century (yes, in just a few short years, this code has been dead for 10 years).
Here's a minimal working example:
import pyglet
source = pyglet.media.load('epic.mp3')
source.play()
pyglet.app.run()
Altho, you probably want some more functionality while playing your stuff. So here's a bigger example of how you could put together a start for a music playing application:
import pyglet
from pyglet.gl import *
from collections import OrderedDict
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.keys = OrderedDict() # This just keeps track of which keys we're holding down. In case we want to do repeated input.
self.alive = 1 # And as long as this is True, we'll keep on rendering.
## Add more songs to the list, either here, via input() from the console or on_key_ress() function below.
self.songs = ['A.wav', 'B.wav', 'C.wav']
self.song_pool = None
self.player = pyglet.media.Player()
for song in self.songs:
media = pyglet.media.load(song)
if self.song_pool is None:
## == if the Song Pool hasn't been setup,
## we'll set one up. Because we need to know the audio_format()
## we can't really set it up in advance (consists more information than just 'mp3' or 'wav')
self.song_pool = pyglet.media.SourceGroup(media.audio_format, None)
## == Queue the media into the song pool.
self.song_pool.queue(pyglet.media.load(song))
## == And then, queue the song_pool into the player.
## We do this because SourceGroup (song_pool) as a function called
## .has_next() which we'll require later on.
self.player.queue(self.song_pool)
## == Normally, you would do self.player.eos_action = self.function()
## But for whatever fucky windows reasons, this doesn't work for me in testing.
## So below is a manual workaround that works about as good.
self.current_track = pyglet.text.Label('', x=width/2, y=height/2+50, anchor_x='center', anchor_y='center')
self.current_time = pyglet.text.Label('', x=width/2, y=height/2-50, anchor_x='center', anchor_y='center')
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_release(self, symbol, modifiers):
try:
del self.keys[symbol]
except:
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
elif symbol == key.SPACE:
if self.player.playing:
self.player.pause()
else:
self.player.play()
elif symbol == key.RIGHT:
self.player.seek(self.player.time + 15)
## == You could check the user input here,
## and add more songs via the keyboard here.
## For as long as self.song_pool has tracks,
## this player will continue to play.
self.keys[symbol] = True
def end_of_tracks(self, *args, **kwargs):
self.alive=0
def render(self):
## Clear the screen
self.clear()
## == You could show some video, image or text here while the music plays.
## I'll drop in a example where the current Track Name and time are playing.
## == Grab the media_info (if any, otherwise this returns None)
media_info = self.player.source.info
if not media_info:
## == if there were no meta-data, we'll show the file-name instead:
media_info = self.player.source._file.name
else:
## == But if we got meta data, we'll show "Artist - Track Title"
media_info = media_info.author + ' - ' + media_info.title
self.current_track.text = media_info
self.current_track.draw()
## == This part exists of two things,
## 1. Grab the Current Time Stamp and the Song Duration.
## Check if the song_pool() is at it's end, and if the track Cur>=Max -> We'll quit.
## * (This is the manual workaround)
cur_t, end_t = int(self.player.time), int(self.player.source._get_duration())
if self.song_pool.has_next() is False and cur_t >= end_t:
self.alive=False
## 2. Show the current time and maximum time in seconds to the user.
self.current_time.text = str(cur_t)+'/'+str(end_t) + 'seconds'
self.current_time.draw()
## This "renders" the graphics:
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
x = main()
x.run()
If you're just interested in playing sounds.
I wholeheartedly recommend Pydub.
Not only does it use libav which is more of a defacto standard when encoding and decoding, but it's also rich in features. Such as converting .mp3 to .wav and you can control audio gain, play two sources at the same time etc.
me and my friend are working on a game and we want our music to loop as long as the game is running. Help please there seems to be no function to put music on repeat
In current versions of pyglet, you should use a SourceGroup, setting the loop attribute to True. You can then queue it into a Player to play it:
snd = pyglet.media.load('sound.wav')
looper = pyglet.media.SourceGroup(snd.audio_format, None)
looper.loop = True
looper.queue(snd)
p = pyglet.media.Player()
p.queue(looper)
p.play()
Not sure if there's a more compact way of doing this but it seems to work...
To make a sound play in a loop, you can use a Player:
# create a player and queue the song
player = pyglet.media.Player()
sound = pyglet.media.load('lines.mp3')
player.queue(sound)
# keep playing for as long as the app is running (or you tell it to stop):
player.eos_action = pyglet.media.SourceGroup.loop
player.play()
To play more background sounds simultaneously, just start up another player for each of the sounds, with the same EOS_LOOP "eos_action" setting as above for each of them.
For playing continuously you can use this code
This will allow you to play files from your root directory
import pyglet
from pyglet.window import key
import glob
window = pyglet.window.Window(1280, 720, "Python Player", resizable=True)
window.set_minimum_size(400,300)
songs=glob.glob("*.wav")
player=pyglet.media.Player()
#window.event
def on_key_press(symbol, modifiers):
if symbol == key.ENTER:
print("A key was pressed")
#window.event
def on_draw():
global player
for i in range(len(songs)):
source=pyglet.resource.media(songs[i])
player.queue(source)
player.play()
pyglet.app.run()
this works for me
myplayer = pyglet.media.Player()
Path = "c:/path/to/youraudio.mp3"
source = pyglet.media.load(filename=source, streaming=False)
myplayer.queue(self.slowCaseSongSource)
myplayer.eos_action = 'loop'
this might be irrelevant:
import pyglet
import time
import random
#WARNING: You have to download your own sounds and define them like this:
#sound_1 = pyglet.resource.media("sound_file.wav", streaming=False)
#replacing "sound_file" with your own file.
# Add the sound variables here
BACKGROUND_OPTIONS = ()
player = pyglet.media.Player()
def play_background_sound():
global player
player.queue(random.choice(BACKGROUND_OPTIONS))
player.play()
# This is optional; it's just a function that keeps the player filled so there aren't any breaks.
def queue_sounds():
global player
while True:
player.queue(random.choice(BACKGROUND_OPTIONS))
time.sleep(60) # change this if the background music you have is shorter than 3 minutes
threading.Thread(target=queue_sounds).start()
I'm coding a python script using several commanline tools like top, so i need a proper visual feedback. Now it is time to give it a menu, so here comes the problem.
I found here a great approach of what i need, but every try to display a feedback before come back to previous menu is futile.
I just need menus, submenus, launch commands, terminate it, and back to previous menu. a GREAT bonus would be to run them in a split of the term.
Is there any pattern/skeleton/stuff/whatever to use as template in order to display several kind of widget with a predictable output?
here is a example of code,which two examples of functions to run:
#!/usr/bin/env python2
import curses
from curses import panel
class Menu(object):
def __init__(self, items, stdscreen):
self.window = stdscreen.subwin(0,0)
self.window.keypad(1)
self.panel = panel.new_panel(self.window)
self.panel.hide()
panel.update_panels()
self.position = 0
self.items = items
self.items.append(('exit','exit'))
def navigate(self, n):
self.position += n
if self.position < 0:
self.position = 0
elif self.position >= len(self.items):
self.position = len(self.items)-1
def display(self):
self.panel.top()
self.panel.show()
self.window.clear()
while True:
self.window.refresh()
curses.doupdate()
for index, item in enumerate(self.items):
if index == self.position:
mode = curses.A_REVERSE
else:
mode = curses.A_NORMAL
msg = '%d. %s' % (index, item[0])
self.window.addstr(1+index, 1, msg, mode)
key = self.window.getch()
if key in [curses.KEY_ENTER, ord('\n')]:
if self.position == len(self.items)-1:
break
else:
self.items[self.position][1]()
elif key == curses.KEY_UP:
self.navigate(-1)
elif key == curses.KEY_DOWN:
self.navigate(1)
self.window.clear()
self.panel.hide()
panel.update_panels()
curses.doupdate()
######################################################### !#
######################################################### !#
############# HERE MY FUNCTIONS examples
############ Everithing works OK, but displays it awfully
def GetPid(name):
import subprocess
command= str(("""pgrep %s""") % name )
p = subprocess.Popen(command, shell = True, stdout = subprocess.PIPE)
procs = []
salida = p.stdout
for line in salida:
procs.append(str.strip(line))
return procs
def top():
os.system("top")
def menuGP():
print GetPid("top")
######################################################### !#
class MyApp(object):
def __init__(self, stdscreen):
self.screen = stdscreen
curses.curs_set(0)
submenu_items = [
('beep', curses.beep),
('top', top)
]
submenu = Menu(submenu_items, self.screen)
main_menu_items = [
('get PID', GetPid),
('submenu', submenu.display)
]
main_menu = Menu(main_menu_items, self.screen)
main_menu.display()
if __name__ == '__main__':
curses.wrapper(MyApp)
Thanks in advise (and sorry for my rough english)
You really have two choices. One you can leave curses mode, execute your program, then resume curses. Two, you can execute your program asynchronously, parse its output and write it to the screen.
The good news on the first option is that you don't actually need to write any fancy save_state / load_state methods for the ui. Curses does this for you. Here's a simple example to show my point
import curses, time, subprocess
class suspend_curses():
"""Context Manager to temporarily leave curses mode"""
def __enter__(self):
curses.endwin()
def __exit__(self, exc_type, exc_val, tb):
newscr = curses.initscr()
newscr.addstr('Newscreen is %s\n' % newscr)
newscr.refresh()
curses.doupdate()
def main(stdscr):
stdscr.addstr('Stdscreen is %s\n' % stdscr)
stdscr.refresh()
time.sleep(1)
with suspend_curses():
subprocess.call(['ls'])
time.sleep(1)
stdscr.refresh()
time.sleep(5)
curses.wrapper(main)
If you run the example, you will notice that the screen created by curses.wrapper and the one created in curses.initscr when resuming are the same object. That is, the window returned by curses.initscr is a singleton. This lets us exit curses and resume like above without having to update each widget's self.screen references each time.
The second option is much more involved but also much more flexible. The following is just to represent the basic idea.
class procWidget():
def __init__(self, stdscr):
# make subwindow / panel
self.proc = subprocess.Popen(my_args, stdout=subprocess.PIPE)
def update(self):
data = self.proc.stdout.readline()
# parse data as necessary
# call addstr() and refresh()
Then somewhere in your program you will want to call update on all your procWidgets on a timer. This gives you the option of making your subwindow any size/place so you can have as many procWidgets as will fit. You will have to add some handling for when the process terminates and other similar events of course.