FMOD API
언리얼, 유니티, 크라이엔진 등 많은 게임 엔진에서 탑재해 사용하는 사운드 라이브러리이다.
출력 가능한 사운드 형식은
ALFF, ASF, ASX, DLS, FLAC, FSB, IT, M3U, MID, MOD, MP2, MP3, OGG, WAV, WMA 등등..
그냥 알고 있는 사운드 확장자는 다 지원한다고 생각해도 무방하다.
WINAPI이나 다이렉트에서도 사운드 관련 라이브러리를 제공하지만
사용에 있어서 매우 편한 FMOD API를 사용해보도록 하겠다.
회원가입만 하면 무료로 사용 가능하다.
FMOD ENGINE 2.02 버전을 받으면
\FMOD Studio API Windows\api\core 경로에 포함 파일과
라이브러리 파일을 본인 프로젝트에 사용하면 된다.
참고로 라이브러리 파일의 dll, lib 파일이 있는데
dll은 동적 라이브러리, lib은 정적 라이브러리로
dll은 실행파일에 lib은 프로젝트에 연결해주면 된다.
1. 환경 구성
헤더 파일과 라이브러리를 연결해준다.
#include "fmod.hpp"
#include "fmod_errors.h"
#pragma comment(lib, "fmod_vc.lib")
FMOD를 사용하기 위해서 3가지 인터페이스가 필요하다.
FMOD::System* m_pSystem = nullptr;
FMOD::Sound* m_pSound = nullptr;
FMOD::Channel* m_pChannel = nullptr;
system은 다이렉트 디바이스와 유사하다. 사운드 시스템이다.
sound은 사운드 포인터로 실제 사운드 메모리이다.
channel은 최대 32 채널을 갖고 중복적으로 실행이 되기 위해서 필요하다. 오디오 채널이다.
1. 시스템 생성
bool KSoundManager::Init()
{
FMOD_RESULT ret;
//FMOD 시스템 디바이스 생성
ret = FMOD::System_Create(&m_pSystem);
if (ret != FMOD_OK)
{
return false;
}
//FMOD 시스템 초기화 채널 32채널을 갖고 사운드 시스템 초기화
//배경음악도 있고 이펙트도 있음, 중복적으로 실행이 되는것
//그 카운터가 32개의 채널이다.
ret = m_pSystem->init(32, FMOD_INIT_NORMAL, 0);
if (ret != FMOD_OK)
{
return false;
}
return true;
}
FMOD::System_Create() 함수로 시스템을 생성했으면 사운드를 적재해 플레이할 수 있다.
사운드 객체는 사운드 파일과 1:1대 응이 되기 때문에, Sound 클래스와 Sound 매니저(싱글톤)를 구현해
구현하였다. 하나의 FMOD시스템이 여러 FMOD 사운드와 FMOD 채널을 사용한다고 생각하면 된다.
2. 시스템 업데이트
//프레임에서 반드시 업데이트를 호출시켜야함
m_pSystem->update();
return true;
음악은 계속해서 나와야 한다. 그래서 자체적으로 구현한
프레임 함수나 업데이트 함수에서(프로그램 루틴)
FMOD 시스템의 update()가 계속 호출하게 해야 한다.
3. 사운드 로드
KSound* sound = new KSound(m_pSystem, m_index, FileName);
std::string mutiname = to_wm(filename);
ret = m_pSystem->createSound(mutiname.c_str(),
FMOD_DEFAULT, 0, &sound->m_pSound);
if (ret != FMOD_OK)
{
sound->Release();
return nullptr;
}
//createSound 사운드 생성 완료 후 리스트에 추가
m_SoundList.insert(std::make_pair(m_index++, sound));
return sound;
}
생성했던 시스템 포인터로 createSound() 함수 호출해 사운드 메모리를 얻는다.
멀티 바이트 문자로 파일 경로를 받아와야 한다. 본인은 유니코드로 구현되어
자체 변환 함수로 바꿔주었다.
4. 사운드 실행
void KSound::SoundPlay(bool bloop)
{
if (m_pChannel != nullptr)
{
m_pChannel->isPlaying(&m_bPlay);
}
if (m_bPlay == false)
{
//채널은 플레이 사운드에서 반환이됨
//FMOD API 에서 플레이 되는 사운드의 제어를 담당함
FMOD_RESULT ret = m_pSystem->playSound(m_pSound,
nullptr, false, &m_pChannel);
if (ret == FMOD_OK)
{
//m_pChannel->setVolume(0.5f);
if (bloop)
m_pChannel->setMode(FMOD_LOOP_NORMAL);
else
m_pChannel->setMode(FMOD_LOOP_OFF);
}
}
}
사운드 포인터를 얻었다면, m_pSystem의 playSound() 함수로 사운드 포인터
를 넘겨서 플레이할 수 있다. m_channel은 기본적으로 FMOD가 관리하는데
우리는 channel 포인터로 정지, 볼륨, 루프 옵션 등을 조절할 수 있다.
위 함수에서 bool 값으로 플레이하는 사운드를 loop 할 것인지를 조절해주고 있다.
또한 sound 포인터로 전체 길이를 받을 수도 있는 getlength()나
channel 포인터로 현재 플레이 초를 받는 getPosition이 있으니
자세한 것은 Document를 참조하도록 하자.
5. 시스템 해제
m_pSystem->close();
m_pSystem->release();
시스템을 종료할때, FMOD 시스템을 끄고 포인터를 해제해준다.
전체 코드
KSoundManager.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | #include "KSoundManager.h" //인덱스로 사운드 포인터 반환 KSound* KSoundManager::GetSound(int index) { auto iter = m_SoundList.find(index); if (iter != m_SoundList.end()) { return (*iter).second; } return nullptr; } //사운드 메모리 적재하고 사운드 생성, 리스트 추가 KSound* KSoundManager::LoadSound(std::wstring filename) { FMOD_RESULT ret; TCHAR szFileName[MAX_PATH] = { 0, }; TCHAR Drive[MAX_PATH] = { 0, }; TCHAR Dir[MAX_PATH] = { 0, }; TCHAR FileName[MAX_PATH] = { 0, }; TCHAR FileExt[MAX_PATH] = { 0, }; std::wstring fullpathname = filename; _tsplitpath_s(fullpathname.c_str(), Drive, Dir, FileName, FileExt); //중복 방지 처리 for (auto data : m_SoundList) { if (data.second->m_name == FileName) { return data.second; } } //동적 사운드 메모리 로드 생성자 KSound* sound = new KSound(m_pSystem, m_index, FileName); //createSound 사운드 생성 std::string mutiname = to_wm(filename); ret = m_pSystem->createSound(mutiname.c_str(), FMOD_DEFAULT, 0, &sound->m_pSound); if (ret != FMOD_OK) { sound->Release(); return nullptr; } //createSound 사운드 생성 완료 후 리스트에 추가 m_SoundList.insert(std::make_pair(m_index++, sound)); return sound; } bool KSoundManager::Init() { FMOD_RESULT ret; //FMOD 시스템 디바이스 생성 ret = FMOD::System_Create(&m_pSystem); if (ret != FMOD_OK) { return false; } //FMOD 시스템 초기화 채널 32채널을 갖고 사운드 시스템 초기화 //배경음악도 있고 이펙트도 있음, 중복적으로 실행이 되는것 //그 카운터가 32개의 채널이다. ret = m_pSystem->init(32, FMOD_INIT_NORMAL, 0); if (ret != FMOD_OK) { return false; } return true; } bool KSoundManager::Frame() { //프레임에서 반드시 업데이트를 호출시켜야함 m_pSystem->update(); return true; } bool KSoundManager::Render() { return true; } bool KSoundManager::Release() { for (auto data : m_SoundList) { data.second->Release(); delete data.second; } m_SoundList.clear(); m_pSystem->close(); m_pSystem->release(); return true; } KSoundManager::KSoundManager() { m_index = 0; m_SoundList.clear(); m_pSystem = nullptr; } KSoundManager::~KSoundManager() { } | cs |
KSound.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #include "KSound.h" //loop 플레이 사운드 void KSound::SoundPlay(bool bloop) { if (m_pChannel != nullptr) { m_pChannel->isPlaying(&m_bPlay); } if (m_bPlay == false) { //채널은 플레이 사운드에서 반환이됨 //FMOD API 에서 플레이 되는 사운드의 제어를 담당함 FMOD_RESULT ret = m_pSystem->playSound(m_pSound, nullptr, false, &m_pChannel); if (ret == FMOD_OK) { //m_pChannel->setVolume(0.5f); if (bloop) m_pChannel->setMode(FMOD_LOOP_NORMAL); else m_pChannel->setMode(FMOD_LOOP_OFF); } } } //채널을 임시적으로 만들어 한번만 사운드 void KSound::SoundPlay_Once() { FMOD::Channel* pChannel = nullptr; // 채널은 플레이 되는 사운드의 제어를 담당. FMOD_RESULT ret = m_pSystem->playSound( m_pSound, nullptr, false, &pChannel); if (ret == FMOD_OK) { } } //사운드 끄기 void KSound::SoundStop() { if (m_pChannel != nullptr) { m_pChannel->stop(); } } //사운드 멈춤 실행중일때는 멈추고 멈춘상태에서는 재실행 void KSound::SoundPaused() { m_pChannel->isPlaying(&m_bPlay); (m_bPlay) ? m_bPlay = false : m_bPlay = true; m_pChannel->setPaused(m_bPlay); } //볼륨 관련 void KSound::SoundVolumeDown(float vol) { m_fVolume -= vol; SoundVolume(m_fVolume); } void KSound::SoundVolumeUp(float vol) { m_fVolume += vol; SoundVolume(m_fVolume); } void KSound::SoundVolume(float vol) { if (m_pChannel != nullptr) { m_fVolume = max(0.0f, m_fVolume); m_fVolume = min(1.0f, m_fVolume); m_pChannel->setVolume(m_fVolume); } } //------------------------------------ bool KSound::Init() { return true; } bool KSound::Frame() { if (m_pSound != nullptr || m_pChannel != nullptr) return true; //전체 길이 m_pSound->getLength(&m_size, FMOD_TIMEUNIT_MS); //플레이 위치 m_pChannel->getPosition(&m_pos, FMOD_TIMEUNIT_MS); return true; } bool KSound::Render() { return true; } bool KSound::Release() { if (m_pSound) { m_pSound->release(); m_pSound = nullptr; } return true; } KSound::KSound() { } KSound::KSound(FMOD::System* system, int index, std::wstring name) { m_pSystem = system; m_id = index; m_name = name; m_bPlay = false; m_fVolume = 1.0f; m_size = 0; m_pos = 0; } KSound::~KSound() { } | cs |