Fade Between Two Music Tracks в процессе выполнения в Pygame

Я хочу, чтобы два музыкальных трека, похожих по своей природе, переходили друг в друга в разное время. Когда происходит такое затухание, одна музыкальная дорожка должна переходить от полной громкости к приглушенной за короткий промежуток времени, и одновременно другая дорожка должна уменьшаться от 0 до 100 и продолжать воспроизводиться с того же временного индекса. Они должны иметь возможность делать это динамически в любое время — когда происходит определенное действие, происходит затухание и новый трек начинает воспроизводиться с той же позиции, что и другой трек. один остановился на.

Это может быть правдоподобно либо с помощью управления громкостью, либо путем запуска и остановки музыки (однако кажется, что существует только опция «затухание», а опция «затухание» отсутствует). Как я могу это сделать? Каков наилучший метод, если таковой имеется? Если невозможно использовать Pygame, приемлемы альтернативы Pygame.


person Southpaw Hare    schedule 11.09.2013    source источник


Ответы (4)


Попробуйте это, это довольно прямолинейно ..

import pygame

pygame.mixer.init()
pygame.init()

# Maybe you can subclass the pygame.mixer.Sound and
# add the methods below to it..
class Fader(object):
    instances = []
    def __init__(self, fname):
        super(Fader, self).__init__()
        assert isinstance(fname, basestring)
        self.sound = pygame.mixer.Sound(fname)
        self.increment = 0.01 # tweak for speed of effect!!
        self.next_vol = 1 # fade to 100 on start
        Fader.instances.append(self)

    def fade_to(self, new_vol):
        # you could change the increment here based on something..
        self.next_vol = new_vol

    @classmethod
    def update(cls):
        for inst in cls.instances:
            curr_volume = inst.sound.get_volume()
            # print inst, curr_volume, inst.next_vol
            if inst.next_vol > curr_volume:
                inst.sound.set_volume(curr_volume + inst.increment)
            elif inst.next_vol < curr_volume:
                inst.sound.set_volume(curr_volume - inst.increment)

sound1 = Fader("1.wav")
sound2 = Fader("2.wav")
sound1.sound.play()
sound2.sound.play()
sound2.sound.set_volume(0)

# fading..
sound1.fade_to(0)
sound2.fade_to(1)


while True:
    Fader.update() # a call that will update all the faders..
person pradyunsg    schedule 18.09.2013
comment
Кажется, это работает, с дополнительной проверкой, чтобы убедиться, что приращение не превышает число, к которому оно стремится. Однако возникает новый вопрос: в чем разница между использованием звука и музыки в Pygame? Кажется, что только звуки имеют эту необходимую функциональность, но звуки также могут быть вырезаны, если каналы станут максимальными? Есть ли способ гарантировать, что такие высокоприоритетные каналы, как эти два трека, не будут обрезаны? - person Southpaw Hare; 22.09.2013

Это не совсем ответ на вопрос, но для будущих гуглеров я написал скрипт для постепенного появления моей музыки с громкости 0 по утрам, и это то, что я использовал:

max_volume = 40 
current_volume = 0

# set the volume to the given percent using amixer
def set_volume_to(percent):
    subprocess.call(["amixer", "-D", "pulse", "sset", "Master", 
                     str(percent) + "%", "stdout=devnull"])

# play the song and fade in the song to the max_volume 
def play_song(song_file):
    global current_volume
    print("Song starting: " + song_file)
    pygame.mixer.music.load(song_file)
    pygame.mixer.music.play()

    # gradually increase volume to max
    while pygame.mixer.music.get_busy():
        if current_volume < max_volume: 
            set_volume_to(current_volume)
            current_volume += 1

        pygame.time.Clock().tick(1)

 play_song("foo.mp3")
person Charles Clayton    schedule 03.02.2017

Псевдокод:

track1 = ...
track2 = ...

track1.play_forever()
track1.volume = 100
track2.play_forever()
track2.volume = 0

playing = track1
tracks = [track1, track2]


def volume_switcher():
    while True:
        playing.volume = min(playing.volume + 1, 100)

        for track in tracks:
            if track != playing:
                track.volume = max(track.volume - 1, 100)

        time.sleep(0.1)

Thread(target=volume_switcher).start()
person Eric    schedule 16.09.2013

Таким образом, похоже, что вы хотите сделать в pygame два объекта «Звук» и создать линейную интерполяцию громкости между ними двумя.

Я бы создал два вектора, каждый из [0,100], и связал бы их обратно с некоторой константой. Таким образом, когда звук A равен 100, звук b равен 0. Затем, когда происходит действие, вы изменяете константу.

t=0
А: [0 ... 100]
Б: [0 ... 100]

t=1
ДЕЙСТВИЕ

t=1.1
A:[0 .. 50 .. 100]
B: [0 .. 50 .. 100]

t=2
A:[0 ... 100]
B: [0 ... 100]

Теперь немного кода. Я не знаком с pygame, но это должно поставить вас на правильный путь.

class Song(object):
    def __init__(self, songfilename):
        self.song = pygame.mixer.Sound(songfilename)

    def setVolume(self, somenumber):
        #number validation
        #possibly do some volume curve here if you wanted
        self.song.set_volume(somenumber)

class SongFader(object):
    def __init__(self, song1, song2):
        self.song1 = song1
        self.song2 = song2
        self.__xAxisMax = 100
        self.__xAxisMin = 0

    def fade(self, xaxis):
        assert(self.__xAxisMin <= xaxis <= self.__xAxisMax) 
          #could be any numbers you want. 
          #i chose 0-100 for convenience
        self.song1.setVolume(xaxis)
        self.song2.setVolume(self.__xAxisMax-xaxis)

song1 = Song('Song1.wav')
song2 = Song('Song2.wav')
fader = SongFader(song1, song2)

#Inside some event loop when you action is triggered
fader.fade(100) #Only song2 is playing
fader.fade(50)  #Songs are evenly split
fader.fade(0)   #Only left song is playing

редактировать

Линейная интерполяция, вероятно, является более важной концепцией здесь, поэтому я изменил класс фейдера, вдохновленный идеей резьбы Эрика.

class SongFader(object):
    def __init__(self, song1, song2):
        self.song1 = song1
        self.song2 = song2
        self.lefttoright = False
        self.starttime = 0
        self.endtime = 0


    def fade(self, starttime, fadeleft):
        self.lefttoright = fadeleft == True #Being verbose here
        self.starttime = starttime #assuming time is in millis
        self.endtime = starttime + 1000
        Thread(target = self.fadeHelper).start()

    #this is where you define how the two songs are faded
    def fadeHelper(self):
        #if using thread, make sure you mutex the 'self.' variables
        starttime = self.starttime
        endtime = self.endtime
        lefttoright = self.lefttoright
        while starttime < endtime:
            fadevalue = (starttime - endtime) / 1000 #a val between [0,1]
            if lefttoright:
                self.song1.setVolume(fadevalue)
                self.song2.setVolume(1-fadevalue)
            else:
                self.song1.setVolume(1-fadevalue)
                self.song2.setVolume(fadefalue)
            starttime = getGameTimeFromSomewhere()
person gregb212    schedule 16.09.2013