Unverified Commit 954de841 authored by Rob Loach's avatar Rob Loach Committed by GitHub
Browse files

Merge pull request #269 from libretro/dev

0.24.0
parents 25fa00fd a6a961b3
......@@ -73,3 +73,8 @@
url = https://github.com/Didstopia/physfs.git
ignore = dirty
branch = master
[submodule "vendor/cppcodec"]
path = vendor/cppcodec
url = https://github.com/tplgy/cppcodec.git
ignore = dirty
branch = master
......@@ -4,6 +4,21 @@ All notable changes to [ChaiLove](https://github.com/RobLoach/ChaiLove) will be
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## 0.24.0 - 2018-09-03
### Features
- Update to use libretro-common audio
- Added `love.filesystem.newFileData()`
- Added `love.system.getClipboardText()`
- Added `love.system.setClipboardText()`
- Added `love.data.encode()`
- Added `love.data.decode()`
### Chores
- Added `love.filesystem.mount()` test
- Updated documentation
- Updated unit testing framework
- Additional method chaining functions
## 0.23.1 - 2018-08-28
### Fixes
- Clean up of git submodules
......
......@@ -66,7 +66,7 @@ docs/html: docs-clean
doxygen docs/Doxyfile
docs-start: docs/html
php -S localhost:9999 -t docs/html
php -S localhost:8080 -t docs/html
docs-clean:
rm -rf docs/html
......
......@@ -22,6 +22,30 @@ FLAGS += -I$(CORE_DIR)/vendor/filesystem
# libretro-common
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
SOURCES_C += \
$(CORE_DIR)/vendor/libretro-deps/libz/zutil.c \
......@@ -147,6 +171,9 @@ SOURCES_C += $(CORE_DIR)/vendor/libretro-common/utils/md5.c
# TinySHA1
FLAGS += -I$(CORE_DIR)/vendor/TinySHA1
# cppcodec
FLAGS += -I$(CORE_DIR)/vendor/cppcodec
# ChaiScript
ifeq ($(HAVE_CHAISCRIPT),)
FLAGS += -I$(CORE_DIR)/vendor/chaiscript/include
......
......@@ -62,7 +62,7 @@ retroarch -L chailove_libretro.so main.chai
## Development
Behind ChaiLove, there's the documentation, compiling it, along with testing.
Behind ChaiLove, there's the [documentation](#documentation), [compiling it](#compiling), along with [testing](#testing).
### Compiling
......@@ -88,7 +88,7 @@ retroarch -L chailove_libretro.so test/main.chai
### Documentation
Visit the [ChaiLove API documentation](https://rawgit.com/libretro/libretro-chailove/docs/). Build it through [Doxygen](http://www.stack.nl/~dimitri/doxygen/) by using:
See the [ChaiLove API documentation](https://rawgit.com/libretro/libretro-chailove/docs/). Build it through [Doxygen](http://www.stack.nl/~dimitri/doxygen/) by using:
```
make docs
......@@ -96,12 +96,19 @@ make docs
### Workflow
ChaiLove uses [Gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) where new features are merged to `dev`, and then a periodic release is made to the `master` branch. Releases use [Semantic Versioning](https://semver.org) to ensure version compatibility.
ChaiLove uses modified [Gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) and [Semantic Versioning](https://semver.org) workflow...
## Authors
1. New features and fixes are added to the [`dev`](https://github.com/libretro/libretro-chailove/tree/dev) branch
2. When a release is put out, the `dev` branch is merged to `master`, and a tag is made
## Contributors
The following individuals contributed to ChaiLove:
- [@RobLoach](http://github.com/robloach)
- [webgeek1234](http://github.com/webgeek1234) for the Android support
- [twinaphex](http://github.com/twinaphex)
- [orbea](http://github.com/orbea)
- [fetzerch](http://github.com/fetzerch)
- [sdl-libretro](https://github.com/r-type/sdl-libretro) by [@r-type](https://github.com/r-type)
- [Logo](docs/chailove.png) by [@baxysquare](https://github.com/baxysquare)
......@@ -23,7 +23,7 @@ PROJECT_NAME = "ChaiLove API"
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = "0.23.1"
PROJECT_NUMBER = "0.24.0"
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
......
......@@ -64,6 +64,9 @@ bool ChaiLove::load(const std::string& file) {
std::string version = CHAILOVE_VERSION_STRING GIT_VERSION;
std::cout << "[ChaiLove] ChaiLove " << version.c_str() << std::endl;
// Iniitalize some of the initial subsystems.
sound.load();
// Initalize the file system.
bool loaded = filesystem.init(file);
if (!loaded) {
......@@ -85,11 +88,6 @@ bool ChaiLove::load(const std::string& file) {
// Load up the window dimensions.
window.load(config);
// Initialize the soud, if needed.
if (config.modules.sound) {
sound.load();
}
console.load(config);
graphics.load();
image.load();
......@@ -169,6 +167,9 @@ std::string ChaiLove::demo() {
}
void ChaiLove::update() {
// Update the sound system.
sound.update();
// Update and poll all the events.
event.update();
......
......@@ -47,9 +47,9 @@
#define SRC_CHAILOVE_H_
#define CHAILOVE_VERSION_MAJOR 0
#define CHAILOVE_VERSION_MINOR 23
#define CHAILOVE_VERSION_PATCH 1
#define CHAILOVE_VERSION_STRING "0.23.1"
#define CHAILOVE_VERSION_MINOR 24
#define CHAILOVE_VERSION_PATCH 0
#define CHAILOVE_VERSION_STRING "0.24.0"
#include "SDL.h"
#include "libretro.h"
......
......@@ -3,28 +3,29 @@
*
* This provides a quick introduction on how to get started with [ChaiLove](http://github.com/libretro/libretro-chailove).
*
* ## Get ChaiLove
* ## 1. Install ChaiLove
*
* ChaiLove is a [libretro](https://www.libretro.com/) core, which can be run through [RetroArch](http://retroarch.com/). To play the sample game [Floppy Bird](https://github.com/RobLoach/ChaiLove-FloppyBird)...
* ChaiLove is a [libretro](https://www.libretro.com/) core, which can be run through [RetroArch](http://retroarch.com/). It can be installed through the user interface...
* 1. Run [RetroArch](http://retroarch.com/)
* 2. *Online Updater* → *Core Updator* → *ChaiLove*
*
* ## Running Games
* ## 2. Running Games
*
* ChaiLove can load a game in two ways:
*
* 1. Run on a `main.chai` file
* 2. Run on a compressed `game.chailove` file
* 1. Run the `main.chai` file
* 2. Run the compressed `game.chailove` file
*
* To download and play the sample game, [Floppy Bird](https://github.com/RobLoach/ChaiLove-FloppyBird)...
*
* To [download and play](https://github.com/RobLoach/ChaiLove-FloppyBird/releases) the sample [Floppy Bird](https://github.com/RobLoach/ChaiLove-FloppyBird) game...
* 1. Run RetroArch
* 2. Visit *Online Updater* → *Content Downloader* → *ChaiLove* → *Floppy Bird*
* 3. *Load Content* → *Downloads* → *Floppy Bird.chailove*
*
* To run games through ChaiLove manually through the command line interface...
* To run games through the command line...
*
* @code
* retroarch -L chailove_libretro.so path/to/FloppyBird.chailove
* retroarch -L chailove_libretro.so FloppyBird.chailove
* @endcode
*
* ## Next Steps
......
......@@ -6,22 +6,21 @@
#include "libretro.h"
#include "ChaiLove.h"
static bool use_audio_cb;
int16_t audio_buffer[2 * (44100 / 60)];
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;
static retro_audio_sample_batch_t audio_batch_cb;
void retro_set_video_refresh(retro_video_refresh_t cb) {
video_cb = 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) {
audio_batch_cb = cb;
ChaiLove::getInstance()->sound.audio_batch_cb = 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) {
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
extern "C" {
#endif
/**
* libretro-sdl callback; Send through the audio.
*/
void libretro_audio_cb(int16_t left, int16_t right) {
audio_cb(left, right);
// Nothing?
// audio_cb(left, right);
}
/**
......@@ -257,7 +244,8 @@ bool retro_serialize(void *data, size_t size) {
return false;
}
// Compress the JSON state data.
// Encode the JSON state data.
// state = app->data.encode("string", "base64", state);
state = app->data.compress(state);
// Save the information to the state data.
......@@ -269,7 +257,7 @@ bool retro_serialize(void *data, size_t size) {
* libretro callback; Unserialize the given data and load the state.
*/
bool retro_unserialize(const void *data, size_t size) {
if (!ChaiLove::hasInstance()) {
if (!ChaiLove::hasInstance() || size <= 0) {
return false;
}
std::cout << "[ChaiLove] retro_unserialize" << std::endl;
......@@ -286,6 +274,7 @@ bool retro_unserialize(const void *data, size_t size) {
ChaiLove* app = ChaiLove::getInstance();
// Decompress the state data.
// loadData = app->data.decode("string", "base64", loadData);
loadData = app->data.decompress(loadData);
// Finally, load the string.
......@@ -333,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 };
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.
std::string gamePath(info ? info->path : "");
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 @@
#include <string>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "../../../ChaiLove.h"
#include "AudioState.h"
#include "physfs.h"
namespace love {
namespace Types {
namespace Audio {
SoundData::SoundData(const std::string& filename) {
// Load the file.
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);
if (result < 0) {
std::cout << "[ChaiLove] Failed to load SoundData " << filename << app->filesystem.getLastError() << std::endl;
// Load the file.
int size = 0;
void* buffer = app->filesystem.readBuffer(filename, size);
if (buffer == NULL) {
std::cout << "[ChaiLove] [SoundData] Failed to load file buffer " << filename << std::endl;
return;
}
sndta.fp = file;
bps = sndta.head.NumChannels * sndta.head.BitsPerSample / 8;
// Load the file into the buffer.
// 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() {
......@@ -32,6 +37,10 @@ SoundData::~SoundData() {
}
float SoundData::getVolume() {
if (m_voice != NULL) {
m_volume = audio_mixer_voice_get_volume(m_voice);
}
return m_volume;
}
......@@ -42,66 +51,88 @@ SoundData& SoundData::setVolume(float volume) {
volume = 0.0f;
}
m_volume = volume;
if (m_voice != NULL) {
audio_mixer_voice_set_volume(m_voice, m_volume);
}
return *this;
}
void SoundData::unload() {
if (isLoaded()) {
PHYSFS_close(sndta.fp);
sndta.fp = NULL;
if (m_voice != NULL) {
audio_mixer_stop(m_voice);
}
}
bool SoundData::play() {
if (isLoaded()) {
PHYSFS_seek(sndta.fp, WAV_HEADER_SIZE);
state = Playing;
return true;
audio_mixer_destroy(m_sound);
m_sound = NULL;
}
return false;
}
bool SoundData::resume() {
if (isLoaded()) {
state = Playing;
return true;
}
return false;
}
bool SoundData::pause() {
bool SoundData::play() {
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 false;
}
bool SoundData::stop() {
state = Stopped;
if (isLoaded()) {
PHYSFS_seek(sndta.fp, WAV_HEADER_SIZE);
if (m_voice != NULL) {
audio_mixer_stop(m_voice);
}
}
return true;
}
bool SoundData::isLoaded() {
return sndta.fp != NULL;
return m_sound != NULL;
}
bool SoundData::isPlaying() {
return state == Playing;
return m_playing;
}
bool SoundData::isLooping() {
return loop;
return m_loop;
}
SoundData& SoundData::setLooping(bool looping) {
loop = looping;
m_loop = looping;
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 Types
} // namespace love
......@@ -2,34 +2,13 @@
#define SRC_LOVE_TYPES_AUDIO_SOUNDDATA_H_
#include <string>
#include "AudioState.h"
#include "physfs.h"
#include "audio/audio_mixer.h"
namespace love {
namespace Types {
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.
*/
......@@ -49,12 +28,6 @@ class SoundData {
bool stop();
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();
/**
......@@ -80,16 +53,6 @@ class SoundData {
*/
bool isPlaying();
/**
* Resumes playing the given Source.
*/
bool resume();
/**
* Pauses the given Source.
*/
bool pause();
/**
* Returns whether the Source will loop.
*/
......@@ -100,7 +63,16 @@ class SoundData {
*/
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
......
#include "FileData.h"
#include <string>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "filesystem/path.h"
#include "../../../ChaiLove.h"
namespace love {
namespace Types {
namespace FileSystem {
FileData::FileData(const std::string& filepath) : m_filepath(filepath) {
// Nothing.
}
int FileData::getSize() {
ChaiLove* app = ChaiLove::getInstance();
return app->filesystem.getSize(m_filepath);
}
std::string FileData::getFilename() {
return m_filepath;
}
std::string FileData::getString() {
ChaiLove* app = ChaiLove::getInstance();
return app->filesystem.read(m_filepath);
}
std::string FileData::getExtension() {
::filesystem::path p(m_filepath.c_str());
std::string extension(p.extension());
return extension;
}
} // namespace FileSystem
} // namespace Types
} // namespace love
#ifndef SRC_LOVE_TYPES_FILESYSTEM_FILEDATA_H_
#define SRC_LOVE_TYPES_FILESYSTEM_FILEDATA_H_
#include <string>
namespace love {
namespace Types {