Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mp3 music #247

Open
dangillet opened this issue Sep 1, 2015 · 8 comments
Open

mp3 music #247

dangillet opened this issue Sep 1, 2015 · 8 comments

Comments

@dangillet
Copy link
Contributor

Hello,

I'm trying to play some music with cocos2d. I'm a bit lost as why my music is not played. Here is a minimal code using pyglet which works:

#!/usr/bin/python3
#-*- encoding: utf-8 -*-

import pyglet

pyglet.resource.path = ['assets']
pyglet.resource.reindex()

music = pyglet.resource.media('ThemeEcranTitre2.mp3')
music.play()

pyglet.app.run()

And here is my attempt in cocos2d which does not work (the windows open correctly but no music is played):

import cocos
from cocos.director import director

director.init()
intro = cocos.scene.Scene()
intro.load_music("assets/ThemeEcranTitre2.mp3")

director.run(intro)

Trying to trace the problem, I found that when the scene enters, cocos is calling cocos.audio.music.control.load(self.music)which actually calls the DummyMusicControl implementation in cocos\audio\music.py.

I want to use AVBin and it's installed on my machine. What am I doing wrong?

I tried to use pygame instead, passing audio_backend='sdl' to director.init(). But this gives this exception:

Traceback (most recent call last):
  File "audio_test.py", line 21, in <module>
    director.run(intro)
  File "C:\Python34\lib\site-packages\cocos\director.py", line 402, in run
    self._set_scene(scene)
  File "C:\Python34\lib\site-packages\cocos\director.py", line 525, in _set_scene
    scene.on_enter()
  File "C:\Python34\lib\site-packages\cocos\scene.py", line 114, in on_enter
    cocos.audio.music.control.load(self.music)
  File "C:\Python34\lib\site-packages\cocos\audio\music.py", line 49, in load
    pygame.music.load(filename)
NameError: name 'pygame' is not defined

Daniel.

@ccanepa
Copy link
Contributor

ccanepa commented Sep 1, 2015

sound services associated with scenes:

  1. those had been introduced with the SDL support, and never extended to AVbin. To use AVbin do entirelly as in pyglet, ie not calling cocos sound functions. Example in this pyweek entry http://code.google.com/p/aiamsori/
  2. that code is basically untested:
    people switching from AVbin to SDL adapted their code with a minor refactor, without using the scene related calls. See for example https://launchpad.net/enjuewemela
  3. I know no app using the scene sound api

The cost effective way for you would be to go with one of those approaches.

Additional info:
SDL was easier to distribute in Linux, and better 'just work' factor in user machines
Windows was happy with both backends, but before committing to SDL now I would test with win8 to win10 and the latest pygame available

@dangillet
Copy link
Contributor Author

Hi Claudio,

Thanks for pointing me in the right direction.

I tried the SDL (pygame) part, and here is what I found out. I tried this code

#!/usr/bin/python3
#-*- encoding: utf-8 -*-

import cocos
from cocos.director import director
from cocos.audio.pygame import mixer, music

director.init()
intro = cocos.scene.Scene()

mixer.init()
music.load('music.mp3')
music.play()
music.set_volume(1)

director.run(intro)

which throws this exception

Traceback (most recent call last):
  File "audio_test.py", line 22, in <module>
    music.load('music.mp3')
  File "C:\Python34\lib\site-packages\cocos\audio\pygame\music.py", line 86, in load
    _current_music = Mix_LoadMUS(filename)
  File "C:\Python34\lib\site-packages\cocos\audio\SDL\dll.py", line 241, in _f
    result = func(*args, **kwargs)
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type

I realized that I'm using Python 3, and strings are unicode objects. Mix_LoadMUS expects a const char *file. But we pass a wchar_t * (NUL terminated).

So I tried to encode my string like this:

music.load('music.mp3'.encode())

and I don't have a Traceback anymore. But I can see that the window is frozen (with a white background). Also I hear the typical sound made by Windows when there is an error, though I don't see any error message.

I tried the same code in Python 2, and I can hear the music.

It seems that the audio might have some problems under Python 3. I'm trying to figure out what it is, but any help (guidance) would be appreciated.

Daniel.

@dangillet
Copy link
Contributor Author

If this is of any help, when doing mixer.Mix_Linked_Version() I'm getting 1.2.13.

@ccanepa
Copy link
Contributor

ccanepa commented Sep 1, 2015

Excuse the conciseness, I'm in a hurry

Summary

  • fast way is use AVbin like from pyglet, ignoring the cocos scene audio stuff
  • expect to invest a considerable amount of time to ensure SDL works proper in modern windows

Background about SDL support:

  • SDL support was bring it by a Linux-only guy, in a 32 bit processors era, based in pygame-ctypes from around 2008, no tests;
    • at the time I added the only sdl sound test in cocos/test, fixing as needed to run the test in windows. No other formal tests.
  • made pygame a dependency in windows as a convenient way to install the SDL dlls; this was pygame 1.7.1
  • anyway, the game enjuewemela (see above response) rendered music and sound in Windows
  • conversion to py3 was minimal: no syntax errors, fixed some issues pointed by 2to3, the only sound test verified working in 2.7 and 3.3

At this point in time, there are probably bugs coming from

  • uncleaned linuxisms from the original code
  • problems in ctypes type declarations in a world of 64bits machines
  • unaccounted changes in the dlls provided by modern pygame, may be worth going back to pure SDL dependencies
  • the SDL support was using numpy for something. Is that working with modern numpy ?
  • errors in the py3 conversion, esp bytes handling
  • problems with dll resolution changes in windows from xp to win10
  • what lurks in C runtime interactions when considering python version, OS version, whatever SDL needed ?

I was never deeply familiarized with the SDL code, my 2to3 conversion skills are a bit rusty and this weeks I'm hard pressed for time.

So, here you are.

@dangillet
Copy link
Contributor Author

Your conciseness is not so concise. ;)

After a morning of debugging I found my problem. The short answer is that smpeg.dll could not be found. Copying it in the same directory as my script solves the problem.

The long answer: cocos loads SDL.dll by looking where the module pygame lives, and changes temporarily the current working directory to this path. It loads the dll and changes the current working directory back to where it was. But later on, SDL.dll need smpeg.dll in order to load mp3 files. And the pygame folder is not on the system path.

My python 2 worked because the dlls were all in C:\Python27\DLL and this folder is in my system path.

I guess the best course of action would be not to change temporarily the current working directory, but to temporarily add the pygame folder to the system path.

I still need to pass a bytes object as the filename parameter to the music.load function to have it work. And I also found (obviously) that whatever is retrieved by SDL_GetError() is a bytes object under python 3. So assigning it to the __init__ method of the class SDL_Exception(Exception) is not what we want. The __str__ method won't work with a bytes object. As a quick fix, I just replaced in cocos.audio.SDL.dll.py every instance of raise cocos.audio.SDL.error.SDL_Exception(cocos.audio.SDL.error.SDL_GetError()) by raise cocos.audio.SDL.error.SDL_Exception(cocos.audio.SDL.error.SDL_GetError().decode()). At least I could see a meaningful error message.

Final observation when trying to use the untested Scene.load_music method, python 3 will throw a ImportError exception in
cocos.audio.pygame.mixer.py line 51:

try:
    from cocos.audio.pygame import music
    _have_music = True
except ImportError:
    _have_music = False

due to circular dependency which unfortunately is silently caught. The offending line in cocos.audio.pygame.music.py is on line 20 from . import mixer.

I'm not experimented enough to know the correct way to fix all this. I can try to give it a shot. But your ideas / suggestions would be very much appreciated. Of course, only when you have some more time. Nothing urgent here!

Dan.

@ryukinix
Copy link

Some workaround to reproduce mp3 files using cocos?

@dangillet
Copy link
Contributor Author

Personally I ended up creating these functions in a sound.py file

from pygame import mixer
from pygame.mixer import music
import pyglet

import cocos

mixer.pre_init(44100, -16, 2, 1024)
mixer.init()

# Make sure we exit the mixer when the application quits
cocos.director.event_loop.push_handlers(on_exit=mixer.quit)

def play(soundtrack):
    # music.load(bytes(os.path.join("assets", "music", soundtrack), 'utf-8'))
    music.load(pyglet.resource.file(soundtrack))
    music.set_volume(0.5)
    music.play(loops=-1)

def transition(soundtrack, time):
    music.fadeout(time * 1000)
    pyglet.clock.schedule_once(next_music, time, soundtrack)

def next_music(dt, soundtrack):
    play(soundtrack)

Of course pygame should be installed and make sure you have also smpeg installed.

I found out that for some unknown reasons, sometimes mp3 files had a delay to start playing, like 3-5 seconds. I reverted back to .ogg files.

I hope it helps.

Daniel.

@swistakm
Copy link

If you dont want to use pygame on your cocos application then maybe pyrilla is worth checking. It is simple and lightweight audio mixer that has no extrernal dependencies. It has no mp3 support but it can easily play ogg and wave audio.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants