just_a_kid_coder_123
just_a_kid_coder_123

Reputation: 93

distorted audio when playing a sound with pygame

I am trying to make a streetfighter style like game with circles and rectangles(too lazy to draw the art). However, when I play try to play a punching sound, it sounds like it was distorted. Same goes for the KO sound effect I play when a player dies.

here are the sounds

https://audio.nathanli1818.repl.co/

heres the code:

import pygame

pygame.init()

colors = {
    "white": (255, 255, 255),
    "black": (0, 0, 0),
    "blue": (71, 52, 237),
    "green": (53, 189, 71),
    "brown": (79, 21, 21),
    "red": (184, 39, 39),
    "purple": (145, 27, 209)

}

class Fighter:
    def __init__(self, win, x, y, win_width, win_height, color, healthx, healthy):
        self.win = win
        self.gravity = 0
        self.win_width = win_width
        self.win_height = win_height
        self.color = color
        self.health = 100
        self.healthx = healthx
        self.healthy = healthy
        self.dir_ = "right"
        self.x = x
        self.y = y

        self.base = pygame.Rect(0, 0, 70, 100)
        self.base.centerx = self.x
        self.base.centery = self.y

        self.attack_ = pygame.Rect(0, 0, 20, 20)
        self.healthbar = pygame.Rect(0, 0, self.health * 3, 20)
        self.healthbar.center = (self.healthx, self.healthy)

        self.background = pygame.Rect(0, 0, 300, 20)
        self.background.center = self.healthbar.center

        self.punch = pygame.mixer.Sound("sounds/attack.wav")
        self.punch.set_volume(0.3)

        self.KO = pygame.mixer.Sound("sounds/KO.mp3")
        self.KO.set_volume(0.3)

    def render(self):
        pygame.draw.ellipse(self.win, self.color, self.base)
        self.x = self.base.centerx
        self.y = self.base.centery

    def move(self, x, y):
        self.base.centerx = x
        self.base.centery = y

    def fall(self):
        self.base.y += self.gravity
        self.gravity += 1

        if self.base.bottom >= self.win_height - 50:
            self.gravity = 0
            self.base.bottom = self.win_height - 50

    def draw_healthbar(self):
        if self.health <= 0:
            flag = 0
            if not flag:
                self.KO.play()
                flag += 1
                self.health = 100
        
        self.healthbar.width = self.health * 3
        pygame.draw.rect(self.win, colors["red"], self.background)
        pygame.draw.rect(self.win, colors["green"], self.healthbar)

    def attack(self, type_):
        if type_ == "punch":
            flag = 0
            if self.dir_ == "right":
                if not flag:
                    self.punch.play()
                    flag += 1

                self.attack_.center = (self.base.topright[0] + 35, self.base.centery - 10)
                pygame.draw.rect(self.win, self.color, self.attack_)

            elif self.dir_ == "left":
                if not flag:
                    self.punch.play()
                    flag += 1

                self.attack_.center = (self.base.topleft[0] - 35, self.base.centery - 10)
                pygame.draw.rect(self.win, self.color, self.attack_)


class Background:
    def __init__(self, win, win_width, win_height):
        self.win = win
        self.win_width = win_width
        self.win_height = win_height

        self.sky = (self.win_width, self.win_height - 100)
        self.ground = (self.win_width, 100)
        self.platform = (220, 30)

        self.platformTop = (220, 5)
        self.platformBottom = (220, 5)

        self.sky_base = pygame.Rect(0, 0, *self.sky)
        self.ground_base = pygame.Rect(0, 0, *self.ground)
        self.ground_base.bottom = self.win_height

        self.platform_base = pygame.Rect(0, 0, *self.platform)
        self.platform_base.center = (self.win_width / 2, self.win_height / 2 + 15)

        self.platform_top = pygame.Rect(0, 0, *self.platformTop)
        self.platform_top.center = (self.platform_base.centerx, self.platform_base.top + self.platformBottom[1])

        self.platform_bottom = pygame.Rect(0, 0, *self.platformBottom)
        self.platform_bottom.center = (self.platform_base.centerx, self.platform_base.bottom - self.platformBottom[1])

    def load(self):
        pygame.draw.rect(self.win, colors["blue"], self.sky_base)
        pygame.draw.rect(self.win, colors["green"], self.ground_base)
        pygame.draw.rect(self.win, colors["brown"], self.platform_base)


class Game:
    def __init__(self):
        self.width = 900
        self.height = 500
        self.running = True
        self.attackFlag1 = 0
        self.attackFlag2 = 0
        self.fps = 60

        self.win = pygame.display.set_mode((self.width, self.height))
        self.clock = pygame.time.Clock()
        self.fighter = Fighter(self.win, 200, 450, self.width, self.height, colors["red"], 200, 50)
        self.fighter2 = Fighter(self.win, 700, 450, self.width, self.height, colors["purple"], 700, 50)
        self.background = Background(self.win, self.width, self.height)

        pygame.display.set_caption("street fighter")

    def fighter_1(self, keys):
        if keys[pygame.K_a]:
            if self.fighter.base.left <= 0:
                self.fighter.x += 10

            self.fighter.dir_ = "left"

            x = self.fighter.x
            self.fighter.move(x - 10, self.fighter.y)

        if keys[pygame.K_d]:
            if self.fighter.base.right >= self.width:
                self.fighter.x -= 10

            self.fighter.dir_ = "right"

            x = self.fighter.x
            self.fighter.move(x + 10, self.fighter.y)

        if keys[pygame.K_w] and self.fighter.base.bottom == 450:
            self.fighter.gravity = -20

        if keys[pygame.K_s] and self.fighter.base.bottom != 450:
                self.fighter.gravity += 10

        if self.background.platform_top.colliderect(self.fighter.base):
            if keys[pygame.K_w]:
                self.fighter.gravity = -20

            else:
                self.fighter.gravity = 0
                self.fighter.base.bottom = self.background.platform_top.bottom

        if self.background.platform_bottom.colliderect(self.fighter.base):
            self.fighter.gravity += 5

        if keys[pygame.K_e]:
            self.fighter.attack("punch")

        if self.fighter.base.colliderect(self.fighter2.attack_):
            if self.attackFlag2 == 0:
                self.fighter.health -= 5
                self.attackFlag2 += 1


    def fighter_2(self, keys):
        if keys[pygame.K_LEFT]:
            if self.fighter2.base.left <= 0:
                self.fighter2.x += 10

            self.fighter2.dir_ = "left"

            x = self.fighter2.x
            self.fighter2.move(x - 10, self.fighter2.y)

        if keys[pygame.K_RIGHT]:
            if self.fighter2.base.right >= self.width:
                self.fighter2.x -= 10

            self.fighter2.dir_ = "right"

            x = self.fighter2.x
            self.fighter2.move(x + 10, self.fighter2.y)

        if keys[pygame.K_UP] and self.fighter2.base.bottom == 450:
            self.fighter2.gravity = -20

        if keys[pygame.K_DOWN] and self.fighter2.base.bottom != 450:
                self.fighter2.gravity += 10

        if self.background.platform_top.colliderect(self.fighter2.base):
            if keys[pygame.K_UP]:
                self.fighter2.gravity = -20

            else:
                self.fighter2.gravity = 0
                self.fighter2.base.bottom = self.background.platform_top.bottom

        if self.background.platform_bottom.colliderect(self.fighter2.base):
            self.fighter2.gravity += 5

        if self.fighter2.base.colliderect(self.fighter.attack_):
            if self.attackFlag1 == 0:
                self.fighter2.health -= 50
                self.attackFlag1 += 1

        if keys[pygame.K_RSHIFT]:
            self.fighter2.attack("punch")


    def run(self):
        while self.running:
            self.clock.tick(self.fps)
            self.background.load()

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False

                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_e:
                        self.fighter.attack_.y = 1000
                        self.attackFlag1 = 0

                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_RSHIFT:
                        self.fighter2.attack_.y = 1000
                        self.attackFlag2 = 0

            keys = pygame.key.get_pressed()

            self.fighter_1(keys)
            self.fighter.fall()
            self.fighter.draw_healthbar()
            self.fighter.render()

            self.fighter_2(keys)
            self.fighter2.fall()
            self.fighter2.draw_healthbar()
            self.fighter2.render()

            pygame.display.update()

        pygame.quit()

if __name__ == '__main__':
    Game().run()

on python 3.8.8

Upvotes: 0

Views: 1263

Answers (1)

user19642323
user19642323

Reputation: 388

Your problem is coming from the fact that the sound is being played multiple times on top of itself. This happens because every frame, you check if pygame.K_e is held, and then play the sound if it is. However, when you press a key, it's likely that you'll be holding the key for more than a single frame, so this leads to the sound being played multiple times on top of itself in quick succession.

One way to rectify the problem would be to play the sound using a pygame.mixer.Channel, which only allows one sound to be playing at any time:

# Get a reference to a pygame channel
self.channel = pygame.mixer.Channel(0)
# Load punch sound effect
self.punch = pygame.mixer.Sound("sounds/attack.wav")

# ...

# Play using channel
self.channel.play(self.punch)

That said, this probably isn't actually what you want - rather, you should probably instead be checking for a 'key release' event instead of just checking whether the E key is held in your fighter_* functions. This will put sufficient time between calls to attack to prevent the distortion you're hearing from occurring.

Upvotes: 1

Related Questions