mirror of https://github.com/kivy/kivy.git
Merge pull request #4372 from Cheaterman/master
Added pitch shifting to audio_sdl2
This commit is contained in:
commit
18b0332d67
|
@ -0,0 +1,42 @@
|
|||
# encoding: utf8
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.lang import Builder
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.audio import SoundLoader
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.button import Button
|
||||
|
||||
from sys import version_info
|
||||
|
||||
|
||||
NOTES = (
|
||||
('Do', 1),
|
||||
('Ré', 9/8.),
|
||||
('Mi', 5/4.),
|
||||
('Fa', 4/3.),
|
||||
('Sol', 3/2.),
|
||||
('La', 5/3.),
|
||||
('Si', 15/8.),
|
||||
)
|
||||
|
||||
class Test(App):
|
||||
def build(self):
|
||||
self.sound = SoundLoader.load(
|
||||
'/usr/lib64/python{}.{}/test/audiodata/pluck-pcm32.wav'
|
||||
.format(*version_info[0:2])
|
||||
)
|
||||
root = BoxLayout()
|
||||
for octave in range(-2, 3):
|
||||
for note, pitch in NOTES:
|
||||
button = Button(text=note)
|
||||
button.pitch = pitch * 2 ** octave
|
||||
button.bind(on_release=self.play_note)
|
||||
root.add_widget(button)
|
||||
return root
|
||||
|
||||
def play_note(self, button):
|
||||
self.sound.pitch = button.pitch
|
||||
self.sound.play()
|
||||
|
||||
Test().run()
|
|
@ -50,10 +50,12 @@ from kivy.core import core_register_libs
|
|||
from kivy.compat import PY2
|
||||
from kivy.resources import resource_find
|
||||
from kivy.properties import StringProperty, NumericProperty, OptionProperty, \
|
||||
AliasProperty, BooleanProperty
|
||||
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.
|
||||
|
@ -116,6 +118,16 @@ class Sound(EventDispatcher):
|
|||
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.9.2
|
||||
|
||||
: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'.
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ Depending the compilation of SDL2 mixer and/or installed libraries:
|
|||
__all__ = ('SoundSDL2', 'MusicSDL2')
|
||||
|
||||
include "../../../kivy/lib/sdl2.pxi"
|
||||
include "../../../kivy/graphics/common.pxi" # For malloc and memcpy (on_pitch)
|
||||
|
||||
from kivy.core.audio import Sound, SoundLoader
|
||||
from kivy.logger import Logger
|
||||
|
@ -36,11 +37,6 @@ from kivy.clock import Clock
|
|||
cdef int mix_is_init = 0
|
||||
cdef int mix_flags = 0
|
||||
|
||||
# old code from audio_sdl, never used it = unfinished?
|
||||
#cdef void channel_finished_cb(int channel) nogil:
|
||||
# with gil:
|
||||
# print('Channel finished playing.', channel)
|
||||
|
||||
|
||||
cdef mix_init():
|
||||
cdef int audio_rate = 44100
|
||||
|
@ -72,14 +68,13 @@ cdef mix_init():
|
|||
mix_is_init = -1
|
||||
return 0
|
||||
|
||||
#Mix_ChannelFinished(channel_finished_cb)
|
||||
|
||||
mix_is_init = 1
|
||||
return 1
|
||||
|
||||
# Container for samples (Mix_LoadWAV)
|
||||
cdef class ChunkContainer:
|
||||
cdef Mix_Chunk *chunk
|
||||
cdef Mix_Chunk *original_chunk
|
||||
cdef int channel
|
||||
|
||||
def __init__(self):
|
||||
|
@ -92,6 +87,9 @@ cdef class ChunkContainer:
|
|||
Mix_HaltChannel(self.channel)
|
||||
Mix_FreeChunk(self.chunk)
|
||||
self.chunk = NULL
|
||||
if self.original_chunk != NULL:
|
||||
Mix_FreeChunk(self.original_chunk)
|
||||
self.original_chunk = NULL
|
||||
|
||||
# Container for music (Mix_LoadMUS), one channel only
|
||||
cdef class MusicContainer:
|
||||
|
@ -158,6 +156,26 @@ class SoundSDL2(Sound):
|
|||
frames = points / channels
|
||||
return <double>frames / <double>freq
|
||||
|
||||
def on_pitch(self, instance, value):
|
||||
cdef ChunkContainer cc = self.cc
|
||||
cdef int freq, channels
|
||||
cdef unsigned short fmt
|
||||
cdef SDL_AudioCVT cvt
|
||||
if cc.chunk == NULL:
|
||||
return
|
||||
if not Mix_QuerySpec(&freq, &fmt, &channels):
|
||||
return
|
||||
SDL_BuildAudioCVT(
|
||||
&cvt,
|
||||
fmt, channels, int(freq * self.pitch),
|
||||
fmt, channels, freq,
|
||||
)
|
||||
cvt.buf = <Uint8 *>malloc(cc.original_chunk.alen * cvt.len_mult)
|
||||
cvt.len = cc.original_chunk.alen
|
||||
memcpy(cvt.buf, cc.original_chunk.abuf, cc.original_chunk.alen)
|
||||
SDL_ConvertAudio(&cvt)
|
||||
cc.chunk = Mix_QuickLoad_RAW(cvt.buf, <Uint32>(cvt.len * cvt.len_ratio))
|
||||
|
||||
def play(self):
|
||||
cdef ChunkContainer cc = self.cc
|
||||
self.stop()
|
||||
|
@ -201,7 +219,10 @@ class SoundSDL2(Sound):
|
|||
Logger.warning('AudioSDL2: Unable to load {}: {}'.format(
|
||||
self.filename, Mix_GetError()))
|
||||
else:
|
||||
cc.original_chunk = Mix_QuickLoad_RAW(cc.chunk.abuf, cc.chunk.alen)
|
||||
cc.chunk.volume = int(self.volume * 128)
|
||||
if self.pitch != 1.:
|
||||
self.on_pitch(self, self.pitch)
|
||||
|
||||
def unload(self):
|
||||
cdef ChunkContainer cc = self.cc
|
||||
|
|
|
@ -775,6 +775,30 @@ cdef extern from "SDL_ttf.h":
|
|||
|
||||
cdef extern from "SDL_audio.h":
|
||||
cdef int AUDIO_S16SYS
|
||||
ctypedef struct SDL_AudioFilter:
|
||||
pass
|
||||
ctypedef struct SDL_AudioCVT:
|
||||
int needed
|
||||
int src_format
|
||||
int dst_format
|
||||
double rate_incr
|
||||
Uint8 *buf
|
||||
int len
|
||||
int len_cvt
|
||||
int len_mult
|
||||
double len_ratio
|
||||
SDL_AudioFilter filters[10]
|
||||
int filter_index
|
||||
cdef int SDL_BuildAudioCVT(
|
||||
SDL_AudioCVT *cvt,
|
||||
int src_format,
|
||||
Uint8 src_channels,
|
||||
int src_rate,
|
||||
int dst_format,
|
||||
Uint8 dst_channels,
|
||||
int dst_rate
|
||||
)
|
||||
cdef int SDL_ConvertAudio(SDL_AudioCVT *cvt)
|
||||
|
||||
cdef extern from "SDL_mixer.h":
|
||||
cdef struct Mix_Chunk:
|
||||
|
|
Loading…
Reference in New Issue