Channel.queue と threading を使って途切れなく発音
左右で音程、上下で(左だけ)音量変化。
先の
はこれのためのテスト。なので波形はちゃんとつながっているみたいですから、未だにぶちぶちいうのは別の要因か?
(Python 2.5 + Pygame 1.8)
from __future__ import division import wave import math import array import threading import Queue import pygame from pygame.locals import * FULLSCREEN = False # True にするとフルスクリーン表示 FPS = 60 # 秒間描画枚数 WIDTH, HEIGHT = 640, 240 # 表示する画面のサイズ LINEWIDTH = 20 # 線の太さ COLOR = 0, 255, 200 # 色 BG_COLOR = 0, 0, 50 # 背景色 BASE_FREQUENCY = 220 # 左端での周波数(Hz) OCTAVE_WIDTH = 240 # 1オクターブの幅 # 作業用 wave ファイルのスペック(実際の発音は pygame の設定に依存) PYGAME_FREQUENCY = 22050 WAVE_LENGTH = 1/20 DUMMY_WAVE = '__dummy__.wav' def dummy_wave(length=WAVE_LENGTH): f = wave.open(DUMMY_WAVE, 'wb') f.setparams((1, 2, PYGAME_FREQUENCY, 0, 'NONE', 'not compressed')) f.writeframes( array.array('h', [0] * int(PYGAME_FREQUENCY * length)).tostring()) f.close() def sins(frequency, start=0, volume=1.0, length=WAVE_LENGTH): left_vol = min(32767, 32767 * volume * BASE_FREQUENCY / frequency) right_vol = min(32767, 32767 * .5 * BASE_FREQUENCY / frequency) datalen =int(PYGAME_FREQUENCY * length) data = array.array('h', [0] * 2 * datalen) a = math.pi * 2 * frequency / PYGAME_FREQUENCY start_a = start for i in range(datalen): value = math.sin(a * i + start_a) data[i * 2] = int(value * left_vol) data[i * 2 + 1] = int(value * right_vol) return data, (a * datalen + start_a) % (math.pi * 2) class MakeWave(threading.Thread): def __init__(self, frequency, end=0, volume=1.0): threading.Thread.__init__(self) self.setDaemon(True) self.frequency = frequency self.end = end self.volume = volume self.sound = None self.start() def run(self): self.wavedata, self.end = sins(self.frequency, self.end, self.volume) sound = pygame.mixer.Sound(DUMMY_WAVE) sound.get_buffer().write(self.wavedata, 0) self.sound = sound def main(): dummy_wave() screen = pygame.display.set_mode( (WIDTH, HEIGHT), (pygame.FULLSCREEN | pygame.HWSURFACE | pygame.DOUBLEBUF) if FULLSCREEN else 0) timer = pygame.time.Clock() channel = pygame.mixer.find_channel() log_base = math.log(BASE_FREQUENCY, 2) sound_on = False wavedata = None makewave = None startwave = 0 skipframe = 0 while True: screen.fill(BG_COLOR) x, y = pygame.mouse.get_pos() button1, button2, button3 = pygame.mouse.get_pressed() focuse = pygame.mouse.get_focused() a = (x / OCTAVE_WIDTH) frequency = 2 ** (log_base + a) volume = y / (HEIGHT - 1) if focuse: if button1: pygame.draw.line( screen, COLOR, (x, 0), (x, HEIGHT - 1), LINEWIDTH) pygame.draw.circle( screen, (255, 255, 0), (x, y), y / 2, min(3, y / 2)) if not sound_on: wavedata = None sound_on = True else: pygame.draw.line( screen, (255, 255, 0), (x, 0), (x, HEIGHT - 1), 3) sound_on = False else: sound_on = False queue = channel.get_queue() if queue is None and sound_on: if makewave is None: makewave = MakeWave(frequency, startwave, volume) elif makewave.sound is not None: wavedata = makewave.wavedata startwave = makewave.end channel.queue(makewave.sound) makewave = None if sound_on and wavedata is not None: h = HEIGHT // 2 pygame.draw.lines( # 右ch screen, (0, 50, 100), False, [(x, wavedata[x * 2 + 1] * h // 32767 + h) for x in range(min(WIDTH, len(wavedata) // 2))], 3) pygame.draw.lines( # 左ch screen, (0, 100, 0), False, [(x, wavedata[x * 2] * h // 32767 + h) for x in range(min(WIDTH, len(wavedata) // 2))], 3) for xb in range(0, WIDTH, OCTAVE_WIDTH): for s in range(12): x = xb + s * OCTAVE_WIDTH / 12 pygame.draw.line( screen, (253 - 23 * s, 0, 23 * s,), (x, 0), (x, HEIGHT - 1), 1) timer.tick(FPS) pygame.display.flip() cap = '%5.2f fps, %7.2fHz, Volume%3d%%' % ( timer.get_fps(), frequency, volume * 100) pygame.display.set_caption(cap) for event in pygame.event.get(): if event.type == QUIT: return elif event.type == KEYDOWN and event.key == K_ESCAPE: return if __name__=='__main__': pygame.mixer.pre_init(PYGAME_FREQUENCY, -16, 2, 2**10) pygame.init() PYGAME_FREQUENCY, format, PYGAME_CHANNELS = pygame.mixer.get_init() print PYGAME_FREQUENCY, format, PYGAME_CHANNELS try: main() finally: pygame.quit() # 好きに流用してください。
Channel.queue を使って短い Sound をつなげる場合 mixer の buffersize を調整しないと音が途切れるよう。おそらく発音の長さよりもバッファが大きいとそこが無音になってしまう感じ。
これ frequency の値によっても必要なサイズが変わってしまうし、その frequency (が設定した通りになるか)はシステムに依存してしまうので……ちょっとやっかいかも。