libraries_2_featured

in Tutorials, UE4

FMOD and UE4 C++ example

With this tutorial we are going to show you how to use the FMOD library to load and play a sound into a UE4 desktop/mobile project. In a previous tutorial we have explain how to set project paths to be able to use a thirdparty library, now we can start coding a class to use this library.

Part 1: Setting up project paths
Part 2: Using the library
Part 3: Frequency spectrum
Part 4: UE4 spectrum visualizer
Part 5: Beat tracking algorithm
Part 6: UE4 beat visualizer


We can start creating two clases, the first one to manage the library and the second one to be able to use this class with the UE4 editor.
Fmod has three main elements: system, channel and sound.
fmod_structure
The first one is in charge to control the FMOD system object, in their initialization we can specify the max channels that we can use in our game, this limits the number of simultaneous sounds that we can play, each channel can hold ONE sound. For this example we only need to play one sound so we only need one channel.

We can declare the three fmod elements in our class members as pointers. Also we need some functions to load the sound stored from a path or from memory, play the sound and pause/unpause this sound.

SoundManager_Fmod.h

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
#ifndef SoundManager_Fmod_H
#define SoundManager_Fmod_H
 
#include <string>
 
namespace FMOD 
{
	class System;
	class Channel;
	class Sound;
};
 
class SoundManager_Fmod
{
public:
	SoundManager_Fmod();
	~SoundManager_Fmod();
 
	int initialize();
	int loadSoundFromPath(std::string pathToFile);
	int loadSoundFromMemory(char* memoryPtr, unsigned int memorySize);
	void playSound();
	void pauseSound(bool unPause = false);
 
private:
	FMOD::System* _system;	FMOD::Channel* _channel;	FMOD::Sound* _sound; 
};
 
#endif

To initialize the system we need to call to their constructor, System_Create, and use an init call indicating in the first parameter the number of channels:

FMOD_RESULT result = FMOD::System_Create(&_system);
// Initialize our Instance with 1 Channel
_system->init(1, FMOD_INIT_NORMAL, NULL);

All FMOD function returns an error code that we can check to control the status of the function execution.
To load a sound we have two options, the first one is load our sound from a path, to do that we need to use a createSound function, providing the path to the sound and the pointer to the fmod sound object, we can set some flags like a looping play too.

_system->createSound(pathToFile.c_str(), FMOD_LOOP_NORMAL, 0, &_sound);

The second option is load the sound into memory and pass their pointer to this function, we need to pass and FMOD_CREATESOUNDEXINFO structure too to indicate the allocated memory size of the pointer. To use this memory we need to pass the FMOD_OPENMEMORY_POINT flag in the function call, to work with a copy FMOD_OPENMEMORY must be set, we will have to release this memory copy after use it with _sound->release()

FMOD_CREATESOUNDEXINFO sndinfo = { 0 };
sndinfo.cbsize = sizeof(sndinfo);
sndinfo.length = memorySize;
 
_system->createSound(memoryPtr, FMOD_OPENMEMORY | FMOD_LOOP_NORMAL, &sndinfo, &_sound);

To play the sound, we need to specify the channel and the sound object using the playSound function

_system->playSound(_sound, 0, false, &_channel);

To pause the sound we need to pause the channel play

_channel->setPaused(true);

SoundManager_Fmod.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
#include "SoundManager_Fmod.h"
 
#include "fmod.hpp"
 
#include <string>
 
using namespace std;
 
SoundManager_Fmod::SoundManager_Fmod():_system(NULL),_channel(NULL),_sound(NULL)
{
 
}
 
int SoundManager_Fmod::initialize()
{
	FMOD_RESULT result = FMOD::System_Create(&_system);
	if (result != FMOD_OK)
	{
		return result;
	}
	else
	{
		_system->init(1, FMOD_INIT_NORMAL, NULL);
	}
 
	return 0;
}
 
SoundManager_Fmod::~SoundManager_Fmod()
{
	if (_sound)
	{
		_sound->release();
	}
 
	if (_system)
	{
		_system->close();
		_system->release();
	}
}
 
int SoundManager_Fmod::loadSoundFromPath(std::string pathToFile)
{
	FMOD_RESULT result = _system->createSound(pathToFile.c_str(), FMOD_LOOP_NORMAL, 0, &_sound);
	return result;
}
 
int SoundManager_Fmod::loadSoundFromMemory(char* memoryPtr, unsigned int memorySize)
{
	FMOD_CREATESOUNDEXINFO sndinfo = { 0 };
	sndinfo.cbsize = sizeof(sndinfo);
	sndinfo.length = memorySize;
 
	FMOD_RESULT result = _system->createSound(memoryPtr, FMOD_OPENMEMORY | FMOD_LOOP_NORMAL, &sndinfo, &_sound);
	return result;
}
 
void SoundManager_Fmod::playSound()
{
	_system->playSound(_sound, 0, false, &_channel);
}
 
void SoundManager_Fmod::pauseSound(bool unPause)
{
	bool isPaused;
	_channel->getPaused(&isPaused);
	if (isPaused && unPause)
	{
		_channel->setPaused(false);
	}
	else if(!isPaused && !unPause)
	{
		_channel->setPaused(true);
	}
}

Our second class, the blueprint wrapper, only need to store the manager object and call their functions:

AudioManager.h

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
#pragma once
 
#include "CoreMinimal.h"
 
#include "SoundManager_Fmod.h"
#include <memory>
 
#include "UObject/NoExportTypes.h"
#include "AudioManager.generated.h"
 
UCLASS(Blueprintable, BlueprintType)
class TUTORIAL_SPECTRUM_API UAudioManager : public UObject
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintCallable, Category = Init)
		int32 InitializeManager();
 
	UFUNCTION(BlueprintCallable, Category = Actions)
		int32 PlaySong();
	UFUNCTION(BlueprintCallable, Category = Actions)
		void PauseSong(bool unPause);
 
	UFUNCTION(BlueprintPure, Category = Access)
		const FString& GetSongName() const;
 
	UAudioManager();
	~UAudioManager();
 
private:
 
	std::unique_ptr<SoundManager_Fmod> _soundManager;
	FString currentSongName;
 
};

To find the path to the sound file in the Android platform we have a problem, the packing process put their NonAssets folder inside the .apk, we would have to extract this from the .apk file before be able to play it using loadSoundFromPath. An alternative option is to load the sound file in memory using the cross-platform Unreal File System (UFS) and pass this pointer to our second load method loadSoundFromMemory.

FString songsPath = FPaths::ProjectContentDir() + "NonAssets/";
currentSongName = "A Drop A Day - Fairy Dust";
FString songFile(songsPath + currentSongName + ".wav");
 
uint8* data; 
unsigned int dataLength = 0;
 
TArray <uint8> rawFile;
FFileHelper::LoadFileToArray(rawFile, *songFile);
 
data = rawFile.GetData();
dataLength = rawFile.Num() * sizeof(uint8);

AudioManager.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
#include "AudioManager.h"
#include "Paths.h"
#include "FileHelper.h"
 
#include <string>
 
UAudioManager::UAudioManager()
{
}
 
UAudioManager::~UAudioManager()
{
}
 
int32 UAudioManager::InitializeManager()
{
	_soundManager = std::unique_ptr<SoundManager_Fmod>(new SoundManager_Fmod());
	int result = _soundManager->initialize();
	if (result > 0)
	{
		return result;
	}
	return 0;
}
 
int32 UAudioManager::PlaySong()
{
 
	FString songsPath = FPaths::ProjectContentDir() + "NonAssets/";
	currentSongName = "A Drop A Day - Fairy Dust";
	FString songFile(songsPath + currentSongName + ".wav");
 
	uint8* data; 
	unsigned int dataLength = 0;
 
	TArray <uint8> rawFile;
	FFileHelper::LoadFileToArray(rawFile, *songFile);
 
	data = rawFile.GetData();
	dataLength = rawFile.Num() * sizeof(uint8);
 
	int32 result = _soundManager->loadSoundFromMemory(reinterpret_cast<char*>(data), dataLength);
	if (result > 0)
	{
			return result; //missing file
	}
 
	_soundManager->playSound();
	return 0;
}
 
void UAudioManager::PauseSong(bool unPause)
{
	_soundManager->pauseSound(unPause);
}
 
const FString& UAudioManager::GetSongName() const
{
	return currentSongName;
}

Now we need to configure some project settings and do the last blueprint part.

The first one is to create the NonAssets folder, inside the Content folder, to store the sound file
content_folder

In the packaging options of our project we need to add this folder to the NonAssets folder list to Package
nonassets_folder

Blueprints

Our main blueprint (Actor GameBP) will store the AudioManager Instance and a Wigdet to control the audio play
A_GameBP

A_GameBP_initUI

InitializeUI function

A_GameBP_initmanager

InitializeAudioManager function

A_GameBP_begin

Actor BeginPlay Event

This basic UI has one button to play the sound and other to pause/unpause the playback
UI_Design

UI_show_hide

UI_playbutton

Play button event

UI_pausebutton

Pause button event

Now we have a basic integration of the FMOD low level library and our UE4 project, working on Win64 and Android platform.

youtube_audioplayer

Tutorial files:

github

UE4 Project

Download

Download C++ Sources

Download

Download Blueprints


Support this blog!

For the past year I've been dedicating more of my time to the creation of tutorials, mainly about game development. If you think these posts have either helped or inspired you, please consider supporting this blog. Thank you so much for your contribution!


Write a Comment

Comment

one × four =