29a.ch

Entries tagged “python”

Play it slowly 1.3

After more than a year there is finally a new release of play it slowly. It does pretty much what you would expect, play back audio files at a different speed or pitch. Special thanks goes to Michael Donovan for helping me out with ideas and patches! I also created a debian package (what a pain).

New Features

  • Settings are now saved per file
  • There is a recent files button
  • There is a finetuner for changing the pitch in steps of 1 cent
  • The shortcuts for seeking were changed from 0 to ctrl+0 etc.
  • Moved sourcecode to github
  • Fixes all known bugs

I hope you like it. Feedback is appreciated!

screenshot

Get it Now

HTTP Ripper 1.1.0 released

logoI have finally made a new HTTP Ripper release. For those who don't know, HTTP Ripper is a tool that acts as http proxy to get (media) files out of websites. It is now translated into 42 languages. That's 40 languages more than I speak! The git repository has been moved over to over to github, play it slowly is now also there. Feel free to fork them.

HTTP Ripper Website

Autostart/Autorun with Python

Some time ago I wrote a little python module to automatically start programs when a user logs in. It works on Windows and Linux. On windows it creates registry entries, on linux it creates *.desktop files as defined in the Desktop Application Autostart Specification. You can use it like this:
import os
import autorun
autorun.add("myapp", os.path.abspath(__file__))
So here's the code:
#!/usr/bin/env python
"""A simple crossplatform autostart helper"""
from __future__ import with_statement

import os
import sys

if sys.platform == 'win32':
    import _winreg
    _registry = _winreg.ConnectRegistry(None, _winreg.HKEY_CURRENT_USER)
    def get_runonce():
        return _winreg.OpenKey(_registry,
                r"Software\Microsoft\Windows\CurrentVersion\Run", 0,
        _winreg.KEY_ALL_ACCESS)

    def add(name, application):
        """add a new autostart entry"""
        key = get_runonce()
        _winreg.SetValueEx(key, name, 0, _winreg.REG_SZ, application)
        _winreg.CloseKey(key)

    def exists(name):
        """check if an autostart entry exists"""
        key = get_runonce()
        exists = True
        try:
            _winreg.QueryValueEx(key, name)
        except WindowsError:
            exists = False
        _winreg.CloseKey(key)
        return exists

    def remove(name):
        """delete an autostart entry"""
        key = get_runonce()
        _winreg.DeleteValue(key, name)
        _winreg.CloseKey(key)
else:
    _xdg_config_home = os.environ.get("XDG_CONFIG_HOME", "~/.config")
    _xdg_user_autostart = os.path.join(os.path.expanduser(_xdg_config_home),
            "autostart")

    def getfilename(name):
        """get the filename of an autostart (.desktop) file"""
        return os.path.join(_xdg_user_autostart, name + ".desktop")

    def add(name, application):
        """add a new autostart entry"""
        desktop_entry = "[Desktop Entry]\n"\
            "Name=%s\n"\
            "Exec=%s\n"\
            "Type=Application\n"\
            "Terminal=false\n" % (name, application)
        with open(getfilename(name), "w") as f:
            f.write(desktop_entry)

    def exists(name):
        """check if an autostart entry exists"""
        return os.path.exists(getfilename(name))

    def remove(name):
        """delete an autostart entry"""
        os.unlink(getfilename(name))
def test():
    assert not exists("test_xxx")
    try:
        add("test_xxx", "test")
        assert exists("test_xxx")
    finally:
        remove("test_xxx")
    assert not exists("test_xxx")

if __name__ == "__main__":
    test()
I hope that helps somebody. :)

Generating Maps/Mazes with Python

Last weekend I wrote some code to generate random mazes in python. The algorithm creates fully connected mazes using backtracking. So between two points in the maze there is always exactly one path. In a game you would probably want to remove some more walls to create loops.

#!/usr/bin/env python
import sys
from random import shuffle

def shuffled(x):
    y = list(x)
    shuffle(y)
    return y

DIRECTIONS = (
    (0, -1),
    (0, 1),
    (1, 0),
    (-1, 0),
)

def make_maze(width, height, cellsize):
    cellsize1 = cellsize+1 # cellsize including one wall
    field_width = width*cellsize1+1
    field_height = height*cellsize1+1
    field = [0]*(field_width*field_height)
    stack = [(0, 0, shuffled(DIRECTIONS))]
    while stack:
        x, y, directions = stack[-1]
        dx, dy = directions.pop()
        # no other ways to go from here
        if not directions:
            stack.pop()
        # new cell
        nx = x+dx
        ny = y+dy
        # out of bounds
        if not (0 <= nx < width and 0 <= ny < height):
            continue
        # index of new cell in field
        fx = 1+nx*cellsize1
        fy = 1+ny*cellsize1
        fi = fx+fy*field_width
        # already visited
        if field[fi]:
            continue
        # tear down walls
        if dx > 0:
            a = -1
            b = field_width
        elif dx < 0:
            a = cellsize
            b = field_width
        elif dy > 0:
            a = -field_width
            b = 1
        else:
            a = cellsize*field_width
            b = 1
        for offset in xrange(cellsize):
            field[fi+a+b*offset] = 1
        # clear cell
        for y in xrange(0, cellsize):
            for x in xrange(0, cellsize):
                field[fi+x+y*field_width] = 1
        # visit cell
        stack.append([nx, ny, shuffled(DIRECTIONS)])
    return field

if __name__ == "__main__":
    if len(sys.argv) != 4:
        raise SystemExit("Usage: %s width height cellsize" % sys.argv[0])
    width, height, cellsize = map(int, sys.argv[1:])
    fields = make_maze(width, height, cellsize)
    w = (cellsize+1)*width+1
    h = (cellsize+1)*height+1
    for y in xrange(h):
        print "".join(map(lambda x: x and " " or "#", fields[y*w:y*w+w]))

And here some examples of the output:

#############################################################
#     #           #              #        #           #     #
#     #           #              #        #           #     #
#  ####  ####  #  #  ##########  #  #  #  #  #######  #  ####
#     #  #     #     #           #  #  #     #     #  #     #
#     #  #     #     #           #  #  #     #     #  #     #
####  #  #  ##########  #############  #######  #  #  ####  #
#     #  #  #  #     #     #           #     #  #  #        #
#     #  #  #  #     #     #           #     #  #  #        #
#  #######  #  #  #  ####  #######  ####  ####  #  #######  #
#           #     #  #     #        #  #     #  #        #  #
#           #     #  #     #        #  #     #  #        #  #
#############  ####  #  ####  #######  #  #  #  #  #######  #
#                 #           #           #     #           #
#                 #           #           #     #           #
#############################################################
################
#           #  #
#           #  #
#  #  #  #  #  #
#  #  #  #  #  #
#  #  #  #  #  #
#  ####  #  #  #
#        #     #
#        #     #
#############  #
#     #        #
#     #        #
#  ####  #######
#              #
#              #
################

Playitslowly 1.2 released

I finally found time to update playitslowly and make a new release.

Changes

  • Compatibility with python 2.6
  • New keyboard shortcuts
  • Back button
  • Improved setup routine
  • Added file command line argument

Compatibility with python 2.6 means that this release should work with Ubuntu 9.04 etc.

screenshot

Get it Now

Concatenating images using python

To generate sprites for a game I'm working on I needed to concatenate images side by side. I wrote a little python script to do the job. It requires the PIL.

from PIL import Image
import sys

if not len(sys.argv) > 3:
    raise SystemExit("Usage: %s src1 [src2] .. dest" % sys.argv[0])

images = map(Image.open, sys.argv[1:-1])
w = sum(i.size[0] for i in images)
mh = max(i.size[1] for i in images)

result = Image.new("RGBA", (w, mh))

x = 0
for i in images:
    result.paste(i, (x, 0))
    x += i.size[0]

result.save(sys.argv[-1])

Getting the number of workdays in a given timeframe

This is a weird function I wrote once to get the number of workdays (all days except saturday and sunday). Now the objective here was to do it in O(1) and without the use of any flow control. So I came up with this:

def get_work_days(startday, days):
    freq = 7
    startday = startday+1%freq
    sundayPhase = (freq - startday) % freq
    saturdayPhase = (sundayPhase + 6) % freq
    remaining = days % freq
    hasSunday = 0**((sundayPhase+1)/(remaining+1))
    hasSaturday = 0**((saturdayPhase+1)/(remaining+1))
    return (days - remaining) / freq * 5 + remaining - hasSunday - hasSaturday

It's abusing the fact that in most programming languages 0^0 is defined to be 1.

Atomic get and increment in python

For generating continuous unique id's in python I needed a thread safe way to do this:
x = counter
counter += 1
When disassembling this code we will get this:
  2           0 LOAD_FAST                0 (counter)
              3 STORE_FAST               1 (x)

  3           6 LOAD_FAST                0 (counter)
              9 LOAD_CONST               1 (1)
             12 INPLACE_ADD         
             13 STORE_FAST               0 (counter)
As you can see not even counter += 1 is atomic. Now the obvious solution would be to use a lock. The not so obvious solution is to use a itertools.counter(). The counter is implemented in C and doesn't release the GIL so it is atomic. The code would the look like this:
x = counter.next()
which is more pretty anyway.

PyGST messing with sys.argv

While developing play it slowly I noticed some strange behavior of the python gstreamer bindings. For some reason pygst parses argv on import. This illustrates the problem:
[veers@castle ~]$ python
Python 2.6.1 (r261:67515, Dec  7 2008, 08:27:41) 
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys.argv
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named argv
>>> import sys
>>> sys.argv.append("--help")
>>> import gst
Usage:
  . [OPTION...] - GStreamer initialization

Help Options:
  -?, --help                        Show help options
  --help-all                        Show all help options
  --help-gst                        Show GStreamer Options
That's not nice of you pygst! But there's a simple workaround:
argv = sys.argv
sys.argv = []
import gst
sys.argv = argv
I guess I should write a patch instead of a post but.. may be later.

Play it slowly 1.1

And there it is, a new version of play it slowly. It fixes all known bugs and even adds some features

  • Saving of state (file, speed, ..)
  • Improved error handling
  • Pitch scale is using semitones
  • Speed scale allows steps of 0.05
  • New command line argument --sink
  • Fixes all known bugs

If you've got suggestions please write a comment

screenshot

Get it Now
Author

Jonas Wagner Jonas Wagner
Software Engineer
Zürich, Switzerland

More about me

Follow 29a_ch on Twitter

Experiments

screenshot screenshot screenshot screenshot

More Experiments

Latest Posts Tags Archive Links

guitarmasterclass.net (guitar lessons)