Skip to main content
 首页 » 编程设计

python之如何在按下键时播放给定赫兹的音调并在释放时停止播放

2025年05月04日23leader

我找到了这段播放声音的代码:

from array import array  # need a newline between normal text and code, edited to make it so 
from time import sleep 
 
import pygame 
from pygame.mixer import Sound, get_init, pre_init 
 
class Note(Sound): 
 
    def __init__(self, frequency, volume=.1): 
        self.frequency = frequency 
        Sound.__init__(self, self.build_samples()) 
        self.set_volume(volume) 
 
    def build_samples(self): 
        period = int(round(get_init()[0] / self.frequency)) 
        samples = array("h", [0] * period) 
        amplitude = 2 ** (abs(get_init()[1]) - 1) - 1 
        for time in range(period): 
            if time < period / 2: 
                samples[time] = amplitude 
            else: 
                samples[time] = -amplitude 
       return samples 
 
if __name__ == "__main__": 
    pre_init(44100, -16, 1, 1024) 
    pygame.init() 
    Note(440).play(-1) 
sleep(5) 

为了方便,我使用这个方法:

def playSound(hz,ms): 
    pre_init(44100, -16, 1, 1024) 
    Note(hz).play(ms) 

将 -1 设置为 ms 无限播放声音。

我的问题是我无法控制声音何时停止。我试过在它上面播放另一个听不见的频率,但两者可以同时播放。关于如何在释放按键时结束声音播放的任何想法?

请您参考如下方法:

您必须构建 "mainloop" 以定期检查 key 。

但是系统只会将按键事件发送到事件窗口,因此您必须创建窗口。

import pygame 
import array 
 
class Note(pygame.mixer.Sound): 
 
    def __init__(self, frequency, volume=.1): 
        self.frequency = frequency 
        pygame.mixer.Sound.__init__(self, self.build_samples()) 
        self.set_volume(volume) 
 
    def build_samples(self): 
        period = int(round(pygame.mixer.get_init()[0] / self.frequency)) 
        samples = array.array("h", [0] * period) 
        amplitude = 2 ** (abs(pygame.mixer.get_init()[1]) - 1) - 1 
        for time in range(period): 
            if time < period / 2: 
                samples[time] = amplitude 
            else: 
                samples[time] = -amplitude 
        return samples 
 
if __name__ == "__main__": 
 
    # --- init --- 
 
    pygame.mixer.pre_init(44100, -16, 1, 1024) 
    pygame.init() 
 
    screen = pygame.display.set_mode((100, 100)) 
 
    # --- objects --- 
 
    tones = { 
        pygame.K_q: Note(440), 
        pygame.K_w: Note(340), 
        pygame.K_e: Note(540) 
    } 
 
    # --- mainloop --- 
 
    running = True 
 
    while running: 
        for event in pygame.event.get(): 
 
            # closing window 
            if event.type == pygame.QUIT: 
                running = False 
 
            # pressing key 
            elif event.type == pygame.KEYDOWN: 
                if event.key in tones: 
                    print('press:', event.key) 
                    tones[event.key].play(-1) 
 
            # releasing key 
            elif event.type == pygame.KEYUP: 
                if event.key in tones: 
                    print('release:', event.key) 
                    tones[event.key].stop() 
 
    # --- end --- 
 
    pygame.quit() 

编辑:显示按下的键的示例。

顺便说一句:有些键盘不会发送超过 4 个按下的键。

import pygame 
import array 
 
class Note(pygame.mixer.Sound): 
 
    def __init__(self, frequency, volume=.1): 
        self.frequency = frequency 
        pygame.mixer.Sound.__init__(self, self.build_samples()) 
        self.set_volume(volume) 
 
    def build_samples(self): 
        period = int(round(pygame.mixer.get_init()[0] / self.frequency)) 
        samples = array.array("h", [0] * period) 
        amplitude = 2 ** (abs(pygame.mixer.get_init()[1]) - 1) - 1 
        for time in range(period): 
            if time < period / 2: 
                samples[time] = amplitude 
            else: 
                samples[time] = -amplitude 
        return samples 
 
if __name__ == "__main__": 
 
    # --- init --- 
 
    pygame.mixer.pre_init(44100, -16, 1, 1024) 
    pygame.init() 
 
    screen = pygame.display.set_mode((110, 100)) 
 
    # --- objects --- 
 
    keys = [ 
        (pygame.K_q, 140), 
        (pygame.K_w, 240), 
        (pygame.K_e, 340), 
        (pygame.K_r, 440), 
        (pygame.K_t, 540), 
    ] 
 
    tones = {} 
    pressed = {} 
 
    for key, val in keys: 
        tones[key] = Note(val) 
        pressed[key] = False 
 
    # --- mainloop --- 
 
    running = True 
 
    while running: 
 
        # - events - 
 
        for event in pygame.event.get(): 
 
            # closing window 
            if event.type == pygame.QUIT: 
                running = False 
 
            # pressing key 
            elif event.type == pygame.KEYDOWN: 
                if event.key in tones: 
                    #print('press:', event.key) 
                    tones[event.key].play(-1) 
                    pressed[event.key] = True 
 
            # releasing key 
            elif event.type == pygame.KEYUP: 
                if event.key in tones: 
                    #print('release:', event.key) 
                    tones[event.key].stop() 
                    pressed[event.key] = False 
 
        # - draws - 
 
        # clear screen 
        screen.fill((0,0,0)) 
        # draw pressed keys 
        for idx, key in enumerate(keys): 
            if key[0] in pressed and pressed[key[0]]: 
                x = 21*idx 
                pygame.draw.rect(screen, (255,255,255), (x, 0, 20, 50)) 
        # send buffer on monitor 
        pygame.display.flip() 
 
    # --- end --- 
 
    pygame.quit()