ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [WinAPI] .wav 파일으로 BGM 추가하기 + 음량 조절하기
    프로그래밍 공부/Win API 2023. 1. 15. 14:34
    728x90

    게임에서 소리는 유무에 따라 퀄리티가 굉장히 달라지는 결과를 가져오게 된다.

    만약 이번에 준비하는 내용이 단순히 소리를 온오프할 뿐이었다면 특별한 작업을 추가해주지 않았어도 됐다.

    그러나 이번엔 프로그래스 바를 이용해 소리를 조작해야 했다.

     

    알아본 결과 일반적으로 FMOD를 많이 사용한다.

    그러나 시간에 제약이 있었기 때문에 완성을 우선시할수밖에 없었다. 따라서 좀 더 사용은 간편한 윈도우 내장 함수를 사용하도록 했다.

    윈도우 내장 함수의 경우 사운드 디바이스가 한정적이기 때문에 여러 소리를 중첩하는 데에 한계가 있다는 특징이 있다.

     

    CSound.h

    #pragma once
    #include "CSoundManager.h"
    
    class CSound
    {
    private:
    	LPDIRECTSOUNDBUFFER		m_pSoundBuffer;
    	DSBUFFERDESC			m_tBuffInfo;
    	int						m_iVolume;
    
    public:
    	int Load(const std::string _strPath);
    	// 일반 재생
    	void Play(bool _bLoop = false);
    	// BGM으로 재생
    	void PlayToBGM(bool _bLoop = false);
    
    	//정지
    	void Stop(bool _bReset = false);
    	
    	//볼륨 범위(0~100 %)
    	void SetVolume(float _fVolume);
    	// 음악파일 위치 조정 0~100
    	void SetPosition(float _fPosition);
    	int getVolume() { return m_iVolume; }
    
    private:
    	bool LoadWaveSound(const std::string _strPath);
    	int GetDecibel(float _fVolume);
    
    public:
    	CSound();
    	virtual ~CSound();
    
    };

     

    CSound.cpp

    #include "Stdafx.h"
    #include "CSound.h"
    
    CSound::CSound()
    	: m_pSoundBuffer(nullptr)
    	, m_tBuffInfo{}
    	, m_iVolume(0)
    {
    }
    
    CSound::~CSound()
    {
    	if (nullptr != m_pSoundBuffer)
    		m_pSoundBuffer->Release();
    }
    
    int CSound::Load(const std::string _strPath)
    {
    	LoadWaveSound(_strPath);
    	return S_OK;
    }
    
    bool CSound::LoadWaveSound(const std::string _strPath)
    {
    	HMMIO	hFile;
    
    	hFile = mmioOpen((LPSTR)_strPath.c_str(), NULL, MMIO_READ);//wave파일을 연다.
    
    	if (nullptr == hFile)
    	{
    		MessageBox(NULL, "사운드 리소스 경로에 파일 없음", "사운드 로딩 실패", MB_OK);
    		return false;
    	}
    
    	//Chunk 청크 구조체, 문자열로 색인을 인식해서 WaveFormat 및 버퍼선언정보를 읽어온다.
    	MMCKINFO	pParent;
    	memset(&pParent, 0, sizeof(pParent));
    	pParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
    	mmioDescend(hFile, &pParent, NULL, MMIO_FINDRIFF);
    
    	MMCKINFO	pChild;
    	memset(&pChild, 0, sizeof(pChild));
    	pChild.ckid = mmioFOURCC('f', 'm', 't', ' ');
    	mmioDescend(hFile, &pChild, &pParent, MMIO_FINDCHUNK);
    
    	WAVEFORMATEX	wft;
    	memset(&wft, 0, sizeof(wft));
    	mmioRead(hFile, (char*)&wft, sizeof(wft));
    
    	mmioAscend(hFile, &pChild, 0);
    	pChild.ckid = mmioFOURCC('d', 'a', 't', 'a');
    	mmioDescend(hFile, &pChild, &pParent, MMIO_FINDCHUNK);
    
    
    
    	memset(&m_tBuffInfo, 0, sizeof(DSBUFFERDESC));
    	m_tBuffInfo.dwBufferBytes = pChild.cksize;
    	m_tBuffInfo.dwSize = sizeof(DSBUFFERDESC);
    	m_tBuffInfo.dwFlags = DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRLVOLUME;
    	m_tBuffInfo.lpwfxFormat = &wft;
    
    	if (FAILED(CSOUNDMANAGER->GetSoundDevice()->CreateSoundBuffer(&m_tBuffInfo, &m_pSoundBuffer, NULL)))
    	{
    		MessageBox(NULL, "사운드버퍼생성실패", "", MB_OK);
    		return false;
    	}
    
    	void* pWrite1 = NULL;
    	void* pWrite2 = NULL;
    	DWORD dwlength1, dwlength2;
    
    	m_pSoundBuffer->Lock(0, pChild.cksize, &pWrite1, &dwlength1
    		, &pWrite2, &dwlength2, 0L);
    
    	if (pWrite1 != nullptr)
    		mmioRead(hFile, (char*)pWrite1, dwlength1);
    	if (pWrite2 != nullptr)
    		mmioRead(hFile, (char*)pWrite2, dwlength2);
    
    	m_pSoundBuffer->Unlock(pWrite1, dwlength1, pWrite2, dwlength2);
    
    	mmioClose(hFile, 0);
    
    	// 초기 음량 70%로 설정
    	SetVolume(70.f);
    
    	return true;
    }
    
    
    void CSound::Play(bool _bLoop)
    {
    	// Play 함수의 1번째 2번째 인자는 0 으로 이미 예약되어있다.
    	// 3번째 변수는 사운드를 반복재생 할 것인지 아닌지를 결정한다.
    	m_pSoundBuffer->SetCurrentPosition(0);
    
    	if (_bLoop)
    		m_pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
    	else
    		m_pSoundBuffer->Play(0, 0, 0);
    }
    
    void CSound::PlayToBGM(bool _bLoop)
    {
    	CSOUNDMANAGER->RegisterToBGM(this);
    
    	// Play 함수의 1번째 2번째 인자는 0 으로 이미 예약되어있다.
    	// 3번째 변수는 사운드를 반복재생 할 것인지 아닌지를 결정한다.
    	if (_bLoop)
    		m_pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
    	else
    		m_pSoundBuffer->Play(0, 0, 0);
    }
    
    void CSound::Stop(bool _bReset)
    {
    	m_pSoundBuffer->Stop();
    
    	if (_bReset)
    		m_pSoundBuffer->SetCurrentPosition(0);
    }
    
    
    void CSound::SetVolume(float _fVolume)
    {
    	m_iVolume = GetDecibel(_fVolume);
    	m_pSoundBuffer->SetVolume(m_iVolume);
    }
    
    void CSound::SetPosition(float _fPosition)
    {
    	Stop(true);
    
    	DWORD dwBytes = (DWORD)((_fPosition / 100.f) * (float)m_tBuffInfo.dwBufferBytes);
    	m_pSoundBuffer->SetCurrentPosition(dwBytes);
    
    	Play();
    }
    
    int CSound::GetDecibel(float _fVolume)
    {
    	if (_fVolume > 100.f)
    		_fVolume = 100.f;
    	else if (_fVolume <= 0.f)
    		_fVolume = 0.00001f;
    
    	// 1 ~ 100 사이값을 데시벨 단위로 변경
    	int iVolume = (LONG)(-2000.0 * log10(100.f / _fVolume));
    
    	if (iVolume < -10000)
    		iVolume = -10000;
    	return  iVolume;
    }

     

    CSoundManager.h

    #pragma once
    #include "SingletonBase.h"
    #include <mmSystem.h>
    #include <mciapi.h>
    #include <dsound.h>
    #include <dinput.h>
    
    #pragma comment(lib, "Winmm.lib")
    #pragma comment(lib, "dsound.lib")
    class CSound;
    
    class CSoundManager : public SingletonBase<CSoundManager>
    {
    private:
    	LPDIRECTSOUND8	m_pSound;	// 사운드카드 대표 객체
    	CSound* m_pBGM;				// 현재 지정된 BGM Sound
    
    public:
    	int init(HWND hwnd);
    	LPDIRECTSOUND8 GetSoundDevice() { return m_pSound; }
    	void RegisterToBGM(CSound* _pSound);
    
    	CSoundManager();
    	~CSoundManager();
    };

     

    CSoundManager.cpp

    #include "Stdafx.h"
    #include "CSoundManager.h"
    #include "CSound.h" 
    
    CSoundManager::CSoundManager()
    	: m_pSound(nullptr)
    	, m_pBGM(nullptr)
    {
    }
    
    CSoundManager::~CSoundManager()
    {
    
    }
    
    int CSoundManager::init(HWND hwnd)
    {
    	if (FAILED(DirectSoundCreate8(NULL, &m_pSound, NULL)))
    	{
    		MessageBox(NULL, "사운드디바이스생성실패", "SYSTEM ERROR", MB_OK);
    		return false;
    	}
    
    	// 사운드 디바이스 협조레벨 설정.
    	if (FAILED(m_pSound->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE))) // Flag값 정리
    	{
    		MessageBox(NULL, "사운드디바이스 협조레벨 설정", "SYSTEM ERROR", MB_OK);
    		return false;
    	}
    
    	return true;
    }
    
    void CSoundManager::RegisterToBGM(CSound* _pSound)
    {
    	if (m_pBGM != nullptr)
    		m_pBGM->Stop(true);
    
    	m_pBGM = _pSound;
    }
    728x90
Designed by Tistory.