added adpcm support

This commit is contained in:
Sandro Sobczyński
2020-11-10 19:31:30 +01:00
parent ac7a04b669
commit f3a1bcb47d
4 changed files with 337 additions and 254 deletions
+115 -34
View File
@@ -11,61 +11,142 @@
#ifndef _TYRA_AUDIO_ #ifndef _TYRA_AUDIO_
#define _TYRA_AUDIO_ #define _TYRA_AUDIO_
#include "../models/audio_listener.hpp"
#include <tamtypes.h> #include <tamtypes.h>
#include <stdio.h> #include <stdio.h>
#include <audsrv.h> #include <audsrv.h>
#include <kernel.h> #include <kernel.h>
#include "../models/audio_listener.hpp" #include <vector>
#define STACK_SIZE 8 * 1024 struct AudioListenerRef
{
AudioListener *listener;
u32 id;
};
/** Class responsible for audio playing */ /**
* Class responsible for audio playing.
* There are two main features:
* - WAV song (background music)
* - ADPCM samples
*/
class Audio class Audio
{ {
public: public:
/** Constructor is called by the engine. */
Audio(); Audio();
~Audio(); ~Audio();
u8 isTrackDone; // Song
ee_sema_t sema;
int fillbufferSema;
void init(u32 t_listenersAmount); /**
void play(); * Load 24bit 22050Hz Stereo WAV.
void stop(); * Can be used multiple times for song switching.
void loadSong(char *t_filename); * @param t_path Example: "song.wav" or "folder/song.wav"
void test(); */
void unloadSong(); void loadSong(char *t_path);
void setVolume(u8 t_volume);
void addListener(AudioListener *t_listener); void playSong();
void stopSong();
/**
* Set song volume.
* @param t_vol Value 0-100
*/
void setSongVolume(const u8 &t_vol);
/**
* If you want to synchronize some actions with
* background song, add audio listener.
* @returns Listener id.
*/
u32 addSongListener(AudioListener *t_listener);
/** Remove song listener. */
void removeSongListener(const u32 &t_id);
const u8 &isSongPlaying() const { return songPlaying; }
const u8 &isSongLoaded() const { return songLoaded; }
const u8 &isSongInLoop() const { return songInLoop; }
u32 getSongListenersCount() const { return songListeners.size(); }
const u8 &getVolume() const { return volume; }
/**
* Set song loop/noloop.
* @param t_set false - noloop, true - loop
*/
void setSongLoop(const u8 &t_set) { songInLoop = t_set; }
// ADPCM
/**
* Load ADPCM sample.
* @param t_path Example: "hit.adpcm" or "folder/jump.adpcm"
*/
audsrv_adpcm_t *loadADPCM(char *t_path);
/**
* Play ADPCM sample.
* ADPCM sample can't be stopped.
* @param t_adpcm ADPCM data, created by loadADPCM();
*/
void playADPCM(audsrv_adpcm_t *t_adpcm);
/**
* Play ADPCM sample.
* ADPCM sample can't be stopped.
* @param t_adpcm ADPCM data, created by loadADPCM();
* @param t_ch Channel (0-23). Type -1 for use any free channel.
*/
void playADPCM(audsrv_adpcm_t *t_adpcm, const s8 &t_ch);
/**
* Set ADPCM volume.
* @param t_vol Value 0-100
* @param t_ch Channel (0-23)
*/
void setADPCMVolume(u8 t_vol, const s8 &t_ch) { audsrv_adpcm_set_volume(t_ch, t_vol); }
// Other
/**
* Start audio thread.
* Do not call this method unless you know what you do.
* Should be called by engine.
*/
void startThread(); void startThread();
const u8 isPlaying() const { return shouldPlay; }
private: private:
void work(); u8 songLoaded, volume, realVolume, songPlaying, songInLoop, songFinished;
static void audioThread(); std::vector<AudioListenerRef> songListeners;
u8 isInitialized, shouldPlay, songLoaded, isVolumeSet, latestVolume, isADPCMLoaded; FILE *wav;
audsrv_adpcm_t adpcmSettings; audsrv_fmt_t format;
ee_thread_t audioThreadAttr; char wavChunk[2048];
u8 audioThreadStack[STACK_SIZE] ALIGNED(16); s32 chunkReadStatus;
int audioThreadId;
u8 threadStack[8 * 1024] __attribute__((aligned(16)));
u32 getThreadStackSize() { return 8 * 1024; }
u32 getSongBufferSize() { return 4 * 1024; }
ee_thread_t thread;
int threadId;
ee_sema_t sema;
s32 fillbufferSema;
void setSongFormat();
void unloadSong();
void rewindSongToStart();
AudioListener **listeners;
u32 listenersAmount;
u32 addedListeners;
void initSema(); void initSema();
void loadModules(); void loadModules();
void initAUDSRV(); void initAUDSRV();
void threadLoop();
u8 *sample1; static void audioThread();
u32 adpcmFileSize;
int ret;
char chunk[2048];
FILE *wav;
audsrv_fmt_t format;
static int fillbuffer(void *arg);
}; };
#endif #endif
+217 -212
View File
@@ -9,31 +9,34 @@
*/ */
#include "../include/modules/audio.hpp" #include "../include/modules/audio.hpp"
#include <loadfile.h>
#include "../include/utils/string.hpp" #include "../include/utils/string.hpp"
#include "../include/utils/debug.hpp" #include "../include/utils/debug.hpp"
#include <loadfile.h>
#define SONG_NAME "MOV-CIRC.WAV" #include <cstdlib>
const int AUDSRV_BUFFER_SIZE = 1024 * 4;
// ---- // ----
// Constructors/Destructors // Constructors/Destructors
// ---- // ----
Audio *audioRef; Audio *audioRef;
Audio::Audio() Audio::Audio()
{ {
addedListeners = 0; // We must set it to 0 on songStop();
isInitialized = 0; realVolume = 100;
songLoaded = 0; volume = 100;
isVolumeSet = 0; chunkReadStatus = 0;
shouldPlay = 0;
isADPCMLoaded = false; songLoaded = false;
songPlaying = false;
songInLoop = false;
songFinished = false;
audioRef = this; audioRef = this;
initSema(); initSema();
loadModules(); loadModules();
initAUDSRV(); initAUDSRV();
setSongFormat();
} }
Audio::~Audio() {} Audio::~Audio() {}
@@ -42,247 +45,249 @@ Audio::~Audio() {}
// Methods // Methods
// ---- // ----
/** Initialize audio module void Audio::loadSong(char *t_path)
* {
* - Initialize threading semaphore if (songLoaded)
* - Load modules unloadSong();
* - Initialize AUDSRV char *fullFilename = String::createConcatenated("host:", t_path);
* - Load background song wav = fopen(fullFilename, "rb");
delete[] fullFilename;
if (wav == NULL)
PRINT_ERR("Failed to open wav file!");
else
{
rewindSongToStart();
songLoaded = true;
PRINT_LOG("Song loaded!");
}
}
void Audio::playSong()
{
if (!songLoaded)
PRINT_ERR("Cant play song because was not loaded!");
volume = realVolume;
songPlaying = true;
}
void Audio::stopSong()
{
volume = 0;
audsrv_set_volume(volume);
songPlaying = false;
}
void Audio::setSongVolume(const u8 &t_vol)
{
realVolume = t_vol;
if (songPlaying)
volume = t_vol;
audsrv_set_volume(volume);
}
u32 Audio::addSongListener(AudioListener *t_listener)
{
AudioListenerRef ref;
ref.id = rand() % 1000000;
ref.listener = t_listener;
songListeners.push_back(ref);
return ref.id;
}
void Audio::removeSongListener(const u32 &t_id)
{
s32 index = -1;
for (u32 i = 0; i < songListeners.size(); i++)
if (songListeners[i].id == t_id)
{
index = i;
break;
}
if (index == -1)
PRINT_ERR("Cant remove listener because given id was not found!");
songListeners.erase(songListeners.begin() + index);
}
// ADPCM
audsrv_adpcm_t *Audio::loadADPCM(char *t_path)
{
char *fullFilename = String::createConcatenated("host:", t_path);
FILE *file = fopen(fullFilename, "rb");
delete[] fullFilename;
fseek(file, 0L, SEEK_END);
u32 adpcmFileSize = ftell(file);
u8 data[adpcmFileSize];
rewind(file);
fread(data, sizeof(u8), adpcmFileSize, file);
audsrv_adpcm_t *result = new audsrv_adpcm_t();
result->size = 0;
result->buffer = 0;
result->loop = 0;
result->pitch = 0;
result->channels = 0;
if (audsrv_load_adpcm(result, data, adpcmFileSize))
{
printf("AUDSRV returned error string: %s", audsrv_get_error_string());
PRINT_ERR("audsrv_load_adpcm() failed!");
}
fclose(file);
return result;
}
void Audio::playADPCM(audsrv_adpcm_t *t_adpcm)
{
if (audsrv_play_adpcm(t_adpcm))
{
printf("AUDSRV returned error string: %s", audsrv_get_error_string());
PRINT_ERR("audsrv_play_adpcm() failed!");
}
}
void Audio::playADPCM(audsrv_adpcm_t *t_adpcm, const s8 &t_ch)
{
if (audsrv_ch_play_adpcm(t_ch, t_adpcm))
{
printf("AUDSRV returned error string: %s", audsrv_get_error_string());
PRINT_ERR("audsrv_play_adpcm() failed!");
}
}
// Other
void Audio::startThread()
{
PRINT_LOG("Creating audio thread");
extern void *_gp;
thread.func = (void *)Audio::audioThread;
thread.stack = threadStack;
thread.stack_size = getThreadStackSize();
thread.gp_reg = (void *)&_gp;
thread.initial_priority = 0x17;
if ((threadId = CreateThread(&thread)) < 0)
PRINT_ERR("Create audio thread failed!");
PRINT_LOG("Audio thread created");
StartThread(threadId, NULL);
PRINT_LOG("Audio thread started");
}
/** Main thread loop */
void Audio::threadLoop()
{
if (!songPlaying || !songLoaded)
return;
if (songFinished)
{
if (songInLoop)
{
printf("Audio: Song finished. Running again.\n");
for (u32 i = 0; i < getSongListenersCount(); i++)
songListeners[i].listener->onAudioFinish();
rewindSongToStart();
}
else
return;
}
if (chunkReadStatus > 0)
{
WaitSema(fillbufferSema); // wait until previous chunk wasn't finished
audsrv_play_audio(wavChunk, chunkReadStatus);
for (u32 i = 0; i < getSongListenersCount(); i++)
songListeners[i].listener->onAudioTick();
}
chunkReadStatus = fread(wavChunk, 1, sizeof(wavChunk), wav);
if (chunkReadStatus < (s32)sizeof(wavChunk))
songFinished = true;
}
/**
* Close file.
* Delete song path from memory.
*/ */
void Audio::init(u32 t_listenersAmount) void Audio::unloadSong()
{ {
PRINT_LOG("Initialize audio module started"); songLoaded = false;
listenersAmount = t_listenersAmount; fclose(wav);
listeners = new AudioListener *[t_listenersAmount];
isInitialized = true;
latestVolume = 100;
PRINT_LOG("Audio module initialized!");
} }
void Audio::addListener(AudioListener *t_listener) /** Set WAV format to 16bit, 22050Hz, stereo. */
void Audio::setSongFormat()
{ {
listeners[addedListeners++] = t_listener; format.bits = 16;
format.freq = 22050;
format.channels = 2;
audsrv_set_format(&format);
} }
/** Initialize threading semaphore */ /** Fseek on wav. */
void Audio::rewindSongToStart()
{
if (wav != NULL)
fseek(wav, 0x30, SEEK_SET);
songFinished = false;
}
/** Initialize semaphore which will wait until chunk of the song is not finished. */
void Audio::initSema() void Audio::initSema()
{ {
PRINT_LOG("Creating semaphore started"); PRINT_LOG("Creating audio semaphore");
sema.init_count = 0; sema.init_count = 0;
sema.max_count = 1; sema.max_count = 1;
sema.option = 0; sema.option = 0;
fillbufferSema = CreateSema(&sema); fillbufferSema = CreateSema(&sema);
PRINT_LOG("Semaphore created"); PRINT_LOG("Audio semaphore created");
} }
/** Load LIBSD and AUDSRV */ /** Load LIBSD and AUDSRV modules */
void Audio::loadModules() void Audio::loadModules()
{ {
PRINT_LOG("Modules loading started (LIBSD, AUDSRV)"); PRINT_LOG("Modules loading started (LIBSD, AUDSRV)");
ret = SifLoadModule("rom0:LIBSD", 0, NULL); int ret = SifLoadModule("rom0:LIBSD", 0, NULL);
if (ret == -203) if (ret == -203)
PRINT_ERR("LIBSD loading failed!"); PRINT_ERR("LIBSD loading failed!");
ret = SifLoadModule("host:AUDSRV.IRX", 0, NULL); ret = SifLoadModule("host:AUDSRV.IRX", 0, NULL);
if (ret == -203) if (ret == -203)
PRINT_ERR("AUDSRV.IRX loading failed!"); PRINT_ERR("AUDSRV.IRX loading failed!");
PRINT_LOG("Modules loaded"); PRINT_LOG("Audio modules loaded");
} }
/** Initialize AUDSRV and install fillbuffer callback */ /**
* Initialize AUDSRV main and ADPCM module.
* Install AUDSRV callback.
*/
void Audio::initAUDSRV() void Audio::initAUDSRV()
{ {
PRINT_LOG("Initialize AUDSRV started"); PRINT_LOG("Initializing AUDSRV");
ret = audsrv_init(); int ret = audsrv_init();
if (ret != 0)
{
printf("AUDSRV returned error string: %s", audsrv_get_error_string());
PRINT_ERR("Failed to initialize AUDSRV!");
}
ret = audsrv_adpcm_init(); ret = audsrv_adpcm_init();
if (ret != 0) if (ret != 0)
{ {
PRINT_ERR("Failed to initialize AUDSRV!");
printf("AUDSRV returned error string: %s", audsrv_get_error_string()); printf("AUDSRV returned error string: %s", audsrv_get_error_string());
PRINT_ERR("Failed to initialize AUDSRV ADPCM!");
} }
else ret = audsrv_on_fillbuf(getSongBufferSize(), (audsrv_callback_t)iSignalSema, (void *)fillbufferSema);
if (ret != 0)
{ {
ret = audsrv_on_fillbuf(AUDSRV_BUFFER_SIZE, Audio::fillbuffer, (void *)fillbufferSema); printf("AUDSRV returned error string: %s", audsrv_get_error_string());
PRINT_ERR("Failed to initialize AUDSRV fillbuffer!");
}
PRINT_LOG("AUDSRV initialized!"); PRINT_LOG("AUDSRV initialized!");
}
} }
/** Set audio format, volume and load song */
void Audio::loadSong(char *t_filename)
{
if (!isInitialized)
{
PRINT_ERR("Please initialize audio class first!");
return;
}
PRINT_LOG("Song loading started");
format.bits = 16;
format.freq = 22050;
format.channels = 2;
char *fullFilename = String::createConcatenated("host:", t_filename);
int err = audsrv_set_format(&format);
if (err == 0)
{
PRINT_LOG("Audio format set");
if (!isVolumeSet)
setVolume(MAX_VOLUME);
PRINT_LOG("Opening song file: " SONG_NAME);
wav = fopen(fullFilename, "rb");
delete[] fullFilename;
if (wav == NULL)
{
PRINT_ERR("Failed to open wav file!");
audsrv_quit();
}
else
{
fseek(wav, 0x30, SEEK_SET);
songLoaded = 1;
PRINT_LOG("Song loaded!");
}
}
else
{
PRINT_ERR("Failed to set audio format!");
printf("AUDSRV returned error string: %s", audsrv_get_error_string());
}
}
void Audio::setVolume(u8 t_volume)
{
audsrv_set_volume(t_volume);
latestVolume = t_volume;
isVolumeSet = true;
}
/** Create and start audio thread. */
void Audio::startThread()
{
PRINT_LOG("Creating audio thread");
extern void *_gp;
audioThreadAttr.func = (void *)Audio::audioThread;
audioThreadAttr.stack = audioThreadStack;
audioThreadAttr.stack_size = STACK_SIZE;
audioThreadAttr.gp_reg = (void *)&_gp;
audioThreadAttr.initial_priority = 0x17;
if ((audioThreadId = CreateThread(&audioThreadAttr)) < 0)
PRINT_ERR("Create audio thread failed!");
PRINT_LOG("Audio thread created");
StartThread(audioThreadId, NULL);
PRINT_LOG("Audio thread started");
}
/** Play song and run it again on finish */
void Audio::work()
{
if (isADPCMLoaded)
{
if (audsrv_load_adpcm(&adpcmSettings, sample1, adpcmFileSize))
PRINT_ERR("audsrv_load_adpcm() failed!");
if (audsrv_play_adpcm(&adpcmSettings))
PRINT_ERR("audsrv_play_adpcm() failed!");
isADPCMLoaded = false;
}
if (!shouldPlay)
return;
if (!isTrackDone)
{
ret = fread(chunk, 1, sizeof(chunk), wav);
if (ret > 0)
{
WaitSema(fillbufferSema);
audsrv_play_audio(chunk, ret);
}
if (ret < (int)sizeof(chunk))
{
isTrackDone = true;
return;
}
for (u32 i = 0; i < addedListeners; i++)
listeners[i]->onAudioTick();
}
else
{
PRINT_LOG("Song " SONG_NAME " finished. Running again...");
for (u32 i = 0; i < addedListeners; i++)
listeners[i]->onAudioFinish();
fseek(wav, 0x30, SEEK_SET);
isTrackDone = false;
}
}
/** Unload audio module by closing file stream and stopping AUDSRV */
void Audio::unloadSong()
{
PRINT_LOG("Song unloaded");
fclose(wav);
}
void Audio::test()
{
// audsrv_adpcm_set_volume(100);
FILE *file = fopen("host:ziobro.adpcm", "rb");
fseek(file, 0L, SEEK_END);
adpcmFileSize = ftell(file);
sample1 = new u8[adpcmFileSize];
rewind(file);
fread(sample1, sizeof(u8), adpcmFileSize, file);
fclose(file);
adpcmSettings.size = 0;
adpcmSettings.buffer = 0;
adpcmSettings.loop = 0;
adpcmSettings.pitch = 0;
adpcmSettings.channels = 0;
isADPCMLoaded = true;
}
void Audio::play()
{
if (!isInitialized)
{
PRINT_ERR("Please initialize audio class first!");
return;
}
if (!shouldPlay)
{
shouldPlay = 1;
audsrv_set_volume(latestVolume);
}
}
void Audio::stop()
{
if (!isInitialized)
{
PRINT_ERR("Please initialize audio class first!");
return;
}
if (shouldPlay)
{
shouldPlay = 0;
audsrv_set_volume(0);
}
}
// ---
/** /**
* Do not call this function. * Do not call this function.
* This is a audio thread, runned by AudioThread::start() * This is a audio thread, runned by engine
*/ */
void Audio::audioThread() void Audio::audioThread()
{ {
while (true) while (true)
audioRef->work(); audioRef->threadLoop();
}
/**
* Do not call this function.
* This is a static callback function
* for AUDSRV fillbuffer (semaphore)
*/
int Audio::fillbuffer(void *arg)
{
iSignalSema((int)arg);
return 0;
} }
+4 -9
View File
@@ -33,10 +33,9 @@ Ari::~Ari() {}
void Ari::onInit() void Ari::onInit()
{ {
engine->renderer->setCameraDefinitions(&camera.worldView, &camera.position, camera.planes); engine->renderer->setCameraDefinitions(&camera.worldView, &camera.position, camera.planes);
engine->audio.init(0); engine->audio.setSongVolume(60);
engine->audio.setVolume(60);
engine->audio.loadSong("MOV-CIRC.WAV"); engine->audio.loadSong("MOV-CIRC.WAV");
adpcm = engine->audio.loadADPCM("ziobro.adpcm");
texRepo = engine->renderer->getTextureRepository(); texRepo = engine->renderer->getTextureRepository();
island.loadDff("sunnyisl/", "sunnyisl", 0.1F, false); island.loadDff("sunnyisl/", "sunnyisl", 0.1F, false);
@@ -78,7 +77,7 @@ void Ari::onInit()
// texRepo->addByMesh("ari/", player.mesh); // texRepo->addByMesh("ari/", player.mesh);
// engine->audio.play(); engine->audio.playSong();
} }
void Ari::initBulb() void Ari::initBulb()
@@ -91,12 +90,8 @@ void Ari::onUpdate()
{ {
if (engine->pad.isCrossClicked) if (engine->pad.isCrossClicked)
{ {
engine->audio.test();
engine->audio.stop();
engine->audio.unloadSong();
engine->audio.loadSong("nob-else.wav"); engine->audio.loadSong("nob-else.wav");
engine->audio.play(); engine->audio.playADPCM(adpcm);
printf("FPS:%f\n", engine->fps); printf("FPS:%f\n", engine->fps);
} }
camera.update(engine->pad, player.mesh); camera.update(engine->pad, player.mesh);
+2
View File
@@ -12,6 +12,7 @@
#define _ARI_ #define _ARI_
#include <tamtypes.h> #include <tamtypes.h>
#include <audsrv.h>
#include <game.hpp> #include <game.hpp>
#include <engine.hpp> #include <engine.hpp>
#include "objects/player.hpp" #include "objects/player.hpp"
@@ -38,6 +39,7 @@ private:
Player player; Player player;
Mesh island, islandAddons, skybox; Mesh island, islandAddons, skybox;
Mesh *waterFloors; Mesh *waterFloors;
audsrv_adpcm_t *adpcm;
Point *spirals; Point *spirals;
Camera camera; Camera camera;
TextureRepository *texRepo; TextureRepository *texRepo;