02
18

FMOD API 

 

언리얼, 유니티, 크라이엔진 등 많은 게임 엔진에서 탑재해 사용하는 사운드 라이브러리이다.

 

출력 가능한 사운드 형식은

ALFF, ASF, ASX, DLS, FLAC, FSB, IT, M3U, MID, MOD, MP2, MP3, OGG, WAV, WMA 등등..

그냥 알고 있는 사운드 확장자는 다 지원한다고 생각해도 무방하다. 

 

WINAPI이나 다이렉트에서도 사운드 관련 라이브러리를 제공하지만

사용에 있어서 매우 편한 FMOD API를 사용해보도록 하겠다. 

회원가입만 하면 무료로 사용 가능하다. 

 

https://www.fmod.com/

 

FMOD

The sonic universe of Creaks We talked to the creative team responsible for the audio of Creaks, the latest game from renowned Czech game developer, Amanita Design. Visit blog

www.fmod.com

 

 

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를 참조하도록 하자. 

 

https://www.fmod.com/resources/documentation-api?version=2.02&page=core-guide.html#initialization-simple-start-up-with-no-configuration-necessary 

 

FMOD - Core API Guide

3. Core API Guide The FMOD Core API is a programmer API that is intended to cover the basics / primitives of sound. This includes concepts such as 'Channels', 'Sounds', 'DSP', 'ChannelGroups', 'Sound Groups', 'Recording' and concepts for 3D Sound and occlu

www.fmod.com

 

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

 

COMMENT