Unverified Commit 629f0e9c authored by RobLoach's avatar RobLoach Committed by GitHub
Browse files

Update Audio System to libretro-common (#260)

* Update libretro-common

* stub the audio

* Fix conversation

* Update

* Compile

* Fix defines

* Init

* Update

* Update libretro-common

* Add initial conver

* deps: Update libretro-common

* Add debugging

* debugging

* Update libretro-common

* debug

* Add more initializing of libretro-audio

* Update audio

* Update audio

* Add love.filesystem.readBuffer()

* Remove unneedded header

* Fix SD

* Clean up vars

* Update

* Use hard coded array size

* Update audio
parent 458931dc
...@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ...@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added `love.system.setClipboardText()` - Added `love.system.setClipboardText()`
- Added `love.data.encode()` - Added `love.data.encode()`
- Added `love.data.decode()` - Added `love.data.decode()`
- New audio system from libretro-common
### Chores ### Chores
- Added `love.filesystem.mount()` test - Added `love.filesystem.mount()` test
......
...@@ -22,6 +22,30 @@ FLAGS += -I$(CORE_DIR)/vendor/filesystem ...@@ -22,6 +22,30 @@ FLAGS += -I$(CORE_DIR)/vendor/filesystem
# libretro-common # libretro-common
FLAGS += -I$(CORE_DIR)/vendor/libretro-common/include FLAGS += -I$(CORE_DIR)/vendor/libretro-common/include
# libretro-common audio
SOURCES_C += $(wildcard \
$(CORE_DIR)/vendor/libretro-common/audio/audio_mix.c \
$(CORE_DIR)/vendor/libretro-common/audio/audio_mixer.c \
$(CORE_DIR)/vendor/libretro-common/formats/wav/rwav.c \
$(CORE_DIR)/vendor/libretro-common/memmap/memalign.c \
$(CORE_DIR)/vendor/libretro-common/audio/conversion/*.c \
$(CORE_DIR)/vendor/libretro-common/streams/file_stream.c \
$(CORE_DIR)/vendor/libretro-common/audio/resampler/audio_resampler.c \
$(CORE_DIR)/vendor/libretro-common/audio/resampler/drivers/sinc_resampler.c \
$(CORE_DIR)/vendor/libretro-common/audio/resampler/drivers/null_resampler.c \
$(CORE_DIR)/vendor/libretro-common/audio/resampler/drivers/nearest_resampler.c \
$(CORE_DIR)/vendor/libretro-common/vfs/*.c \
$(CORE_DIR)/vendor/libretro-common/features/features_cpu.c \
$(CORE_DIR)/vendor/libretro-common/file/config_file_userdata.c \
$(CORE_DIR)/vendor/libretro-common/file/file_path.c \
$(CORE_DIR)/vendor/libretro-common/compat/compat_strl.c \
$(CORE_DIR)/vendor/libretro-common/file/config_file_userdata.c \
$(CORE_DIR)/vendor/libretro-common/file/config_file.c \
$(CORE_DIR)/vendor/libretro-common/lists/string_list.c \
$(CORE_DIR)/vendor/libretro-common/compat/compat_strcasestr.c \
$(CORE_DIR)/vendor/libretro-common/lists/string_list.c \
)
# zlib # zlib
SOURCES_C += \ SOURCES_C += \
$(CORE_DIR)/vendor/libretro-deps/libz/zutil.c \ $(CORE_DIR)/vendor/libretro-deps/libz/zutil.c \
......
...@@ -64,6 +64,9 @@ bool ChaiLove::load(const std::string& file) { ...@@ -64,6 +64,9 @@ bool ChaiLove::load(const std::string& file) {
std::string version = CHAILOVE_VERSION_STRING GIT_VERSION; std::string version = CHAILOVE_VERSION_STRING GIT_VERSION;
std::cout << "[ChaiLove] ChaiLove " << version.c_str() << std::endl; std::cout << "[ChaiLove] ChaiLove " << version.c_str() << std::endl;
// Iniitalize some of the initial subsystems.
sound.load();
// Initalize the file system. // Initalize the file system.
bool loaded = filesystem.init(file); bool loaded = filesystem.init(file);
if (!loaded) { if (!loaded) {
...@@ -85,11 +88,6 @@ bool ChaiLove::load(const std::string& file) { ...@@ -85,11 +88,6 @@ bool ChaiLove::load(const std::string& file) {
// Load up the window dimensions. // Load up the window dimensions.
window.load(config); window.load(config);
// Initialize the soud, if needed.
if (config.modules.sound) {
sound.load();
}
console.load(config); console.load(config);
graphics.load(); graphics.load();
image.load(); image.load();
...@@ -169,6 +167,9 @@ std::string ChaiLove::demo() { ...@@ -169,6 +167,9 @@ std::string ChaiLove::demo() {
} }
void ChaiLove::update() { void ChaiLove::update() {
// Update the sound system.
sound.update();
// Update and poll all the events. // Update and poll all the events.
event.update(); event.update();
......
...@@ -6,22 +6,21 @@ ...@@ -6,22 +6,21 @@
#include "libretro.h" #include "libretro.h"
#include "ChaiLove.h" #include "ChaiLove.h"
static bool use_audio_cb;
int16_t audio_buffer[2 * (44100 / 60)];
static retro_video_refresh_t video_cb; static retro_video_refresh_t video_cb;
// This is needed to allow SDL-libretro to compile.
// @see SDL_LIBRETROaudio.c:37
retro_audio_sample_t audio_cb; retro_audio_sample_t audio_cb;
static retro_audio_sample_batch_t audio_batch_cb;
void retro_set_video_refresh(retro_video_refresh_t cb) { void retro_set_video_refresh(retro_video_refresh_t cb) {
video_cb = cb; video_cb = cb;
} }
void retro_set_audio_sample(retro_audio_sample_t cb) { void retro_set_audio_sample(retro_audio_sample_t cb) {
audio_cb = cb; ChaiLove::getInstance()->sound.audio_cb = cb;
} }
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {
audio_batch_cb = cb; ChaiLove::getInstance()->sound.audio_batch_cb = cb;
} }
void retro_set_input_poll(retro_input_poll_t cb) { void retro_set_input_poll(retro_input_poll_t cb) {
...@@ -32,25 +31,13 @@ void retro_set_input_state(retro_input_state_t cb) { ...@@ -32,25 +31,13 @@ void retro_set_input_state(retro_input_state_t cb) {
ChaiLove::input_state_cb = cb; ChaiLove::input_state_cb = cb;
} }
static void emit_audio(void) {
if (ChaiLove::hasInstance()) {
ChaiLove::getInstance()->audio.mixer_render(audio_buffer);
audio_batch_cb(audio_buffer, 44100 / 60);
}
}
static void audio_set_state(bool enable) {
(void)enable;
}
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/**
* libretro-sdl callback; Send through the audio.
*/
void libretro_audio_cb(int16_t left, int16_t right) { void libretro_audio_cb(int16_t left, int16_t right) {
audio_cb(left, right); // Nothing?
// audio_cb(left, right);
} }
/** /**
...@@ -335,10 +322,6 @@ bool retro_load_game(const struct retro_game_info *info) { ...@@ -335,10 +322,6 @@ bool retro_load_game(const struct retro_game_info *info) {
struct retro_frame_time_callback frame_cb = { frame_time_cb, 1000000 / 60 }; struct retro_frame_time_callback frame_cb = { frame_time_cb, 1000000 / 60 };
ChaiLove::environ_cb(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, &frame_cb); ChaiLove::environ_cb(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, &frame_cb);
// Make the audio callback.
struct retro_audio_callback audio_cb = { emit_audio, audio_set_state };
use_audio_cb = ChaiLove::environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK, &audio_cb);
// Find the game path. // Find the game path.
std::string gamePath(info ? info->path : ""); std::string gamePath(info ? info->path : "");
if (gamePath == ".") { if (gamePath == ".") {
......
#ifndef SRC_LOVE_TYPES_AUDIO_AUDIOSTATE_H_
#define SRC_LOVE_TYPES_AUDIO_AUDIOSTATE_H_
namespace love {
namespace Types {
namespace Audio {
enum AudioState {
Stopped = 0,
Paused,
Playing
};
} // namespace Audio
} // namespace Types
} // namespace love
#endif // SRC_LOVE_TYPES_AUDIO_AUDIOSTATE_H_
...@@ -2,29 +2,34 @@ ...@@ -2,29 +2,34 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "../../../ChaiLove.h" #include "../../../ChaiLove.h"
#include "AudioState.h"
#include "physfs.h"
namespace love { namespace love {
namespace Types { namespace Types {
namespace Audio { namespace Audio {
SoundData::SoundData(const std::string& filename) { SoundData::SoundData(const std::string& filename) {
// Load the file.
ChaiLove* app = ChaiLove::getInstance(); ChaiLove* app = ChaiLove::getInstance();
PHYSFS_file* file = app->filesystem.openFile(filename);
if (file == NULL) {
return;
}
int result = PHYSFS_readBytes(file, &sndta.head, sizeof(uint8_t) * WAV_HEADER_SIZE); // Load the file.
if (result < 0) { int size = 0;
std::cout << "[ChaiLove] Failed to load SoundData " << filename << app->filesystem.getLastError() << std::endl; void* buffer = app->filesystem.readBuffer(filename, size);
if (buffer == NULL) {
std::cout << "[ChaiLove] [SoundData] Failed to load file buffer " << filename << std::endl;
return; return;
} }
sndta.fp = file; // Load the file into the buffer.
bps = sndta.head.NumChannels * sndta.head.BitsPerSample / 8; // TODO(RobLoach): Check the audio file extensions of ".wav".
m_sound = audio_mixer_load_wav(buffer, size);
free(buffer);
if (m_sound == NULL) {
std::cout << "[ChaiLove] [SoundData] Failed to load wav from buffer " << filename << std::endl;
}
} }
SoundData::~SoundData() { SoundData::~SoundData() {
...@@ -32,6 +37,10 @@ SoundData::~SoundData() { ...@@ -32,6 +37,10 @@ SoundData::~SoundData() {
} }
float SoundData::getVolume() { float SoundData::getVolume() {
if (m_voice != NULL) {
m_volume = audio_mixer_voice_get_volume(m_voice);
}
return m_volume; return m_volume;
} }
...@@ -42,66 +51,88 @@ SoundData& SoundData::setVolume(float volume) { ...@@ -42,66 +51,88 @@ SoundData& SoundData::setVolume(float volume) {
volume = 0.0f; volume = 0.0f;
} }
m_volume = volume; m_volume = volume;
if (m_voice != NULL) {
audio_mixer_voice_set_volume(m_voice, m_volume);
}
return *this; return *this;
} }
void SoundData::unload() { void SoundData::unload() {
if (isLoaded()) { if (m_voice != NULL) {
PHYSFS_close(sndta.fp); audio_mixer_stop(m_voice);
sndta.fp = NULL;
} }
}
bool SoundData::play() {
if (isLoaded()) { if (isLoaded()) {
PHYSFS_seek(sndta.fp, WAV_HEADER_SIZE); audio_mixer_destroy(m_sound);
state = Playing; m_sound = NULL;
return true;
} }
return false;
} }
bool SoundData::resume() { bool SoundData::play() {
if (isLoaded()) {
state = Playing;
return true;
}
return false;
}
bool SoundData::pause() {
if (isLoaded()) { if (isLoaded()) {
state = Paused; m_voice = audio_mixer_play(m_sound, m_loop, m_volume, audioCallback);
if (m_voice != NULL) {
m_playing = true;
}
return true; return true;
} }
return false; return false;
} }
bool SoundData::stop() { bool SoundData::stop() {
state = Stopped;
if (isLoaded()) { if (isLoaded()) {
PHYSFS_seek(sndta.fp, WAV_HEADER_SIZE); if (m_voice != NULL) {
audio_mixer_stop(m_voice);
}
} }
return true; return true;
} }
bool SoundData::isLoaded() { bool SoundData::isLoaded() {
return sndta.fp != NULL; return m_sound != NULL;
} }
bool SoundData::isPlaying() { bool SoundData::isPlaying() {
return state == Playing; return m_playing;
} }
bool SoundData::isLooping() { bool SoundData::isLooping() {
return loop; return m_loop;
} }
SoundData& SoundData::setLooping(bool looping) { SoundData& SoundData::setLooping(bool looping) {
loop = looping; m_loop = looping;
return *this; return *this;
} }
void SoundData::audioCallback(audio_mixer_sound_t* sound, unsigned reason) {
// This is called when an audio finishes.
ChaiLove* app = ChaiLove::getInstance();
// Loop through all sounds, and find the given one.
std::vector<Types::Audio::SoundData*> v = app->sound.sounds;
for(std::vector<SoundData*>::iterator it = v.begin(); it != v.end(); ++it) {
SoundData* currentsound = *it;
if (currentsound == NULL) {
continue;
}
// We found the active sound.
if (currentsound->m_sound == sound) {
switch (reason) {
case AUDIO_MIXER_SOUND_FINISHED:
case AUDIO_MIXER_SOUND_STOPPED:
currentsound->m_playing = false;
break;
case AUDIO_MIXER_SOUND_REPEATED:
currentsound->m_playing = true;
break;
}
break;
}
}
}
} // namespace Audio } // namespace Audio
} // namespace Types } // namespace Types
} // namespace love } // namespace love
...@@ -2,34 +2,13 @@ ...@@ -2,34 +2,13 @@
#define SRC_LOVE_TYPES_AUDIO_SOUNDDATA_H_ #define SRC_LOVE_TYPES_AUDIO_SOUNDDATA_H_
#include <string> #include <string>
#include "AudioState.h"
#include "physfs.h" #include "physfs.h"
#include "audio/audio_mixer.h"
namespace love { namespace love {
namespace Types { namespace Types {
namespace Audio { namespace Audio {
typedef struct {
char ChunkID[4];
uint32_t ChunkSize;
char Format[4];
char Subchunk1ID[4];
uint32_t Subchunk1Size;
uint16_t AudioFormat;
uint16_t NumChannels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlign;
uint16_t BitsPerSample;
char Subchunk2ID[4];
uint32_t Subchunk2Size;
} wavhead_t;
typedef struct {
PHYSFS_File* fp = NULL;
wavhead_t head;
} snd_SoundData;
/** /**
* Contains audio samples that you can playback. * Contains audio samples that you can playback.
*/ */
...@@ -49,12 +28,6 @@ class SoundData { ...@@ -49,12 +28,6 @@ class SoundData {
bool stop(); bool stop();
void unload(); void unload();
snd_SoundData sndta;
unsigned bps = 0;
bool loop = false;
float m_volume = 1.0f;
float pitch = 1.0f;
AudioState state = Stopped;
bool isLoaded(); bool isLoaded();
/** /**
...@@ -80,16 +53,6 @@ class SoundData { ...@@ -80,16 +53,6 @@ class SoundData {
*/ */
bool isPlaying(); bool isPlaying();
/**
* Resumes playing the given Source.
*/
bool resume();
/**
* Pauses the given Source.
*/
bool pause();
/** /**
* Returns whether the Source will loop. * Returns whether the Source will loop.
*/ */
...@@ -100,7 +63,16 @@ class SoundData { ...@@ -100,7 +63,16 @@ class SoundData {
*/ */
SoundData& setLooping(bool loop); SoundData& setLooping(bool loop);
int WAV_HEADER_SIZE = 44; // Properties
bool m_playing = false;
bool m_loop = false;
float m_volume = 1.0f;
audio_mixer_sound* m_sound = NULL;
// TODO(RobLoach): Make voice a vector.
audio_mixer_voice_t* m_voice = NULL;
// The audio callback for when a sound finishes.
static void audioCallback(audio_mixer_sound_t* sound, unsigned reason);
}; };
} // namespace Audio } // namespace Audio
......
#include "audio.h" #include "audio.h"
#include <string> #include <string>
#include "Types/Audio/SoundData.h" #include "Types/Audio/SoundData.h"
#include "Types/Audio/AudioState.h"
#include "../ChaiLove.h" #include "../ChaiLove.h"
#include "sound.h" #include "sound.h"
#include "physfs.h" #include "physfs.h"
#include "audio/conversion/float_to_s16.h"
using love::Types::Audio::SoundData; using love::Types::Audio::SoundData;
using love::Types::Audio::AudioState::Stopped;
namespace love { namespace love {
...@@ -44,60 +43,4 @@ audio& audio::setVolume(float volume) { ...@@ -44,60 +43,4 @@ audio& audio::setVolume(float volume) {
m_volume = volume; m_volume = volume;
return *this; return *this;
} }
void audio::mixer_render(int16_t *buffer) {
if (!ChaiLove::hasInstance()) {
return;
}
ChaiLove* app = ChaiLove::getInstance();
// Clear buffer
memset(buffer, 0, AUDIO_FRAMES * 2 * sizeof(int16_t));
// Mix all the sounds together.
for (std::vector<SoundData*>::size_type i = 0; i != app->sound.sounds.size(); i++) {
SoundData* currentSound = app->sound.sounds[i];
if (currentSound == NULL || !currentSound->isLoaded() || currentSound->state == Stopped) {
continue;
}
// Figure out what is playing.
uint8_t* rawsamples8 = reinterpret_cast<uint8_t*>(calloc(AUDIO_FRAMES * currentSound->bps, sizeof(uint8_t)));
// bool end = !fread(rawsamples8, sizeof(uint8_t), AUDIO_FRAMES * currentSound->bps, currentSound->sndta.fp);
PHYSFS_readBytes(currentSound->sndta.fp, rawsamples8, sizeof(uint8_t) * AUDIO_FRAMES * currentSound->bps);
bool end = PHYSFS_eof(currentSound->sndta.fp);
int16_t* rawsamples16 = reinterpret_cast<int16_t*>(rawsamples8);
for (unsigned j = 0; j < AUDIO_FRAMES; j++) {
int16_t left = 0;
int16_t right = 0;
if (currentSound->sndta.head.NumChannels == 1 && currentSound->sndta.head.BitsPerSample == 8) {
left = right = rawsamples8[j] * 64;
}
if (currentSound->sndta.head.NumChannels == 2 && currentSound->sndta.head.BitsPerSample == 8) {
left = rawsamples8[j * 2 + 0] * 64;
right = rawsamples8[j * 2 + 1] * 64;
}
if (currentSound->sndta.head.NumChannels == 1 && currentSound->sndta.head.BitsPerSample == 16) {
left = right = rawsamples16[j];
}
if (currentSound->sndta.head.NumChannels == 2 && currentSound->sndta.head.BitsPerSample == 16) {
left = rawsamples16[j * 2 + 0];
right = rawsamples16[j * 2 + 1];
}
buffer[j * 2 + 0] += left * currentSound->getVolume() * m_volume;
buffer[j * 2 + 1] += right * currentSound->getVolume() * m_volume;
}
if (end) {
if (currentSound->loop) {
currentSound->play();
} else {
currentSound->stop();
}
}
free(rawsamples8);
}
}
} // namespace love } // namespace love
...@@ -33,10 +33,6 @@ class audio { ...@@ -33,10 +33,6 @@ class audio {
SoundData* newSource(const std::string& filename, const std::string& type); SoundData* newSource(const std::string& filename, const std::string& type);
SoundData* newSource(const std::string& filename);