Added menu sounds. See THENMES.md for more details.

This commit is contained in:
Aloshi 2012-10-13 13:29:53 -05:00
parent 20aa6d2a6e
commit ea1a3117a5
17 changed files with 288 additions and 26 deletions

View file

@ -1,7 +1,7 @@
CC=g++ CC=g++
CFLAGS=-c -Wall -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/usr/include/freetype2 -I/usr/include/SDL -I/usr/include -D_RPI_ CFLAGS=-c -Wall -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/usr/include/freetype2 -I/usr/include/SDL -I/usr/include -D_RPI_
LDFLAGS=-L/opt/vc/lib -lbcm_host -lEGL -lGLESv2 -lfreetype -lSDL -lboost_system -lboost_filesystem -lfreeimage LDFLAGS=-L/opt/vc/lib -lbcm_host -lEGL -lGLESv2 -lfreetype -lSDL -lboost_system -lboost_filesystem -lfreeimage -lSDL_mixer
SRCSOURCES=main.cpp Renderer.cpp Renderer_init.cpp Font.cpp Renderer_draw_gl.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp MathExp.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp components/GuiMenu.cpp components/GuiTheme.cpp components/GuiFastSelect.cpp components/GuiBox.cpp pugiXML/pugixml.cpp SRCSOURCES=main.cpp Renderer.cpp Renderer_init.cpp Font.cpp Renderer_draw_gl.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp MathExp.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp components/GuiMenu.cpp components/GuiTheme.cpp components/GuiFastSelect.cpp components/GuiBox.cpp AudioManager.cpp Sound.cpp pugiXML/pugixml.cpp
SOURCES=$(addprefix src/,$(SRCSOURCES)) SOURCES=$(addprefix src/,$(SRCSOURCES))
OBJECTS=$(SOURCES:.cpp=.o) OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=emulationstation EXECUTABLE=emulationstation

View file

@ -1,7 +1,7 @@
CC=g++ CC=g++
CFLAGS=-c -Wall -I/usr/include/freetype2 -I/usr/include/SDL -I/usr/include -D_DESKTOP_ -g CFLAGS=-c -Wall -I/usr/include/freetype2 -I/usr/include/SDL -I/usr/include -D_DESKTOP_ -g
LDFLAGS=-lGL -lfreetype -lSDL -lboost_system -lboost_filesystem -lfreeimage LDFLAGS=-lGL -lfreetype -lSDL -lboost_system -lboost_filesystem -lfreeimage -lSDL_mixer
SRCSOURCES=main.cpp Renderer.cpp Renderer_init.cpp Font.cpp Renderer_draw_gl.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp MathExp.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp components/GuiMenu.cpp components/GuiTheme.cpp components/GuiFastSelect.cpp components/GuiBox.cpp pugiXML/pugixml.cpp SRCSOURCES=main.cpp Renderer.cpp Renderer_init.cpp Font.cpp Renderer_draw_gl.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp MathExp.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp components/GuiMenu.cpp components/GuiTheme.cpp components/GuiFastSelect.cpp components/GuiBox.cpp Sound.cpp AudioManager.cpp pugiXML/pugixml.cpp
SOURCES=$(addprefix src/,$(SRCSOURCES)) SOURCES=$(addprefix src/,$(SRCSOURCES))
OBJECTS=$(SOURCES:.cpp=.o) OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=emulationstation EXECUTABLE=emulationstation

View file

@ -98,6 +98,14 @@ The Fast Select box can be themed with these tags:
`<boxCorner>` - path to the "top left corner" image file. It will be flipped for the top right, bottom right, and bottom left corners. ~ and . are expanded. `<boxCorner>` - path to the "top left corner" image file. It will be flipped for the top right, bottom right, and bottom left corners. ~ and . are expanded.
Audio
=====
Themes can also define menu sounds. Sounds should be in the .wav format.
`<menuScrollSound>` - path to the sound to play when the game list is scrolling.
List of variables List of variables
================= =================

98
src/AudioManager.cpp Normal file
View file

@ -0,0 +1,98 @@
#include "AudioManager.h"
#include "SDL.h"
#include "SDL_mixer.h"
#include <iostream>
#include "Sound.h"
#include <vector>
namespace AudioManager
{
std::vector<Sound*> sSoundVector;
bool sInitialized = false;
void init()
{
int result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16SYS, 2, 1024);
if(result == -1)
{
std::cerr << "Error initializing AudioManager!\n";
std::cerr << " " << Mix_GetError() << "\n";
return;
}
sInitialized = true;
for(unsigned int i = 0; i < sSoundVector.size(); i++)
{
sSoundVector.at(i)->init();
}
}
void registerSound(Sound* sound)
{
sSoundVector.push_back(sound);
}
void unregisterSound(Sound* sound)
{
for(unsigned int i = 0; i < sSoundVector.size(); i++)
{
if(sSoundVector.at(i) == sound)
{
sSoundVector.erase(sSoundVector.begin() + i);
return;
}
}
std::cerr << "AudioManager Error - tried to unregister a sound that wasn't registered!\n";
}
void test()
{
Mix_Chunk* sound = NULL;
sound = Mix_LoadWAV("test.wav");
if(sound == NULL)
{
std::cerr << "Error loading test sound!\n";
std::cerr << " " << Mix_GetError() << "\n";
return;
}
int channel = -1;
//third argument is play count, -1 = infinite loop, 0 = once
channel = Mix_PlayChannel(-1, sound, 0);
if(channel == -1)
{
std::cerr << "Error playing sound!\n";
std::cerr << " " << Mix_GetError() << "\n";
return;
}
while(Mix_Playing(channel) != 0);
Mix_FreeChunk(sound);
}
void deinit()
{
for(unsigned int i = 0; i < sSoundVector.size(); i++)
{
sSoundVector.at(i)->deinit();
}
Mix_CloseAudio();
sInitialized = false;
}
bool isInitialized()
{
return sInitialized;
}
}

18
src/AudioManager.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef _AUDIOMANAGER_H_
#define _AUDIOMANAGER_H_
class Sound;
namespace AudioManager
{
void registerSound(Sound* sound);
void unregisterSound(Sound* sound);
bool isInitialized();
void init();
void test();
void deinit();
}
#endif

View file

@ -64,11 +64,12 @@ namespace Renderer {
//make sure our font exists //make sure our font exists
if(!boost::filesystem::exists(fontPath)) if(!boost::filesystem::exists(fontPath))
{ {
std::cout << "Default font \"" << fontPath << "\" does not exist! Attempting to default to a system font...\n"; std::cout << "Default font \"" << fontPath << "\" does not exist! Attempting to default to a system font...";
fontPath = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"; fontPath = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf";
if(!boost::filesystem::exists(fontPath)) if(!boost::filesystem::exists(fontPath))
{ {
std::cerr << "System font \"" << fontPath << "\" wasn't found either! Well, you're kind of screwed. Sorry.\n"; std::cerr << "System font \"" << fontPath << "\" wasn't found either! Well, you're kind of screwed. Sorry.\n";
return;
} }
} }

View file

@ -20,9 +20,10 @@ namespace Renderer
{ {
std::cout << "Creating surface..."; std::cout << "Creating surface...";
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) != 0) if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) != 0)
{ {
std::cerr << "Error initializing SDL!\n"; std::cerr << "Error initializing SDL!\n";
std::cerr << " " << SDL_GetError() << "\n";
return false; return false;
} }

78
src/Sound.cpp Normal file
View file

@ -0,0 +1,78 @@
#include "Sound.h"
#include <iostream>
#include "AudioManager.h"
Sound::Sound(std::string path)
{
mSound = NULL;
AudioManager::registerSound(this);
loadFile(path);
}
Sound::~Sound()
{
deinit();
AudioManager::unregisterSound(this);
}
void Sound::loadFile(std::string path)
{
mPath = path;
init();
}
void Sound::init()
{
if(!AudioManager::isInitialized())
return;
if(mSound != NULL)
deinit();
if(mPath.empty())
return;
mSound = Mix_LoadWAV(mPath.c_str());
if(mSound == NULL)
{
std::cerr << "Error loading sound \"" << mPath << "\"!\n";
std::cerr << " " << Mix_GetError() << "\n";
}
}
void Sound::deinit()
{
if(mSound != NULL)
{
Mix_FreeChunk(mSound);
mSound = NULL;
}
}
void Sound::play()
{
if(mSound == NULL)
return;
mChannel = -1;
mChannel = Mix_PlayChannel(-1, mSound, 0);
if(mChannel == -1)
{
std::cerr << "Error playing sound!\n";
std::cerr << " " << Mix_GetError() << "\n";
}
}
bool Sound::isPlaying()
{
if(mChannel != -1 && Mix_Playing(mChannel))
return true;
else
return false;
}

26
src/Sound.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef _SOUND_H_
#define _SOUND_H_
#include <string>
#include "SDL_mixer.h"
class Sound
{
public:
Sound(std::string path = "");
~Sound();
void init();
void deinit();
void loadFile(std::string path);
void play();
bool isPlaying();
private:
std::string mPath;
int mChannel;
Mix_Chunk* mSound;
};
#endif

View file

@ -6,6 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <SDL/SDL_joystick.h> #include <SDL/SDL_joystick.h>
#include "Renderer.h" #include "Renderer.h"
#include "AudioManager.h"
std::vector<SystemData*> SystemData::sSystemVector; std::vector<SystemData*> SystemData::sSystemVector;
@ -74,6 +75,7 @@ void SystemData::launchGame(GameData* game)
//suspend SDL joystick events (these'll pile up even while something else is running) //suspend SDL joystick events (these'll pile up even while something else is running)
SDL_JoystickEventState(0); SDL_JoystickEventState(0);
AudioManager::deinit();
Renderer::deinit(); Renderer::deinit();
std::string command = mLaunchCommand; std::string command = mLaunchCommand;
@ -88,6 +90,7 @@ void SystemData::launchGame(GameData* game)
std::cout << "...launch terminated!\n"; std::cout << "...launch terminated!\n";
Renderer::init(0, 0); Renderer::init(0, 0);
AudioManager::init();
//re-enable SDL joystick events //re-enable SDL joystick events
SDL_JoystickEventState(1); SDL_JoystickEventState(1);

View file

@ -206,6 +206,7 @@ void GuiGameList::updateTheme()
mList->setSelectorColor(mTheme->getSelectorColor()); mList->setSelectorColor(mTheme->getSelectorColor());
mList->setSelectedTextColor(mTheme->getSelectedTextColor()); mList->setSelectedTextColor(mTheme->getSelectedTextColor());
mList->setCentered(mTheme->getListCentered()); mList->setCentered(mTheme->getListCentered());
mList->setScrollSound(mTheme->getMenuScrollSound());
if(mDetailed) if(mDetailed)
{ {

View file

@ -11,7 +11,6 @@
#include "../GameData.h" #include "../GameData.h"
#include "../FolderData.h" #include "../FolderData.h"
//This is where the magic happens - GuiGameList is the parent of almost every graphical element in ES at the moment. //This is where the magic happens - GuiGameList is the parent of almost every graphical element in ES at the moment.
//It has a GuiList child that handles the game list, a GuiTheme that handles the theming system, and a GuiImage for game images. //It has a GuiList child that handles the game list, a GuiTheme that handles the theming system, and a GuiImage for game images.
class GuiGameList : GuiComponent class GuiGameList : GuiComponent

View file

@ -19,7 +19,7 @@ GuiList<listType>::GuiList(int offsetX, int offsetY, Renderer::FontSize fontsize
mFont = fontsize; mFont = fontsize;
mSelectorColor = 0x000000; mSelectorColor = 0x000000;
mSelectedTextColorOverride = -1; mSelectedTextColorOverride = -1;
mScrollSound = NULL;
mDrawCentered = true; mDrawCentered = true;
InputManager::registerComponent(this); InputManager::registerComponent(this);
@ -92,13 +92,13 @@ void GuiList<listType>::onInput(InputManager::InputButton button, bool keyDown)
if(button == InputManager::DOWN) if(button == InputManager::DOWN)
{ {
mScrollDir = 1; mScrollDir = 1;
mSelection++; scroll();
} }
if(button == InputManager::UP) if(button == InputManager::UP)
{ {
mScrollDir = -1; mScrollDir = -1;
mSelection--; scroll();
} }
}else{ }else{
if((button == InputManager::DOWN && mScrollDir > 0) || (button == InputManager::UP && mScrollDir < 0)) if((button == InputManager::DOWN && mScrollDir > 0) || (button == InputManager::UP && mScrollDir < 0))
@ -108,12 +108,6 @@ void GuiList<listType>::onInput(InputManager::InputButton button, bool keyDown)
mScrollDir = 0; mScrollDir = 0;
} }
} }
if(mSelection < 0)
mSelection += mRowVector.size();
if(mSelection >= (int)mRowVector.size())
mSelection -= mRowVector.size();
} }
} }
@ -141,16 +135,26 @@ void GuiList<listType>::onTick(int deltaTime)
{ {
mScrollAccumulator -= SCROLLTIME; mScrollAccumulator -= SCROLLTIME;
mSelection += mScrollDir; scroll();
if(mSelection < 0)
mSelection += mRowVector.size();
if(mSelection >= (int)mRowVector.size())
mSelection -= mRowVector.size();
} }
} }
} }
} }
template <typename listType>
void GuiList<listType>::scroll()
{
mSelection += mScrollDir;
if(mSelection < 0)
mSelection += mRowVector.size();
if(mSelection >= (int)mRowVector.size())
mSelection -= mRowVector.size();
if(mScrollSound)
mScrollSound->play();
}
//list management stuff //list management stuff
template <typename listType> template <typename listType>
void GuiList<listType>::addObject(std::string name, listType obj, int color) void GuiList<listType>::addObject(std::string name, listType obj, int color)
@ -249,3 +253,9 @@ void GuiList<listType>::setSelection(int i)
{ {
mSelection = i; mSelection = i;
} }
template <typename listType>
void GuiList<listType>::setScrollSound(Sound* sound)
{
mScrollSound = sound;
}

View file

@ -6,6 +6,7 @@
#include "../InputManager.h" #include "../InputManager.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include "../Sound.h"
//A graphical list. Supports multiple colors for rows and scrolling. //A graphical list. Supports multiple colors for rows and scrolling.
//TODO - add truncation to text rendering if name exceeds a maximum width (a trailing elipses, perhaps). //TODO - add truncation to text rendering if name exceeds a maximum width (a trailing elipses, perhaps).
@ -34,7 +35,7 @@ public:
void setSelectorColor(int selectorColor); void setSelectorColor(int selectorColor);
void setSelectedTextColor(int selectedColor); void setSelectedTextColor(int selectedColor);
void setCentered(bool centered); void setCentered(bool centered);
void setScrollSound(Sound* sound);
void setTextOffsetX(int textoffsetx); void setTextOffsetX(int textoffsetx);
int getObjectCount(); int getObjectCount();
@ -45,6 +46,8 @@ private:
static const int SCROLLDELAY = 507; static const int SCROLLDELAY = 507;
static const int SCROLLTIME = 200; static const int SCROLLTIME = 200;
void scroll(); //helper method, scrolls in whatever direction scrollDir is
int mScrollDir, mScrollAccumulator; int mScrollDir, mScrollAccumulator;
bool mScrolling; bool mScrolling;
@ -63,6 +66,7 @@ private:
std::vector<ListRow> mRowVector; std::vector<ListRow> mRowVector;
int mSelection; int mSelection;
Sound* mScrollSound;
}; };
#include "GuiList.cpp" #include "GuiList.cpp"

View file

@ -24,6 +24,8 @@ int GuiTheme::getSelectedTextColor() { return mListSelectedColor; }
GuiBoxData GuiTheme::getBoxData() { return mBoxData; } GuiBoxData GuiTheme::getBoxData() { return mBoxData; }
Sound* GuiTheme::getMenuScrollSound() { return &mMenuScrollSound; }
GuiTheme::GuiTheme(std::string path) GuiTheme::GuiTheme(std::string path)
{ {
setDefaults(); setDefaults();
@ -60,6 +62,8 @@ void GuiTheme::setDefaults()
mBoxData.verticalPath = ""; mBoxData.verticalPath = "";
mBoxData.verticalTiled = false; mBoxData.verticalTiled = false;
mBoxData.cornerPath = ""; mBoxData.cornerPath = "";
mMenuScrollSound.loadFile("");
} }
void GuiTheme::deleteComponents() void GuiTheme::deleteComponents()
@ -127,10 +131,11 @@ void GuiTheme::readXML(std::string path)
mGameImageOffsetY = strToFloat(root.child("gameImageOffsetY").text().get(), mGameImageOffsetY); mGameImageOffsetY = strToFloat(root.child("gameImageOffsetY").text().get(), mGameImageOffsetY);
mListTextOffsetX = strToFloat(root.child("listTextOffsetX").text().get(), mListTextOffsetX); mListTextOffsetX = strToFloat(root.child("listTextOffsetX").text().get(), mListTextOffsetX);
//sounds
mMenuScrollSound.loadFile(root.child("menuScrollSound").text().get());
//recursively create children for all <components> with proper parenting //recursively create children for all <components> with proper parenting
createComponentChildren(root, this); createComponentChildren(root, this);
//std::cout << "Finished parsing theme.\n";
} }
void GuiTheme::createComponentChildren(pugi::xml_node node, GuiComponent* parent) void GuiTheme::createComponentChildren(pugi::xml_node node, GuiComponent* parent)

View file

@ -4,6 +4,7 @@
#include "../GuiComponent.h" #include "../GuiComponent.h"
#include "../pugiXML/pugixml.hpp" #include "../pugiXML/pugixml.hpp"
#include "GuiBox.h" #include "GuiBox.h"
#include "../Sound.h"
//This class loads an XML-defined list of GuiComponents. //This class loads an XML-defined list of GuiComponents.
class GuiTheme : public GuiComponent class GuiTheme : public GuiComponent
@ -29,6 +30,8 @@ public:
float getGameImageOffsetY(); float getGameImageOffsetY();
GuiBoxData getBoxData(); GuiBoxData getBoxData();
Sound* getMenuScrollSound();
private: private:
void setDefaults(); void setDefaults();
void deleteComponents(); void deleteComponents();
@ -50,6 +53,8 @@ private:
float mListOffsetX, mGameImageOffsetY, mListTextOffsetX; float mListOffsetX, mGameImageOffsetY, mListTextOffsetX;
GuiBoxData mBoxData; GuiBoxData mBoxData;
Sound mMenuScrollSound;
}; };
#endif #endif

View file

@ -7,6 +7,7 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "components/GuiInputConfig.h" #include "components/GuiInputConfig.h"
#include <SDL.h> #include <SDL.h>
#include "AudioManager.h"
#include "platform.h" #include "platform.h"
@ -71,7 +72,7 @@ int main(int argc, char* argv[])
bool running = true; bool running = true;
//the renderer also takes care of setting up SDL for input //the renderer also takes care of setting up SDL for input and sound
bool renderInit = Renderer::init(width, height); bool renderInit = Renderer::init(width, height);
if(!renderInit) if(!renderInit)
{ {
@ -80,9 +81,12 @@ int main(int argc, char* argv[])
} }
SDL_JoystickEventState(SDL_ENABLE); //initialize audio
AudioManager::init();
SDL_JoystickEventState(SDL_ENABLE);
//make sure the config directory exists //make sure the config directory exists
std::string home = getenv("HOME"); std::string home = getenv("HOME");
std::string configDir = home + "/.emulationstation"; std::string configDir = home + "/.emulationstation";
@ -171,6 +175,7 @@ int main(int argc, char* argv[])
} }
AudioManager::deinit();
Renderer::deleteAll(); Renderer::deleteAll();
Renderer::deinit(); Renderer::deinit();
SystemData::deleteSystems(); SystemData::deleteSystems();