import pygame
import numpy as np
from collections import deque
from pygame.locals import QUIT, KEYDOWN, K_RETURN, MOUSEBUTTONDOWN
import threading
pygame.init()
pygame.mixer.init()
WIDTH, HEIGHT = 800, 400
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("数字音符播放器")
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
GREEN = (0, 200, 80)
BLUE = (50, 150, 250)
font = pygame.font.Font(None, 36)
small_font = pygame.font.Font(None, 24)
NOTE_FREQS = {
'1.': 261.63 / 2,
'2.': 293.66 / 2,
'3.': 329.63 / 2,
'4.': 349.23 / 2,
'5.': 392.00 / 2,
'6.': 440.00 / 2,
'7.': 493.88 / 2,
'1': 261.63,
'2': 293.66,
'3': 329.63,
'4': 349.23,
'5': 392.00,
'6': 440.00,
'7': 493.88,
'1*': 261.63 * 2,
'2*': 293.66 * 2,
'3*': 329.63 * 2,
'4*': 349.23 * 2,
'5*': 392.00 * 2,
'6*': 440.00 * 2,
'7*': 493.88 * 2,
'-': 0,
}
INPUT_RECT = pygame.Rect(50, 50, 800, 200)
BUTTON_RECT = pygame.Rect(150, 130, 200, 60)
input_text = "6.3.6.3212--1-6.2116.5.---1-6.53322----1-6.2116.5.----5.-6.1--1-6.12---1-6.2116.5.---1--6.53322----1-6.2116.5.----5.-6.1--1-6.12--1-6.2116.1---1--6.53322--1-6.2116.5.----5.-6.1--1-6.12--1-6.2116.5.---1--6.53322----1-6.2116.5.----5.-6.1--1-6.12--1-6.2116.1---3353124321-5.6.1235-561*7654543453--217126.7.15.---1--6.53322--1-6.2116.5.---5.-6.1--1-6.12---1-6.2116.5.---1-6.53322----1-6.2116.5.----5.-6.1--1-6.12--1-6.2116.1------"
play_queue = deque()
is_playing = False
input_box_active = False
def play_note(freq, duration=0.3):
if freq == 0:
pygame.time.wait(int(duration * 1000))
return
sample_rate = 44100
t = np.linspace(0, duration, int(sample_rate * duration), False)
wave = np.sin(2 * np.pi * freq * t) * 32767
stereo_wave = np.column_stack((wave, wave)).astype(np.int16)
sound = pygame.sndarray.make_sound(stereo_wave)
sound.play()
pygame.time.wait(int(duration * 1000))
def parse_input(text):
queue = deque()
i = 0
while i < len(text):
char = text[i]
if char in '1234567' and i + 1 < len(text) and text[i + 1] in ('.', '*'):
key = text[i:i + 2]
i += 2
else:
key = char
i += 1
queue.append(NOTE_FREQS.get(key, 0))
return queue
def play_thread():
global is_playing
is_playing = True
try:
while play_queue and is_playing:
freq = play_queue.popleft()
play_note(freq)
except Exception as e:
print(f"播放错误: {e}")
finally:
is_playing = False
def draw_input_box():
pygame.draw.rect(screen, GRAY if input_box_active else WHITE, INPUT_RECT, 2)
text_surface = font.render(input_text, True, BLACK)
screen.blit(text_surface, (INPUT_RECT.x + 10, INPUT_RECT.y + 10))
def draw_play_button():
color = BLUE if is_playing else GREEN
pygame.draw.rect(screen, color, BUTTON_RECT, 0)
pygame.draw.rect(screen, BLACK, BUTTON_RECT, 2)
text_surface = font.render("播放音符", True, BLACK)
screen.blit(text_surface, (BUTTON_RECT.x + 30, BUTTON_RECT.y + 10))
running = True
while running:
screen.fill(WHITE)
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == MOUSEBUTTONDOWN:
if BUTTON_RECT.collidepoint(event.pos):
play_queue = parse_input(input_text)
if play_queue:
threading.Thread(target=play_thread, daemon=True).start()
elif INPUT_RECT.collidepoint(event.pos):
input_box_active = True
else:
input_box_active = False
if event.type == KEYDOWN:
if input_box_active:
if event.key == K_RETURN:
play_queue = parse_input(input_text)
if play_queue:
threading.Thread(target=play_thread, daemon=True).start()
elif event.key == pygame.K_BACKSPACE:
input_text = input_text[:-1]
else:
char = event.unicode
if char in '1234567.-*' and len(input_text) < 50:
input_text += char
draw_input_box()
draw_play_button()
text_surface = small_font.render("输入格式:低音1. 中音1 高音1*,-=休止符,最多50位", True, BLACK)
screen.blit(text_surface, (150, 710))
pygame.display.flip()
pygame.time.Clock().tick(30)
pygame.quit()