LoginSignup
0
0

More than 3 years have passed since last update.

NimでSDL2 ⑤ 音を出す

Posted at

はじめに

NimでSDL2 ① 準備とウィンドウ表示
NimでSDL2 ② 画像を表示
NimでSDL2 ③ 入力処理
NimでSDL2 ④ FPS(frames per second)の調整

音を出してみます。

概要

oggファイルとwavファイルを鳴らしていく。
SDLではBGMなどに使うMusicと効果音などで使うChunkに分かれている。
Musicはoggファイルなどのフォーマットが使用でき、プログラム中一つしか鳴らせない。
Chunkは複数同時に鳴らせる。

基本的にオーディオを初期化してMusicやChunkを読み込んで再生する感じ。

詳細はこちらを参照されたし。

コード

追記箇所はコメント参照

# sdl2/mixerをimport
import sdl2, sdl2/image, os, sugar, sets, sdl2/mixer

template sdlFailIf(cond: typed, desc: string) =
  if cond: raise Exception.newException(
    desc & ": " & $getError())

type FPS* = ref object
  frameTime, targetFramePeriod: uint32

proc newFPS*(fps: int): FPS =
  FPS(targetFramePeriod: uint32(1000 / fps))

proc adjust*(self: FPS) =
  let now = getTicks()
  if self.frameTime > now:
    delay(self.frameTime - now)
  self.frameTime += self.targetFramePeriod

type SDL2Context = ref object
  window: WindowPtr
  renderer: RendererPtr
  cleanups: seq[proc ()]
  pressed: HashSet[cint] 
  press: HashSet[cint] # 押しているキーじゃなく押されているキーを保持
  fps: FPS

proc cleanup(self: SDL2Context) =
  while self.cleanups.len > 0:
    (self.cleanups.pop)()

proc init(self: SDL2Context, title: string, w, h: cint): SDL2Context {.discardable.} =
  result = self
  sdlFailIf(not sdl2.init(INIT_VIDEO), "SDL2 init")
  self.cleanups.add(() => sdl2.quit())
  self.window = createWindow(title = title,
                            x = SDL_WINDOWPOS_CENTERED,
                            y = SDL_WINDOWPOS_CENTERED,
                            w = w, h = h,
                            flags = SDL_WINDOW_SHOWN)
  sdlFailIf(self.window.isNil, "createWindow")
  self.cleanups.add(() => self.window.destroy)
  sdlFailIf(image.init(IMG_INIT_PNG) != IMG_INIT_PNG, "image init")
  self.cleanups.add(() => image.quit())
  self.renderer = self.window.createRenderer(
    index = -1,
    flags = Renderer_Accelerated or Renderer_PresentVsync)
  sdlFailIf(self.renderer.isNil, "createRenderer")
  self.cleanups.add(() => self.renderer.destroy)
  self.renderer.setDrawColor(r=0, g=128, b=128)
  self.pressed = initHashSet[cint]()
  self.fps = newFPS(60)
  # オーディオを初期化
  sdlFailIf(mixer.openAudio(
    MIX_DEFAULT_FREQUENCY,
    MIX_DEFAULT_FORMAT,
    MIX_DEFAULT_CHANNELS,
    4096) != 0, "openAudio")
  # オーディオのクリーンアップ処理を登録
  self.cleanups.add(() => closeAudio())

proc loadTexture(self: SDL2Context, path: string): TexturePtr =
  result = self.renderer.loadTexture(path)
  sdlFailIf(result.isNil, "loadTexture")

proc keyStateUpdate*(self: SDL2Context, event: KeyboardEventPtr) =
  case event.kind
  of KeyDown:
    self.pressed.incl(event.keysym.sym)
    self.press.incl(event.keysym.sym) # 押されたキーにも追加
  of KeyUp: self.pressed.excl(event.keysym.sym)
  else: discard

proc pollEvent(self: SDL2Context): bool =
  result = true
  var event = defaultEvent
  self.press.clear # 押されたキーは毎回初期化
  while pollEvent(event):
    case event.kind:
    of QuitEvent: return false
    of KeyDown, KeyUp: self.keyStateUpdate(event.key)
    else: discard

proc mainLoop(self: SDL2Context) =
  let
    w: cint = 256
    h: cint = 256
    speed: cint = 3
    neko = self.loadTexture("genbaneko.png")
  var
    windowWidth: cint
    windowHeight: cint
  # BGM(oggファイル)読み込み
  let music = mixer.loadMUS("bgm.ogg")
  sdlFailIf(music.isNil, "loadMUS")
  # サウンド(wavファイル)読み込み
  let sound = mixer.loadWav("sound.wav")
  sdlFailIf(sound.isNil, "loadWav")
  self.window.getSize(windowWidth, windowHeight)
  var dst = rect(windowWidth div 2 - w div 2, windowHeight div 2 - h div 2, w, h)
  while self.pollEvent:
    if K_RIGHT in self.pressed: dst.x += speed
    if K_LEFT  in self.pressed: dst.x -= speed
    if K_DOWN  in self.pressed: dst.y += speed
    if K_UP    in self.pressed: dst.y -= speed
    # Mキーで音楽再生、停止
    if K_m in self.press:
      # 音楽再生中かチェック
      if playingMusic() == 0:
        # 音楽未再生なら再生
        sdlFailif(playMusic(music, 0) == -1, "playMusic")
      else:
        # 音楽再生中なら停止
        sdlFailif(haltMusic() == -1, "haltMusic")
    # Sキーでサウンド再生
    if K_s in self.press:
      sdlFailif(playChannel(-1, sound, 0) == -1, "playChannel")
    self.renderer.clear
    self.renderer.copyEx(neko, nil, dst.addr, 0.0, nil, SDL_FLIP_NONE)
    self.renderer.present
    self.fps.adjust

proc main() =
  let sdl2 = SDL2Context()
  defer: sdl2.cleanup
  sdl2.init("SDL2 Keyboard sample", 640, 480).mainLoop

main()

次は?

フォントを使ってテキストを表示してみたいですね。

以上〜。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0