build: Merge submodules into repo

This commit is contained in:
robshape 2023-04-15 10:36:01 +02:00
parent 540a30f719
commit 4c64279f90
422 changed files with 106715 additions and 8 deletions

6
.gitmodules vendored
View file

@ -1,6 +0,0 @@
[submodule "src/minarch/libretro-common"]
path = src/minarch/libretro-common
url = https://github.com/libretro/libretro-common
[submodule "other/DinguxCommander"]
path = other/DinguxCommander
url = https://github.com/shauninman/DinguxCommander.git

@ -1 +0,0 @@
Subproject commit 2b8b29042c172dbca78eadbbb989e51a0b9b635d

1
other/DinguxCommander/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
output/

View file

@ -0,0 +1,76 @@
###########################################################
ifeq (,$(PLATFORM))
PLATFORM=$(UNION_PLATFORM)
endif
ifeq (,$(PLATFORM))
$(error please specify PLATFORM, eg. make PLATFORM=trimui)
endif
ifeq (,$(CROSS_COMPILE))
$(error missing CROSS_COMPILE for this toolchain)
endif
###########################################################
CXX:=$(CROSS_COMPILE)g++
CXXFLAGS:=-DPLATFORM_$(shell echo $(PLATFORM) | tr a-z A-Z) -O3 -fomit-frame-pointer -ffast-math -funroll-loops
ifeq (miyoomini,$(PLATFORM))
CXXFLAGS+= -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7ve -O3
endif
ifeq (rg35xx,$(PLATFORM))
CXXFLAGS+= -marm -mtune=cortex-a9 -mfpu=neon-fp16 -mfloat-abi=hard -march=armv7-a -O3
endif
RESDIR:=res
CXXFLAGS+= -Wno-unknown-pragmas -Wno-format -Wno-write-strings # -Wall
CXXFLAGS+=-DRESDIR="\"$(RESDIR)\""
LINKFLAGS+=-s
LINKFLAGS+=-lSDL -lSDL_image -lSDL_ttf
ifdef V
CMD:=
SUM:=@\#
else
CMD:=@
SUM:=@echo
endif
OUTDIR:=output
EXECUTABLE:=$(OUTDIR)/DinguxCommander
OBJS:=main.o sdlutils.o resourceManager.o fileLister.o commander.o panel.o \
dialog.o window.o fileutils.o viewer.o keyboard.o
DEPFILES:=$(patsubst %.o,$(OUTDIR)/%.d,$(OBJS))
.PHONY: all clean
all: $(EXECUTABLE)
$(EXECUTABLE): $(addprefix $(OUTDIR)/,$(OBJS))
$(SUM) " LINK $@"
$(CMD)$(CXX) $(LINKFLAGS) -o $@ $^
$(OUTDIR)/%.o: src/%.cpp
@mkdir -p $(@D)
$(SUM) " CXX $@"
$(CMD)$(CXX) $(CXXFLAGS) -MP -MMD -MF $(@:%.o=%.d) -c $< -o $@
@touch $@ # Force .o file to be newer than .d file.
clean:
$(SUM) " RM $(OUTDIR)"
$(CMD)rm -rf $(OUTDIR)
# Load dependency files.
-include $(DEPFILES)
# Generate dependencies that do not exist yet.
# This is only in case some .d files have been deleted;
# in normal operation this rule is never triggered.
$(DEPFILES):

View file

@ -0,0 +1,8 @@
DinguxCommander
===============
DinguxCommander is a two-pane file manager in the style of Norton Commander.
Original site: http://beyondds.free.fr/index.php?Dingoo-dinguxcommander
This repository contains customizations to make it integrate better with OpenDingux.

View file

@ -0,0 +1,82 @@
DinguxCommander for Dingux
--------------------------
History
-------
2011-03-09 : Version 2.1
- Rename a file/dir
- Create new dir
- Minor fixes
2011-02-16 : Version 2.0
- Execute file
- View file
- Disk information
- Disk used by a list of selected files/dirs
- New button mapping
- Small fixes
2011-02-05 : Version 1.0 : Initial version.
Introduction
------------
DinguxCommander is a file manager for Dingoo (Dingux).
It uses two vertical panels side by side, one being the source and the other the
destination, like many 'commander-style' file managers such as Norton Commander
or Midnight Commander.
DinguxCommander allows to:
o Copy, move and delete multiple files.
o View a file
o Execute a file
o Rename a file or directory
o Create a new directory
o Display disk space used by a list of selected files/dirs
o Display disk information (used, available, total)
Installation
------------
As usual:
o Extract the archive, keeping the directory structure
o Copy the directory 'DinguxCommander' and its contents somewhere on your SD
card under '/boot/local' (for example: in /boot/local/apps/)
o Create a shortcut to DinguxCommander.dge in your favorite menu.
Controls
--------
o D-pad Move
o L/R Page up/page down
o A For a directory: open
For a file: view or execute
o B Go to parent directory / cancel
o Y System actions:
- Select all items
- Select no items
- Create new directory
- Display disk information
- Quit program
o X Actions on selected items:
- Copy to destination directory
- Move to destination directory
- Rename (appears only if 1 item is selected)
- Delete
- Display disk used
o SELECT Select highlighted item.
Selected items are displayed in red.
o START Open highlighted directory in destination panel.
If a file is highlighted, open current directory in destination panel.
Credits
-------
Homepage: http://beyondds.free.fr/
Development: Mia
Font: Beycan Çetin

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

View file

@ -0,0 +1,353 @@
#include <iostream>
#include <sstream>
#include "commander.h"
#include "resourceManager.h"
#include "sdlutils.h"
#include "def.h"
#include "dialog.h"
#include "fileutils.h"
#include "viewer.h"
#include "keyboard.h"
#define X_LEFT 1
#define X_RIGHT 162
CCommander::CCommander(const std::string &p_pathL, const std::string &p_pathR):
CWindow::CWindow(),
m_panelLeft(p_pathL, X_LEFT),
m_panelRight(p_pathR, X_RIGHT),
m_panelSource(NULL),
m_panelTarget(NULL),
m_background(CResourceManager::instance().getSurface(CResourceManager::T_SURFACE_BG))
{
m_panelSource = &m_panelLeft;
m_panelTarget = &m_panelRight;
}
CCommander::~CCommander(void)
{
}
void CCommander::render(const bool p_focus) const
{
INHIBIT(std::cout << "CCommander::render fullscreen: " << isFullScreen() << " focus: " << p_focus << std::endl;)
// Draw background image
SDL_utils::applySurface(0, 0, m_background, Globals::g_screen);
// Draw panels
m_panelLeft.render(p_focus && (m_panelSource == &m_panelLeft));
m_panelRight.render(p_focus && (m_panelSource == &m_panelRight));
}
const bool CCommander::keyPress(const SDL_Event &p_event)
{
CWindow::keyPress(p_event);
bool l_ret(false);
switch (p_event.key.keysym.sym)
{
case MYKEY_SYSTEM:
case MYKEY_MENU:
if (openSystemMenu())
m_panelSource->refresh();
l_ret = true;
break;
case MYKEY_UP:
l_ret = m_panelSource->moveCursorUp(1);
break;
case MYKEY_DOWN:
l_ret = m_panelSource->moveCursorDown(1);
break;
case MYKEY_PAGEUP:
l_ret = m_panelSource->moveCursorUp(NB_VISIBLE_LINES - 1);
break;
case MYKEY_PAGEDOWN:
l_ret = m_panelSource->moveCursorDown(NB_VISIBLE_LINES - 1);
break;
case MYKEY_LEFT:
if (m_panelSource == &m_panelRight)
{
m_panelSource = &m_panelLeft;
m_panelTarget = &m_panelRight;
l_ret = true;
}
break;
case MYKEY_RIGHT:
if (m_panelSource == &m_panelLeft)
{
m_panelSource = &m_panelRight;
m_panelTarget = &m_panelLeft;
l_ret = true;
}
break;
case MYKEY_OPEN:
if (m_panelSource->isDirectoryHighlighted())
{
// It's a dir => open it
l_ret = m_panelSource->open();
}
else
{
// It's a file => open execute menu
openExecuteMenu();
l_ret = true;
}
break;
case MYKEY_PARENT:
l_ret = m_panelSource->goToParentDir();
break;
case MYKEY_OPERATION:
// If there's no file in the select list, add current file
if (m_panelSource->getSelectList().empty() && m_panelSource->getHighlightedItem() != "..")
m_panelSource->addToSelectList(false);
if (!m_panelSource->getSelectList().empty())
{
if (openCopyMenu())
{
// Refresh file lists
m_panelSource->refresh();
m_panelTarget->refresh();
}
else
{
if (m_panelSource->getSelectList().size() == 1 && (*m_panelSource->getSelectList().begin()) == m_panelSource->getHighlightedIndex())
m_panelSource->selectNone();
}
l_ret = true;
}
break;
case MYKEY_SELECT:
l_ret = m_panelSource->addToSelectList(true);
break;
case MYKEY_TRANSFER:
if (m_panelSource->isDirectoryHighlighted() && m_panelSource->getHighlightedItem() != "..")
l_ret = m_panelTarget->open(m_panelSource->getHighlightedItemFull());
else
l_ret = m_panelTarget->open(m_panelSource->getCurrentPath());
break;
default:
break;
}
return l_ret;
}
const bool CCommander::keyHold(void)
{
bool l_ret(false);
switch(m_lastPressed)
{
case MYKEY_UP:
if (tick(SDL_GetKeyState(NULL)[MYKEY_UP]))
l_ret = m_panelSource->moveCursorUp(1);
break;
case MYKEY_DOWN:
if (tick(SDL_GetKeyState(NULL)[MYKEY_DOWN]))
l_ret = m_panelSource->moveCursorDown(1);
break;
case MYKEY_PAGEUP:
if (tick(SDL_GetKeyState(NULL)[MYKEY_PAGEUP]))
l_ret = m_panelSource->moveCursorUp(NB_VISIBLE_LINES - 1);
break;
case MYKEY_PAGEDOWN:
if (tick(SDL_GetKeyState(NULL)[MYKEY_PAGEDOWN]))
l_ret = m_panelSource->moveCursorDown(NB_VISIBLE_LINES - 1);
break;
case MYKEY_SELECT:
if (tick(SDL_GetKeyState(NULL)[MYKEY_SELECT]))
l_ret = m_panelSource->addToSelectList(true);
break;
default:
break;
}
return l_ret;
}
const bool CCommander::openCopyMenu(void) const
{
bool l_ret(false);
int l_dialogRetVal(0);
bool l_rename(false);
// List of selected files
std::vector<std::string> l_list;
m_panelSource->getSelectList(l_list);
// The rename option appears only if one item is selected
l_rename = (l_list.size() == 1);
{
bool l_loop(false);
std::ostringstream l_stream;
l_stream << l_list.size() << " selected:";
// File operation dialog
CDialog l_dialog(l_stream.str(), 0, Y_LIST + m_panelSource->getHighlightedIndexRelative() * LINE_HEIGHT);
l_dialog.addOption(m_panelSource == &m_panelLeft ? "Copy >" : "< Copy");
l_dialog.addOption(m_panelSource == &m_panelLeft ? "Move >" : "< Move");
if (l_rename)
l_dialog.addOption("Rename");
l_dialog.addOption("Delete");
l_dialog.addOption("Disk used");
l_dialog.init();
do
{
l_loop = false;
l_dialogRetVal = l_dialog.execute();
if (l_dialogRetVal == 3 + l_rename)
{
CDialog l_dialog2("", l_dialog.getX() + l_dialog.getImage()->w - DIALOG_BORDER, l_dialog.getY() + DIALOG_BORDER + (l_dialog.getHighlightedIndex() + 1) * LINE_HEIGHT);
l_dialog2.addOption("Yes");
l_dialog2.addOption("No");
l_dialog2.init();
if (l_dialog2.execute() != 1)
l_loop = true;
}
}
while (l_loop);
}
// Perform operation
switch (l_dialogRetVal)
{
case 1:
// Copy
File_utils::copyFile(l_list, m_panelTarget->getCurrentPath());
l_ret = true;
break;
case 2:
// Move
File_utils::moveFile(l_list, m_panelTarget->getCurrentPath());
l_ret = true;
break;
case 3:
if (l_rename)
{
// Rename
CKeyboard l_keyboard(m_panelSource->getHighlightedItem());
if (l_keyboard.execute() == 1 && !l_keyboard.getInputText().empty() && l_keyboard.getInputText() != m_panelSource->getHighlightedItem())
{
File_utils::renameFile(m_panelSource->getHighlightedItemFull(), m_panelSource->getCurrentPath() + (m_panelSource->getCurrentPath() == "/" ? "" : "/") + l_keyboard.getInputText());
l_ret = true;
}
}
else
{
// Delete
File_utils::removeFile(l_list);
l_ret = true;
}
break;
case 4:
if (l_rename)
{
// Delete
File_utils::removeFile(l_list);
l_ret = true;
}
else
// Disk used
File_utils::diskUsed(l_list);
break;
case 5:
if (l_rename)
// Disk used
File_utils::diskUsed(l_list);
break;
default:
break;
}
return l_ret;
}
const bool CCommander::openSystemMenu(void)
{
bool l_ret(false);
int l_dialogRetVal(0);
// Selection dialog
{
CDialog l_dialog("System:", 0, Y_LIST + m_panelSource->getHighlightedIndexRelative() * LINE_HEIGHT);
l_dialog.addOption("Select all");
l_dialog.addOption("Select none");
l_dialog.addOption("New directory");
l_dialog.addOption("Disk info");
l_dialog.addOption("Quit");
l_dialog.init();
l_dialogRetVal = l_dialog.execute();
}
switch (l_dialogRetVal)
{
case 1:
// Select all
m_panelSource->selectAll();
break;
case 2:
// Select none
m_panelSource->selectNone();
break;
case 3:
// New dir
{
CKeyboard l_keyboard("");
if (l_keyboard.execute() == 1 && !l_keyboard.getInputText().empty())
{
File_utils::makeDirectory(m_panelSource->getCurrentPath() + (m_panelSource->getCurrentPath() == "/" ? "" : "/") + l_keyboard.getInputText());
l_ret = true;
}
}
break;
case 4:
// Disk info
File_utils::diskInfo();
break;
case 5:
// Quit
m_retVal = -1;
break;
default:
break;
}
return l_ret;
}
void CCommander::openExecuteMenu(void) const
{
int l_dialogRetVal(0);
// Dialog
{
CDialog l_dialog(m_panelSource->getHighlightedItem() + ":", 0, Y_LIST + m_panelSource->getHighlightedIndexRelative() * LINE_HEIGHT);
l_dialog.addOption("View");
l_dialog.addOption("Execute");
l_dialog.init();
l_dialogRetVal = l_dialog.execute();
}
// Perform operation
switch (l_dialogRetVal)
{
case 1:
// View
{
// Check size
const std::string l_file(m_panelSource->getHighlightedItemFull());
INHIBIT(std::cout << "File size: " << File_utils::getFileSize(l_file) << std::endl;)
if (File_utils::getFileSize(l_file) > VIEWER_SIZE_MAX)
{
// File is too big to be viewed!
CDialog l_dialog("Error:", 0, 0);
l_dialog.addLabel("File is too big!");
l_dialog.addOption("OK");
l_dialog.init();
l_dialog.execute();
}
else
{
CViewer l_viewer(m_panelSource->getHighlightedItemFull());
l_viewer.execute();
}
}
break;
case 2:
// Execute
File_utils::executeFile(m_panelSource->getHighlightedItemFull());
break;
default:
break;
}
}
const bool CCommander::isFullScreen(void) const
{
return true;
}

View file

@ -0,0 +1,54 @@
#ifndef _COMMANDER_H_
#define _COMMANDER_H_
#include <SDL/SDL.h>
#include "panel.h"
#include "window.h"
class CCommander : public CWindow
{
public:
// Constructor
CCommander(const std::string &p_pathL, const std::string &p_pathR);
// Destructor
virtual ~CCommander(void);
private:
// Forbidden
CCommander(void);
CCommander(const CCommander &p_source);
const CCommander &operator =(const CCommander &p_source);
// Key press management
virtual const bool keyPress(const SDL_Event &p_event);
// Key hold management
virtual const bool keyHold(void);
// Draw
virtual void render(const bool p_focus) const;
// Is window full screen?
virtual const bool isFullScreen(void) const;
// Open the file operation menus
const bool openCopyMenu(void) const;
void openExecuteMenu(void) const;
// Open the selection menu
const bool openSystemMenu(void);
// The two panels
CPanel m_panelLeft;
CPanel m_panelRight;
CPanel* m_panelSource;
CPanel* m_panelTarget;
// Pointers to resources
SDL_Surface *m_background;
};
#endif

View file

@ -0,0 +1,124 @@
#ifndef _DEF_H_
#define _DEF_H_
//~ #define INHIBIT(X) X
#define INHIBIT(X) /* X */
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define SCREEN_BPP 16
#define SURFACE_FLAGS SDL_SWSURFACE
#define MS_PER_FRAME 33
// Panel
#define LINE_HEIGHT 15
#define NB_VISIBLE_LINES 14
#define Y_LIST 17
#define Y_HEADER 3
#if defined(PLATFORM_TRIMUI)
#define Y_OFFSET 2 // added for larger Commander-11.ttf
#else
#define Y_OFFSET 0
#endif
#define Y_FOOTER 228
#define H_FOOTER 13
// Dialogs
#define DIALOG_BORDER 2
#define DIALOG_MARGIN 8
// Colors
#define COLOR_KEY 255,0,255
#define COLOR_TEXT_NORMAL 70,27,10
#define COLOR_TEXT_TITLE 233,229,227
#define COLOR_TEXT_DIR 75,70,164
#define COLOR_TEXT_SELECTED 255,0,0
#define COLOR_CURSOR_1 232,152,80
#define COLOR_CURSOR_2 232,201,173
#define COLOR_BG_1 255,255,255
#define COLOR_BG_2 232,228,224
#define COLOR_BORDER 102,85,74
#define DINGOO_X SDLK_LSHIFT
#define DINGOO_Y SDLK_LALT
#if defined(PLATFORM_TRIMUI)
#define MYKEY_UP SDLK_UP // Up
#define MYKEY_RIGHT SDLK_RIGHT // Right
#define MYKEY_DOWN SDLK_DOWN // Down
#define MYKEY_LEFT SDLK_LEFT // Left
#define MYKEY_SYSTEM SDLK_LALT // Y
#define MYKEY_PAGEUP SDLK_TAB // L
#define MYKEY_PAGEDOWN SDLK_BACKSPACE // R
#define MYKEY_OPEN SDLK_SPACE // A
#define MYKEY_PARENT SDLK_LCTRL // B
#define MYKEY_OPERATION SDLK_LSHIFT // X
#define MYKEY_SELECT SDLK_RCTRL // SELECT
#define MYKEY_TRANSFER SDLK_RETURN // START
#define MYKEY_MENU SDLK_ESCAPE // MENU
#define PATH_DEFAULT "/"
#define FILE_SYSTEM "/dev/mmcblk0p1"
#elif defined(PLATFORM_RG35XX)
#define MYKEY_UP SDLK_KATAKANA
#define MYKEY_RIGHT SDLK_KATAKANAHIRAGANA
#define MYKEY_DOWN SDLK_HIRAGANA
#define MYKEY_LEFT SDLK_HENKAN
#define MYKEY_SYSTEM SDLK_RCTRL // Y
#define MYKEY_PAGEUP SDLK_RALT // L
#define MYKEY_PAGEDOWN SDLK_BREAK // R
#define MYKEY_OPEN SDLK_MUHENKAN // A
#define MYKEY_PARENT SDLK_KP_JPCOMMA // B
#define MYKEY_OPERATION SDLK_KP_ENTER // X
#define MYKEY_SELECT SDLK_PRINT // SELECT
#define MYKEY_TRANSFER SDLK_KP_DIVIDE // START
#define MYKEY_MENU SDLK_PAGEUP // MENU
#define PATH_DEFAULT "/"
#define FILE_SYSTEM "/dev/mmcblk0p1"
#elif defined(PLATFORM_MIYOOMINI)
#define MYKEY_UP SDLK_UP // Up
#define MYKEY_RIGHT SDLK_RIGHT // Right
#define MYKEY_DOWN SDLK_DOWN // Down
#define MYKEY_LEFT SDLK_LEFT // Left
#define MYKEY_SYSTEM SDLK_LALT // Y
#define MYKEY_PAGEUP SDLK_e // L
#define MYKEY_PAGEDOWN SDLK_t // R
#define MYKEY_OPEN SDLK_SPACE // A
#define MYKEY_PARENT SDLK_LCTRL // B
#define MYKEY_OPERATION SDLK_LSHIFT // X
#define MYKEY_SELECT SDLK_RCTRL // SELECT
#define MYKEY_TRANSFER SDLK_RETURN // START
#define MYKEY_MENU SDLK_ESCAPE // MENU
#define PATH_DEFAULT "/"
#define FILE_SYSTEM "/dev/mmcblk0p1"
#elif defined(PLATFORM_RG350) || defined(PLATFORM_ODBETA)
#define MYKEY_UP SDLK_UP // Up
#define MYKEY_RIGHT SDLK_RIGHT // Right
#define MYKEY_DOWN SDLK_DOWN // Down
#define MYKEY_LEFT SDLK_LEFT // Left
#define MYKEY_SYSTEM SDLK_LSHIFT // X
#define MYKEY_PAGEUP SDLK_TAB // L
#define MYKEY_PAGEDOWN SDLK_BACKSPACE // R
#define MYKEY_OPEN SDLK_LCTRL // A
#define MYKEY_PARENT SDLK_LALT // B
#define MYKEY_OPERATION SDLK_SPACE // Y
#define MYKEY_SELECT SDLK_ESCAPE // SELECT
#define MYKEY_TRANSFER SDLK_RETURN // START
#define MYKEY_MENU SDLK_HOME // POWER
#define PATH_DEFAULT "/"
#define FILE_SYSTEM "/dev/mmcblk0p1"
#elif defined(PLATFORM_RETROFW)
#define MYKEY_UP SDLK_UP // Up
#define MYKEY_RIGHT SDLK_RIGHT // Right
#define MYKEY_DOWN SDLK_DOWN // Down
#define MYKEY_LEFT SDLK_LEFT // Left
#define MYKEY_SYSTEM SDLK_LSHIFT // X
#define MYKEY_PAGEUP SDLK_TAB // L
#define MYKEY_PAGEDOWN SDLK_BACKSPACE // R
#define MYKEY_OPEN SDLK_LCTRL // A
#define MYKEY_PARENT SDLK_LALT // B
#define MYKEY_OPERATION SDLK_SPACE // Y
#define MYKEY_SELECT SDLK_ESCAPE // SELECT
#define MYKEY_TRANSFER SDLK_RETURN // START
#define MYKEY_MENU SDLK_END // POWER
#define PATH_DEFAULT "/"
#define FILE_SYSTEM "/dev/mmcblk0p1"
#endif
#endif

View file

@ -0,0 +1,260 @@
#include <iostream>
#include "dialog.h"
#include "sdlutils.h"
#include "resourceManager.h"
#include "def.h"
CDialog::CDialog(const std::string &p_title, const Sint16 p_x, const Sint16 p_y):
CWindow(),
m_nbTitle(false),
m_nbLabels(0),
m_nbOptions(0),
m_highlightedLine(0),
m_image(NULL),
m_cursor1(NULL),
m_cursor2(NULL),
m_x(p_x),
m_y(p_y),
m_cursorX(0),
m_cursorY(0),
m_font(CResourceManager::instance().getFont()),
m_font_sm(CResourceManager::instance().getSmallFont())
{
// Title
if (!p_title.empty())
{
m_nbTitle = true;
m_lines.push_back(p_title);
}
// Init clip
m_clip.x = 0;
m_clip.y = 0;
m_clip.w = 0;
m_clip.h = 0;
}
CDialog::~CDialog(void)
{
// Free surfaces
if (m_image != NULL)
{
SDL_FreeSurface(m_image);
m_image = NULL;
}
if (m_cursor1 != NULL)
{
SDL_FreeSurface(m_cursor1);
m_cursor1 = NULL;
}
if (m_cursor2 != NULL)
{
SDL_FreeSurface(m_cursor2);
m_cursor2 = NULL;
}
for (std::vector<SDL_Surface *>::iterator l_it = m_linesImg.begin(); l_it != m_linesImg.end(); ++l_it)
{
if (*l_it != NULL)
{
SDL_FreeSurface(*l_it);
*l_it = NULL;
}
}
}
void CDialog::addLabel(const std::string &p_label)
{
m_lines.push_back(p_label);
++m_nbLabels;
}
void CDialog::addOption(const std::string &p_option)
{
m_lines.push_back(p_option);
++m_nbOptions;
}
void CDialog::init(void)
{
// The width of the window depends on the width of the largest line
int l_width(0);
int l_cursorWidth(0);
SDL_Surface *l_surfaceTmp(NULL);
// Render every line
for (std::vector<std::string>::const_iterator l_it = m_lines.begin(); l_it != m_lines.end(); ++l_it)
{
// Render line
l_surfaceTmp = SDL_utils::renderText(m_font, *l_it, (m_nbTitle && l_it == m_lines.begin()) ? Globals::g_colorTextTitle : Globals::g_colorTextNormal);
if (l_surfaceTmp->w > l_width)
l_width = l_surfaceTmp->w;
m_linesImg.push_back(l_surfaceTmp);
}
// Cursor width
l_cursorWidth = l_width + 2 * DIALOG_MARGIN;
if (l_cursorWidth > SCREEN_WIDTH - 2 * DIALOG_BORDER)
l_cursorWidth = SCREEN_WIDTH - 2 * DIALOG_BORDER;
// Line clip
m_clip.h = m_linesImg.front()->h;
m_clip.w = l_cursorWidth - DIALOG_MARGIN - 1;
// Adjust image width
l_width = l_width + 2 * DIALOG_MARGIN + 2 * DIALOG_BORDER;
if (l_width > SCREEN_WIDTH)
l_width = SCREEN_WIDTH;
// Create dialog image
m_image = SDL_utils::createImage(l_width, m_linesImg.size() * LINE_HEIGHT + 2 * DIALOG_BORDER, SDL_MapRGB(Globals::g_screen->format, COLOR_BORDER));
{
SDL_Rect l_rect;
l_rect.x = DIALOG_BORDER;
l_rect.y = DIALOG_BORDER + m_nbTitle * LINE_HEIGHT;
l_rect.w = m_image->w - 2 * DIALOG_BORDER;
l_rect.h = m_image->h - 2 * DIALOG_BORDER - m_nbTitle * LINE_HEIGHT;
SDL_FillRect(m_image, &l_rect, SDL_MapRGB(m_image->format, COLOR_BG_1));
}
// Create cursor image
m_cursor1 = SDL_utils::createImage(l_cursorWidth, LINE_HEIGHT, SDL_MapRGB(Globals::g_screen->format, COLOR_CURSOR_1));
m_cursor2 = SDL_utils::createImage(l_cursorWidth, LINE_HEIGHT, SDL_MapRGB(Globals::g_screen->format, COLOR_CURSOR_2));
// Adjust dialog coordinates
if (!m_x)
m_x = (SCREEN_WIDTH - m_image->w) >> 1;
if (!m_y)
{
m_y = (SCREEN_HEIGHT - m_image->h) >> 1;
}
else
{
m_y = m_y - (m_image->h >> 1) + (LINE_HEIGHT >> 1);
if (m_y < Y_LIST)
m_y = Y_LIST;
if (m_y + m_image->h > Y_FOOTER + 1)
m_y = Y_FOOTER + 1 - m_image->h;
}
// Cursor coordinates
m_cursorX = m_x + DIALOG_BORDER;
m_cursorY = m_y + DIALOG_BORDER + (m_nbTitle + m_nbLabels) * LINE_HEIGHT;
}
void CDialog::render(const bool p_focus) const
{
INHIBIT(std::cout << "CDialog::render fullscreen: " << isFullScreen() << " focus: " << p_focus << std::endl;)
// Draw background
SDL_utils::applySurface(m_x, m_y, m_image, Globals::g_screen);
// Draw cursor
SDL_utils::applySurface(m_cursorX, m_cursorY + m_highlightedLine * LINE_HEIGHT, p_focus ? m_cursor1 : m_cursor2, Globals::g_screen);
// Draw lines text
Sint16 l_y(m_y + 4);
for (std::vector<SDL_Surface *>::const_iterator l_it = m_linesImg.begin(); l_it != m_linesImg.end(); ++l_it)
{
SDL_utils::applySurface(m_cursorX + DIALOG_MARGIN, (m_nbTitle && l_it == m_linesImg.begin()) ? l_y - 1 - Y_OFFSET : l_y - Y_OFFSET, *l_it, Globals::g_screen, &m_clip);
l_y += LINE_HEIGHT;
}
}
const bool CDialog::keyPress(const SDL_Event &p_event)
{
CWindow::keyPress(p_event);
bool l_ret(false);
switch (p_event.key.keysym.sym)
{
case MYKEY_PARENT:
m_retVal = -1;
l_ret = true;
break;
case MYKEY_UP:
l_ret = moveCursorUp(true);
break;
case MYKEY_DOWN:
l_ret = moveCursorDown(true);
break;
case MYKEY_PAGEUP:
if (m_highlightedLine)
{
m_highlightedLine = 0;
l_ret = true;
}
break;
case MYKEY_PAGEDOWN:
if (m_highlightedLine + 1 < m_nbOptions)
{
m_highlightedLine = m_nbOptions - 1;
l_ret = true;
}
break;
case MYKEY_OPEN:
m_retVal = m_highlightedLine + 1;
l_ret = true;
break;
default:
break;
}
return l_ret;
}
const bool CDialog::moveCursorUp(const bool p_loop)
{
bool l_ret(false);
if (m_highlightedLine)
{
--m_highlightedLine;
l_ret = true;
}
else if (p_loop && m_highlightedLine + 1 < m_nbOptions)
{
m_highlightedLine = m_nbOptions - 1;
l_ret = true;
}
return l_ret;
}
const bool CDialog::moveCursorDown(const bool p_loop)
{
bool l_ret(false);
if (m_highlightedLine + 1 < m_nbOptions)
{
++m_highlightedLine;
l_ret = true;
}
else if (p_loop && m_highlightedLine)
{
m_highlightedLine = 0;
l_ret = true;
}
return l_ret;
}
const bool CDialog::keyHold(void)
{
bool l_ret(false);
switch(m_lastPressed)
{
case MYKEY_UP:
if (tick(SDL_GetKeyState(NULL)[MYKEY_UP]))
l_ret = moveCursorUp(false);
break;
case MYKEY_DOWN:
if (tick(SDL_GetKeyState(NULL)[MYKEY_DOWN]))
l_ret = moveCursorDown(false);
break;
default:
break;
}
return l_ret;
}
const Sint16 &CDialog::getX(void) const
{
return m_x;
}
const Sint16 &CDialog::getY(void) const
{
return m_y;
}
const SDL_Surface * const CDialog::getImage(void) const
{
return m_image;
}
const unsigned int &CDialog::getHighlightedIndex(void) const
{
return m_highlightedLine;
}

View file

@ -0,0 +1,89 @@
#ifndef _DIALOG_H_
#define _DIALOG_H_
#include <string>
#include <vector>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include "window.h"
class CDialog : public CWindow
{
public:
// Constructor
// Coordinates = 0 => centered
CDialog(const std::string &p_title, const Sint16 p_x, const Sint16 p_y);
// Destructor
virtual ~CDialog(void);
// Add a label
void addLabel(const std::string &p_label);
// Add a menu option
void addOption(const std::string &p_option);
// Init. Call after all options are added.
void init(void);
// Accessors
const Sint16 &getX(void) const;
const Sint16 &getY(void) const;
const SDL_Surface * const getImage(void) const;
const unsigned int &getHighlightedIndex(void) const;
private:
// Forbidden
CDialog(void);
CDialog(const CDialog &p_source);
const CDialog &operator =(const CDialog &p_source);
// Key press management
virtual const bool keyPress(const SDL_Event &p_event);
// Key hold management
virtual const bool keyHold(void);
// Draw
virtual void render(const bool p_focus) const;
// Move cursor
const bool moveCursorUp(const bool p_loop);
const bool moveCursorDown(const bool p_loop);
// Number of titles (0 or 1), labels, and options
bool m_nbTitle;
unsigned char m_nbLabels;
unsigned char m_nbOptions;
// List of lines
std::vector<std::string> m_lines;
std::vector<SDL_Surface *> m_linesImg;
// The highlighted item
unsigned int m_highlightedLine;
// The image representing the dialog
SDL_Surface *m_image;
// The cursor
SDL_Surface *m_cursor1;
SDL_Surface *m_cursor2;
// Coordinates
Sint16 m_x;
Sint16 m_y;
Sint16 m_cursorX;
Sint16 m_cursorY;
// Line clip
mutable SDL_Rect m_clip;
// Pointers to resources
TTF_Font *m_font;
TTF_Font *m_font_sm;
};
#endif

View file

@ -0,0 +1,115 @@
#include <iostream>
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
#include <string.h>
#include "fileLister.h"
bool compareNoCase(const T_FILE& p_s1, const T_FILE& p_s2)
{
return strcasecmp(p_s1.m_name.c_str(), p_s2.m_name.c_str()) <= 0;
}
CFileLister::CFileLister(void)
{
}
CFileLister::~CFileLister(void)
{
}
const bool CFileLister::list(const std::string &p_path)
{
// Open dir
DIR *l_dir = opendir(p_path.c_str());
if (l_dir == NULL)
{
std::cerr << "CFileLister::list: Error opening dir " << p_path << std::endl;
return false;
}
// Clean up
m_listFiles.clear();
m_listDirs.clear();
// Read dir
std::string l_file("");
std::string l_fileFull("");
struct stat l_stat;
struct dirent *l_dirent = readdir(l_dir);
while (l_dirent != NULL)
{
l_file = l_dirent->d_name;
// Filter the '.' and '..' dirs
if (l_file != "." && l_file != "..")
{
// Stat the file
l_fileFull = p_path + "/" + l_file;
if (stat(l_fileFull.c_str(), &l_stat) == -1)
{
std::cerr << "CFileLister::list: Error stat " << l_fileFull << std::endl;
}
else
{
// Check type
if (S_ISDIR(l_stat.st_mode))
// It's a directory
m_listDirs.push_back(T_FILE(l_file, l_stat.st_size));
else
// It's a file
m_listFiles.push_back(T_FILE(l_file, l_stat.st_size));
}
}
// Next
l_dirent = readdir(l_dir);
}
// Close dir
closedir(l_dir);
// Sort lists
sort(m_listFiles.begin(), m_listFiles.end(), compareNoCase);
sort(m_listDirs.begin(), m_listDirs.end(), compareNoCase);
// Add "..", always at the first place
m_listDirs.insert(m_listDirs.begin(), T_FILE("..", 0));
return true;
}
const T_FILE &CFileLister::operator[](const unsigned int p_i) const
{
if (p_i < m_listDirs.size())
return m_listDirs[p_i];
else
return m_listFiles[p_i - m_listDirs.size()];
}
const unsigned int CFileLister::getNbDirs(void) const
{
return m_listDirs.size();
}
const unsigned int CFileLister::getNbFiles(void) const
{
return m_listFiles.size();
}
const unsigned int CFileLister::getNbTotal(void) const
{
return m_listDirs.size() + m_listFiles.size();
}
const bool CFileLister::isDirectory(const unsigned int p_i) const
{
return p_i < m_listDirs.size();
}
const unsigned int CFileLister::searchDir(const std::string &p_name) const
{
unsigned int l_ret = 0;
bool l_found = false;
// Search name in dirs
for (std::vector<T_FILE>::const_iterator l_it = m_listDirs.begin(); (!l_found) && (l_it != m_listDirs.end()); ++l_it)
{
if ((*l_it).m_name == p_name)
l_found = true;
else
++l_ret;
}
return l_found ? l_ret : 0;
}

View file

@ -0,0 +1,58 @@
#ifndef _FILE_LISTER_H_
#define _FILE_LISTER_H_
#include <vector>
#include <string>
// Class used to store file info
struct T_FILE
{
T_FILE(void): m_name(""), m_size(0) {}
T_FILE(const std::string &p_name, const unsigned long int &p_size): m_name(p_name), m_size(p_size) {}
T_FILE(const T_FILE &p_source): m_name(p_source.m_name), m_size(p_source.m_size) {}
~T_FILE(void) {}
const T_FILE &operator =(const T_FILE &p_source) { m_name = p_source.m_name; m_size = p_source.m_size; return *this; }
std::string m_name;
unsigned long int m_size;
};
class CFileLister
{
public:
// Constructor
CFileLister(void);
// Destructor
virtual ~CFileLister(void);
// Read the contents of the given path
// Returns false if the path does not exist
const bool list(const std::string &p_path);
// Get an element in the list (dirs and files combined)
const T_FILE &operator[](const unsigned int p_i) const;
// Get the number of dirs/files
const unsigned int getNbDirs(void) const;
const unsigned int getNbFiles(void) const;
const unsigned int getNbTotal(void) const;
// True => directory, false => file
const bool isDirectory(const unsigned int p_i) const;
// Get index of the given dir name, 0 if not found
const unsigned int searchDir(const std::string &p_name) const;
private:
// Forbidden
CFileLister(const CFileLister &p_source);
const CFileLister &operator =(const CFileLister &p_source);
// The list of files/dir
std::vector<T_FILE> m_listDirs;
std::vector<T_FILE> m_listFiles;
};
#endif

View file

@ -0,0 +1,356 @@
#include <sys/stat.h>
#include <unistd.h>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <sstream>
#include "fileutils.h"
#include "def.h"
#include "dialog.h"
#include "sdlutils.h"
#define SPECIAL_CHARS "\\`$();|{}&'\"*?<>[]!^~-#\n\r "
void File_utils::copyFile(const std::vector<std::string> &p_src, const std::string &p_dest)
{
std::string l_command("");
std::string l_destFile("");
std::string l_fileName("");
bool l_loop(true);
bool l_confirm(true);
bool l_execute(true);
for (std::vector<std::string>::const_iterator l_it = p_src.begin(); l_loop && (l_it != p_src.end()); ++l_it)
{
l_execute = true;
// Check if destination files already exists
if (l_confirm)
{
l_fileName = getFileName(*l_it);
l_destFile = p_dest + (p_dest.at(p_dest.size() - 1) == '/' ? "" : "/") + l_fileName;
if (fileExists(l_destFile))
{
INHIBIT(std::cout << "File " << l_destFile << " already exists => ask for confirmation" << std::endl;)
CDialog l_dialog("Question:", 0, 0);
l_dialog.addLabel("Overwrite " + l_fileName + "?");
l_dialog.addOption("Yes");
l_dialog.addOption("Yes to all");
l_dialog.addOption("No");
l_dialog.addOption("Cancel");
l_dialog.init();
switch (l_dialog.execute())
{
case 1:
// Yes
break;
case 2:
// Yes to all
l_confirm = false;
break;
case 3:
// No
l_execute = false;
break;
default:
// Cancel
l_execute = false;
l_loop = false;
break;
}
}
}
if (l_execute)
{
// Waiting message
SDL_utils::pleaseWait();
l_command = "\\cp -r " + specialChars(*l_it) + " " + specialChars(p_dest);
INHIBIT(std::cout << "Command: " << l_command << std::endl;)
system(l_command.c_str());
}
}
}
void File_utils::moveFile(const std::vector<std::string> &p_src, const std::string &p_dest)
{
std::string l_command("");
std::string l_destFile("");
std::string l_fileName("");
bool l_loop(true);
bool l_confirm(true);
bool l_execute(true);
for (std::vector<std::string>::const_iterator l_it = p_src.begin(); l_loop && (l_it != p_src.end()); ++l_it)
{
l_execute = true;
// Check if destination files already exists
if (l_confirm)
{
l_fileName = getFileName(*l_it);
l_destFile = p_dest + (p_dest.at(p_dest.size() - 1) == '/' ? "" : "/") + l_fileName;
if (fileExists(l_destFile))
{
INHIBIT(std::cout << "File " << l_destFile << " already exists => ask for confirmation" << std::endl;)
CDialog l_dialog("Question:", 0, 0);
l_dialog.addLabel("Overwrite " + l_fileName + "?");
l_dialog.addOption("Yes");
l_dialog.addOption("Yes to all");
l_dialog.addOption("No");
l_dialog.addOption("Cancel");
l_dialog.init();
switch (l_dialog.execute())
{
case 1:
// Yes
break;
case 2:
// Yes to all
l_confirm = false;
break;
case 3:
// No
l_execute = false;
break;
default:
// Cancel
l_execute = false;
l_loop = false;
break;
}
}
}
if (l_execute)
{
// Waiting message
SDL_utils::pleaseWait();
l_command = "\\mv " + specialChars(*l_it) + " " + specialChars(p_dest);
INHIBIT(std::cout << "Command: " << l_command << std::endl;)
system(l_command.c_str());
}
}
}
void File_utils::renameFile(const std::string &p_file1, const std::string &p_file2)
{
bool l_execute(true);
// Check if destination files already exists
if (fileExists(p_file2))
{
INHIBIT(std::cout << "File " << p_file2 << " already exists => ask for confirmation" << std::endl;)
CDialog l_dialog("Question:", 0, 0);
l_dialog.addLabel("Overwrite " + getFileName(p_file2) + "?");
l_dialog.addOption("Yes");
l_dialog.addOption("No");
l_dialog.init();
if (l_dialog.execute() != 1)
l_execute = false;
}
if (l_execute)
{
std::string l_command = "\\mv " + specialChars(p_file1) + " " + specialChars(p_file2);
INHIBIT(std::cout << "Command: " << l_command << std::endl;)
system(l_command.c_str());
}
}
void File_utils::removeFile(const std::vector<std::string> &p_files)
{
std::string l_command("");
for (std::vector<std::string>::const_iterator l_it = p_files.begin(); l_it != p_files.end(); ++l_it)
{
l_command = "\\rm -rf " + specialChars(*l_it);
INHIBIT(std::cout << "Command: " << l_command << std::endl;)
system(l_command.c_str());
}
}
void File_utils::makeDirectory(const std::string &p_file)
{
std::string l_command = "\\mkdir -p " + specialChars(p_file);
INHIBIT(std::cout << "Command: " << l_command << std::endl;)
system(l_command.c_str());
}
const bool File_utils::fileExists(const std::string &p_path)
{
struct stat l_stat;
return stat(p_path.c_str(), &l_stat) == 0;
}
const std::string File_utils::getFileName(const std::string &p_path)
{
size_t l_pos = p_path.rfind('/');
return p_path.substr(l_pos + 1);
}
const std::string File_utils::getPath(const std::string &p_path)
{
size_t l_pos = p_path.rfind('/');
return p_path.substr(0, l_pos);
}
void File_utils::executeFile(const std::string &p_file)
{
// Command
std::string l_command = "./" + specialChars(getFileName(p_file));
INHIBIT(std::cout << "File_utils::executeFile: " << l_command << " in " << getPath(p_file) << std::endl;)
// CD to the file's location
chdir(getPath(p_file).c_str());
// Quit
SDL_utils::hastalavista();
// Execute file
execlp("/bin/sh", "/bin/sh", "-c", l_command.c_str(), NULL);
// If we're here, there was an error with the execution
std::cerr << "Error executing file " << p_file << std::endl;
// Relaunch DinguxCommander
l_command = "./" + specialChars(getSelfExecutionName());
INHIBIT(std::cout << "File_utils::executeFile: " << l_command << " in " << getSelfExecutionPath() << std::endl;)
chdir(getSelfExecutionPath().c_str());
execlp(l_command.c_str(), l_command.c_str(), NULL);
}
const std::string File_utils::getSelfExecutionPath(void)
{
// Get execution path
std::string l_exePath("");
char l_buff[255];
int l_i = readlink("/proc/self/exe", l_buff, 255);
l_exePath = l_buff;
l_exePath = l_exePath.substr(0, l_i);
l_i = l_exePath.rfind("/");
l_exePath = l_exePath.substr(0, l_i);
return l_exePath;
}
const std::string File_utils::getSelfExecutionName(void)
{
// Get execution path
std::string l_exePath("");
char l_buff[255];
int l_i = readlink("/proc/self/exe", l_buff, 255);
l_exePath = l_buff;
l_exePath = l_exePath.substr(0, l_i);
l_i = l_exePath.rfind("/");
l_exePath = l_exePath.substr(l_i + 1);
return l_exePath;
}
void File_utils::stringReplace(std::string &p_string, const std::string &p_search, const std::string &p_replace)
{
// Replace all occurrences of p_search by p_replace in p_string
size_t l_pos = p_string.find(p_search, 0);
while (l_pos != std::string::npos)
{
p_string.replace(l_pos, p_search.length(), p_replace);
l_pos = p_string.find(p_search, l_pos + p_replace.length());
}
}
const std::string File_utils::specialChars(const std::string &p_string)
{
// Insert a '\' before special characters
std::string l_ret(p_string);
const std::string l_specialChars(SPECIAL_CHARS);
const size_t l_length = l_specialChars.size();
std::string l_char("");
for (unsigned int l_i = 0; l_i < l_length; ++l_i)
{
l_char = l_specialChars.substr(l_i, 1);
stringReplace(l_ret, l_char, "\\" + l_char);
}
return l_ret;
}
const unsigned long int File_utils::getFileSize(const std::string &p_file)
{
unsigned long int l_ret(0);
struct stat l_stat;
if (stat(p_file.c_str(), &l_stat) == -1)
std::cerr << "File_utils::getFileSize: Error stat " << p_file << std::endl;
else
l_ret = l_stat.st_size;
return l_ret;
}
void File_utils::diskInfo(void)
{
std::string l_line("");
// Execute command df -h
{
char l_buffer[256];
FILE *l_pipe = popen("df -h", "r");
if (l_pipe == NULL)
{
std::cerr << "File_utils::diskInfo: Error popen" << std::endl;
return;
}
while (l_line.empty() && fgets(l_buffer, sizeof(l_buffer), l_pipe) != NULL)
if (strstr(l_buffer, FILE_SYSTEM) != NULL)
l_line = l_buffer;
pclose(l_pipe);
}
if (!l_line.empty())
{
// Separate line by spaces
std::istringstream l_iss(l_line);
std::vector<std::string> l_tokens;
copy(std::istream_iterator<std::string>(l_iss), std::istream_iterator<std::string>(), std::back_inserter<std::vector<std::string> >(l_tokens));
// Display dialog
CDialog l_dialog("Disk information:", 0, 0);
l_dialog.addLabel("Size: " + l_tokens[1]);
l_dialog.addLabel("Used: " + l_tokens[2] + " (" + l_tokens[4] + ")");
l_dialog.addLabel("Available: " + l_tokens[3]);
l_dialog.addOption("OK");
l_dialog.init();
l_dialog.execute();
}
else
std::cerr << "File_utils::diskInfo: Unable to find " << FILE_SYSTEM << std::endl;
}
void File_utils::diskUsed(const std::vector<std::string> &p_files)
{
std::string l_line("");
// Waiting message
SDL_utils::pleaseWait();
// Build and execute command
{
std::string l_command("du -csh");
for (std::vector<std::string>::const_iterator l_it = p_files.begin(); l_it != p_files.end(); ++l_it)
l_command = l_command + " \"" + *l_it + "\"";
char l_buffer[256];
FILE *l_pipe = popen(l_command.c_str(), "r");
if (l_pipe == NULL)
{
std::cerr << "File_utils::diskUsed: Error popen" << std::endl;
return;
}
while (fgets(l_buffer, sizeof(l_buffer), l_pipe) != NULL);
l_line = l_buffer;
pclose(l_pipe);
}
// Separate line by spaces
{
std::istringstream l_iss(l_line);
std::vector<std::string> l_tokens;
copy(std::istream_iterator<std::string>(l_iss), std::istream_iterator<std::string>(), std::back_inserter<std::vector<std::string> >(l_tokens));
l_line = l_tokens[0];
}
// Dialog
std::ostringstream l_stream;
CDialog l_dialog("Disk used:", 0, 0);
l_stream << p_files.size() << " items selected";
l_dialog.addLabel(l_stream.str());
l_dialog.addLabel("Disk used: " + l_line);
l_dialog.addOption("OK");
l_dialog.init();
l_dialog.execute();
}
void File_utils::formatSize(std::string &p_size)
{
// Format 123456789 to 123,456,789
int l_i = p_size.size() - 3;
while (l_i > 0)
{
p_size.insert(l_i, ",");
l_i -= 3;
}
}

View file

@ -0,0 +1,50 @@
#ifndef _FILEUTILS_H_
#define _FILEUTILS_H_
#include <string>
#include <vector>
namespace File_utils
{
// File operations
void copyFile(const std::vector<std::string> &p_src, const std::string &p_dest);
void moveFile(const std::vector<std::string> &p_src, const std::string &p_dest);
void removeFile(const std::vector<std::string> &p_files);
void executeFile(const std::string &p_file);
void makeDirectory(const std::string &p_file);
void renameFile(const std::string &p_file1, const std::string &p_file2);
// File utilities
const bool fileExists(const std::string &p_path);
const unsigned long int getFileSize(const std::string &p_file);
void formatSize(std::string &p_size);
const std::string getFileName(const std::string &p_path);
const std::string getPath(const std::string &p_path);
const std::string getSelfExecutionPath(void);
const std::string getSelfExecutionName(void);
void stringReplace(std::string &p_string, const std::string &p_search, const std::string &p_replace);
const std::string specialChars(const std::string &p_string);
// Dialogs
void diskInfo(void);
void diskUsed(const std::vector<std::string> &p_files);
}
#endif

View file

@ -0,0 +1,483 @@
#include <iostream>
#include "keyboard.h"
#include "sdlutils.h"
#include "resourceManager.h"
#include "def.h"
#define KB_X 28
#define KB_Y 128
#define FIELD_Y 98
#define FIELD_W 258
CKeyboard::CKeyboard(const std::string &p_inputText):
CWindow(),
m_imageKeyboard(NULL),
m_textField(NULL),
m_inputText(p_inputText),
m_selected(0),
m_footer(NULL),
m_keySet(0),
m_font(CResourceManager::instance().getFont()),
m_font_sm(CResourceManager::instance().getSmallFont())
{
// Key sets
m_keySets[0] = "abcdefghijklmnopqrstuvwxyz0123456789., ";
m_keySets[1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789., ";
m_keySets[2] = "!#$%&'()[]{}+-;=@^_`´~¡¿¢£¤¥¦§«»±×÷©®° ";
m_keySets[3] = "áàäâãåéèëêæœçíìïîóòöôõðøñþúùüûýÿøßµ¹²³ ";
m_keySets[4] = "ÁÀÄÂÃÅÉÈËÊÆŒÇÍÌÏÎÓÒÖÔÕÐØÑÞÚÙÜÛÝŸØßµ¼½¾ ";
// Create keyboard image
{
SDL_Rect l_rect;
// Create keyboard image
m_imageKeyboard = SDL_utils::createImage(265, 84, SDL_MapRGB(Globals::g_screen->format, COLOR_BORDER));
l_rect.x = 2;
l_rect.y = 2;
l_rect.w = 261;
l_rect.h = 80;
SDL_FillRect(m_imageKeyboard, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BG_2));
// Keys
for (unsigned int l_y = 0; l_y < 3; ++l_y)
{
for (unsigned int l_x = 0; l_x < 13; ++l_x)
{
l_rect.x = 3 + 20 * l_x;
l_rect.y = 3 + 20 * l_y;
l_rect.w = 19;
l_rect.h = 18;
SDL_FillRect(m_imageKeyboard, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BORDER));
++l_rect.x;
++l_rect.y;
l_rect.w -= 2;
l_rect.h -= 2;
SDL_FillRect(m_imageKeyboard, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BG_1));
}
}
// Buttons Cancel and OK
l_rect.x = 3;
l_rect.y = 63;
l_rect.w = 129;
l_rect.h = 18;
SDL_FillRect(m_imageKeyboard, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BORDER));
l_rect.x = 133;
SDL_FillRect(m_imageKeyboard, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BORDER));
l_rect.w -= 2;
l_rect.h -= 2;
++l_rect.y;
l_rect.x = 4;
SDL_FillRect(m_imageKeyboard, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BG_1));
l_rect.x = 134;
SDL_FillRect(m_imageKeyboard, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BG_1));
// Create text field image
m_textField = SDL_utils::createImage(265, 19, SDL_MapRGB(Globals::g_screen->format, COLOR_BORDER));
l_rect.x = 2;
l_rect.y = 2;
l_rect.w = 261;
l_rect.h = 15;
SDL_FillRect(m_textField, &l_rect, SDL_MapRGB(m_imageKeyboard->format, COLOR_BG_1));
}
// Create footer
m_footer = SDL_utils::createImage(SCREEN_WIDTH, H_FOOTER, SDL_MapRGB(Globals::g_screen->format, COLOR_BORDER));
SDL_utils::applyText(SCREEN_WIDTH >> 1, 1, m_footer, m_font_sm, "A-Input B-Cancel START-OK L/R-Change Y-Backspace X-Space", Globals::g_colorTextTitle, SDL_utils::T_TEXT_ALIGN_CENTER);
}
CKeyboard::~CKeyboard(void)
{
// Free surfaces
if (m_imageKeyboard != NULL)
{
SDL_FreeSurface(m_imageKeyboard);
m_imageKeyboard = NULL;
}
if (m_textField != NULL)
{
SDL_FreeSurface(m_textField);
m_textField = NULL;
}
if (m_footer != NULL)
{
SDL_FreeSurface(m_footer);
m_footer = NULL;
}
}
void CKeyboard::render(const bool p_focus) const
{
INHIBIT(std::cout << "CKeyboard::render fullscreen: " << isFullScreen() << " focus: " << p_focus << std::endl;)
// Draw input text field
SDL_utils::applySurface(KB_X, FIELD_Y, m_textField, Globals::g_screen);
// Input text
if (!m_inputText.empty())
{
SDL_Surface *l_surfaceTmp = SDL_utils::renderText(m_font, m_inputText, Globals::g_colorTextNormal);
if (l_surfaceTmp->w > FIELD_W)
{
// Text is too big => clip it
SDL_Rect l_rect;
l_rect.x = l_surfaceTmp->w - FIELD_W;
l_rect.y = 0;
l_rect.w = FIELD_W;
l_rect.h = l_surfaceTmp->h;
SDL_utils::applySurface(KB_X + 5, FIELD_Y + 4 - Y_OFFSET, l_surfaceTmp, Globals::g_screen, &l_rect);
}
else
SDL_utils::applySurface(KB_X + 5, FIELD_Y + 4 - Y_OFFSET, l_surfaceTmp, Globals::g_screen);
}
// Draw keyboard
SDL_utils::applySurface(KB_X, KB_Y, m_imageKeyboard, Globals::g_screen);
// Cursor
{
SDL_Rect l_rect;
if (m_selected < 39)
{
// A letter is selected
l_rect.w = 17;
l_rect.h = 16;
l_rect.x = KB_X + 4;
if (m_selected >= 26)
{
l_rect.x = KB_X + 4 + (m_selected - 26) * 20;
l_rect.y = KB_Y + 44;
}
else if (m_selected >= 13)
{
l_rect.x = KB_X + 4 + (m_selected - 13) * 20;
l_rect.y = KB_Y + 24;
}
else
{
l_rect.x = KB_X + 4 + m_selected * 20;
l_rect.y = KB_Y + 4;
}
}
else
{
l_rect.w = 127;
l_rect.h = 16;
l_rect.x = KB_X + 4 + (m_selected == 40) * 130;
l_rect.y = KB_Y + 64;
}
SDL_FillRect(Globals::g_screen, &l_rect, SDL_MapRGB(Globals::g_screen->format, COLOR_CURSOR_1));
}
// Draw keys text
{
unsigned int l_i(0);
unsigned int l_x(0);
unsigned int l_y(0);
std::string l_text("");
for (l_y = 0; l_y < 3; ++l_y)
{
for (l_x = 0; l_x < 13; ++l_x)
{
if (utf8Code(m_keySets[m_keySet].at(l_i)))
{
l_text = m_keySets[m_keySet].substr(l_i, 2);
l_i += 2;
}
else
{
l_text = m_keySets[m_keySet].substr(l_i, 1);
l_i += 1;
}
SDL_utils::applyText(KB_X + 20 * l_x + 13, KB_Y + 7 + 20 * l_y - Y_OFFSET, Globals::g_screen, m_font, l_text, Globals::g_colorTextNormal, SDL_utils::T_TEXT_ALIGN_CENTER);
}
}
}
// Buttons text
SDL_utils::applyText(KB_X + 67, KB_Y + 67 - Y_OFFSET, Globals::g_screen, m_font, "Cancel", Globals::g_colorTextNormal, SDL_utils::T_TEXT_ALIGN_CENTER);
SDL_utils::applyText(KB_X + 197, KB_Y + 67 - Y_OFFSET, Globals::g_screen, m_font, "OK", Globals::g_colorTextNormal, SDL_utils::T_TEXT_ALIGN_CENTER);
// Draw footer
SDL_utils::applySurface(0, 227, m_footer, Globals::g_screen);
}
const bool CKeyboard::keyPress(const SDL_Event &p_event)
{
CWindow::keyPress(p_event);
bool l_ret(false);
switch (p_event.key.keysym.sym)
{
case MYKEY_PARENT:
// B => Cancel
m_retVal = -1;
l_ret = true;
break;
case MYKEY_UP:
l_ret = moveCursorUp(true);
break;
case MYKEY_DOWN:
l_ret = moveCursorDown(true);
break;
case MYKEY_LEFT:
l_ret = moveCursorLeft(true);
break;
case MYKEY_RIGHT:
l_ret = moveCursorRight(true);
break;
case MYKEY_SYSTEM:
// Y => Backspace
l_ret = backspace();
break;
case MYKEY_OPERATION:
// X => Space
l_ret = type(" ");
break;
case MYKEY_OPEN:
// A => Button pressed
if (m_selected == 39)
{
// Button Cancel
m_retVal = -1;
l_ret = true;
}
else if (m_selected == 40)
{
// Button OK
m_retVal = 1;
l_ret = true;
}
else
// A letter button
l_ret = type();
break;
case MYKEY_PAGEDOWN:
// R => Change keys forward
m_keySet = (m_keySet + 1) % NB_KEY_SETS;
l_ret = true;
break;
case MYKEY_PAGEUP:
// L => Change keys backward
m_keySet = m_keySet ? m_keySet - 1 : NB_KEY_SETS - 1;
l_ret = true;
break;
case MYKEY_TRANSFER:
// START => OK
m_retVal = 1;
l_ret = true;
default:
break;
}
return l_ret;
}
const bool CKeyboard::keyHold(void)
{
bool l_ret(false);
switch(m_lastPressed)
{
case MYKEY_UP:
if (tick(SDL_GetKeyState(NULL)[MYKEY_UP]))
l_ret = moveCursorUp(false);
break;
case MYKEY_DOWN:
if (tick(SDL_GetKeyState(NULL)[MYKEY_DOWN]))
l_ret = moveCursorDown(false);
break;
case MYKEY_LEFT:
if (tick(SDL_GetKeyState(NULL)[MYKEY_LEFT]))
l_ret = moveCursorLeft(false);
break;
case MYKEY_RIGHT:
if (tick(SDL_GetKeyState(NULL)[MYKEY_RIGHT]))
l_ret = moveCursorRight(false);
break;
case MYKEY_OPEN:
// A => Add letter
if (tick(SDL_GetKeyState(NULL)[MYKEY_OPEN]))
l_ret = type();
break;
case MYKEY_SYSTEM:
// Y => Backspace
if (tick(SDL_GetKeyState(NULL)[MYKEY_SYSTEM]))
l_ret = backspace();
break;
case MYKEY_OPERATION:
// X => Space
if (tick(SDL_GetKeyState(NULL)[MYKEY_OPERATION]))
l_ret = type(" ");
break;
default:
break;
}
return l_ret;
}
const bool CKeyboard::moveCursorUp(const bool p_loop)
{
bool l_ret(false);
if (m_selected == 39)
{
m_selected = 29;
l_ret = true;
}
else if (m_selected == 40)
{
m_selected = 35;
l_ret = true;
}
else if (m_selected >= 13)
{
m_selected -= 13;
l_ret = true;
}
else if (m_selected >= 6)
{
if (p_loop)
{
m_selected = 40;
l_ret = true;
}
}
else
{
if (p_loop)
{
m_selected = 39;
l_ret = true;
}
}
return l_ret;
}
const bool CKeyboard::moveCursorDown(const bool p_loop)
{
bool l_ret(false);
if (m_selected == 39)
{
if (p_loop)
{
m_selected = 3;
l_ret = true;
}
}
else if (m_selected == 40)
{
if (p_loop)
{
m_selected = 9;
l_ret = true;
}
}
else if (m_selected >= 32)
{
m_selected = 40;
l_ret = true;
}
else if (m_selected >= 26)
{
m_selected = 39;
l_ret = true;
}
else
{
m_selected += 13;
l_ret = true;
}
return l_ret;
}
const bool CKeyboard::moveCursorLeft(const bool p_loop)
{
bool l_ret(false);
switch (m_selected)
{
case 0:
case 13:
case 26:
if (p_loop)
{
m_selected += 12;
l_ret = true;
}
break;
case 39:
if (p_loop)
{
m_selected = 40;
l_ret = true;
}
break;
default:
--m_selected;
l_ret = true;
break;
}
return l_ret;
}
const bool CKeyboard::moveCursorRight(const bool p_loop)
{
bool l_ret(false);
switch (m_selected)
{
case 12:
case 25:
case 38:
if (p_loop)
{
m_selected -= 12;
l_ret = true;
}
break;
case 40:
if (p_loop)
{
m_selected = 39;
l_ret = true;
}
break;
default:
++m_selected;
l_ret = true;
break;
}
return l_ret;
}
const bool CKeyboard::type(const std::string &p_text)
{
if (p_text.empty())
{
// Append selected character to the input text
if (m_selected < 39)
{
// Get real index
unsigned char l_index(0);
for (unsigned char l_c = 0; l_c < m_selected; ++l_c)
l_index += 1 + utf8Code(m_keySets[m_keySet].at(l_index));
// Get size
size_t l_size = 1 + utf8Code(m_keySets[m_keySet].at(l_index));
// Append text
m_inputText += m_keySets[m_keySet].substr(l_index, l_size);
}
else
std::cerr << "CKeyboard::type : unexpected value: " << m_selected << std::endl;
}
else
// Append given text
m_inputText += p_text;
return true;
}
const std::string &CKeyboard::getInputText(void) const
{
return m_inputText;
}
const bool CKeyboard::backspace(void)
{
bool l_ret(false);
if (!m_inputText.empty())
{
if (m_inputText.size() >= 2 && utf8Code(m_inputText.at(m_inputText.size() - 2)))
m_inputText.resize(m_inputText.size() - 2);
else
m_inputText.resize(m_inputText.size() - 1);
l_ret = true;
}
return l_ret;
}
const bool CKeyboard::utf8Code(const unsigned char p_c) const
{
return (p_c >= 194 && p_c <= 198) || p_c == 208 || p_c == 209;
}

View file

@ -0,0 +1,79 @@
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
#include <string>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include "window.h"
#define NB_KEY_SETS 5
class CKeyboard : public CWindow
{
public:
// Constructor
CKeyboard(const std::string &p_inputText);
// Destructor
virtual ~CKeyboard(void);
// Get input text
const std::string &getInputText(void) const;
private:
// Forbidden
CKeyboard(void);
CKeyboard(const CKeyboard &p_source);
const CKeyboard &operator =(const CKeyboard &p_source);
// Key press management
virtual const bool keyPress(const SDL_Event &p_event);
// Key hold management
virtual const bool keyHold(void);
// Draw
virtual void render(const bool p_focus) const;
// Move cursor
const bool moveCursorUp(const bool p_loop);
const bool moveCursorDown(const bool p_loop);
const bool moveCursorLeft(const bool p_loop);
const bool moveCursorRight(const bool p_loop);
// Type a letter
const bool type(const std::string &p_text = "");
// Remove last letter
const bool backspace(void);
// UTF8 character or not
const bool utf8Code(const unsigned char p_c) const;
// The image representing the keyboard
SDL_Surface *m_imageKeyboard;
// The image representing the input text field
SDL_Surface *m_textField;
// The input text
std::string m_inputText;
// The cursor index
unsigned char m_selected;
// The footer
SDL_Surface *m_footer;
// Key sets
std::string m_keySets[NB_KEY_SETS];
unsigned char m_keySet;
// Pointers to resources
TTF_Font *m_font;
TTF_Font *m_font_sm;
};
#endif

View file

@ -0,0 +1,89 @@
#include <cstdlib>
#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include "def.h"
#include "sdlutils.h"
#include "resourceManager.h"
#include "commander.h"
// Globals
SDL_Surface *Globals::g_screen = NULL;
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
SDL_Surface *Globals::g_scaled = NULL;
#endif
const SDL_Color Globals::g_colorTextNormal = {COLOR_TEXT_NORMAL};
const SDL_Color Globals::g_colorTextTitle = {COLOR_TEXT_TITLE};
const SDL_Color Globals::g_colorTextDir = {COLOR_TEXT_DIR};
const SDL_Color Globals::g_colorTextSelected = {COLOR_TEXT_SELECTED};
std::vector<CWindow *> Globals::g_windows;
int main(int argc, char** argv)
{
// Avoid crash due to the absence of mouse
{
char l_s[]="SDL_NOMOUSE=1";
putenv(l_s);
}
#if defined(PLATFORM_RG35XX)
// we launch in an overclocked state on rg35xx
system("echo 504000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");
#endif
// Init SDL
SDL_Init(SDL_INIT_VIDEO);
// Hide cursor
SDL_ShowCursor(SDL_DISABLE);
SDL_EnableKeyRepeat(300,100);
// Screen
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
Globals::g_scaled = SDL_SetVideoMode(640, 480, SCREEN_BPP, SURFACE_FLAGS);
if (Globals::g_scaled == NULL)
{
std::cerr << "SDL_SetVideoMode failed: " << SDL_GetError() << std::endl;
return 1;
}
Globals::g_screen = SDL_CreateRGBSurface(SURFACE_FLAGS, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0,0,0,0);
if (Globals::g_screen == NULL)
{
std::cerr << "SDL_CreateRGBSurface (screen) failed: " << SDL_GetError() << std::endl;
return 1;
}
#else
Globals::g_screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SURFACE_FLAGS);
if (Globals::g_screen == NULL)
{
std::cerr << "SDL_SetVideoMode failed: " << SDL_GetError() << std::endl;
return 1;
}
#endif
// Init font
if (TTF_Init() == -1)
{
std::cerr << "TTF_Init failed: " << SDL_GetError() << std::endl;
return 1;
}
#if defined(PLATFORM_RG350) || defined(PLATFORM_ODBETA)
SDL_utils::checkIPU();
SDL_utils::setIPUSharpness("0");
#endif
// Create instances
CResourceManager::instance();
char *home = getenv("HOME");
std::string l_path = home ? home : PATH_DEFAULT;
CCommander l_commander(l_path, l_path);
// Main loop
l_commander.execute();
//Quit
SDL_utils::hastalavista();
return 0;
}

View file

@ -0,0 +1,339 @@
#include <iostream>
#include <sstream>
#include "panel.h"
#include "resourceManager.h"
#include "sdlutils.h"
#include "fileutils.h"
#define PANEL_SIZE 158
#define NAME_SIZE 140
CPanel::CPanel(const std::string &p_path, const Sint16 p_x):
m_currentPath(""),
m_camera(0),
m_x(p_x),
m_highlightedLine(0),
m_iconDir(CResourceManager::instance().getSurface(CResourceManager::T_SURFACE_FOLDER)),
m_iconFile(CResourceManager::instance().getSurface(CResourceManager::T_SURFACE_FILE)),
m_iconUp(CResourceManager::instance().getSurface(CResourceManager::T_SURFACE_UP)),
m_cursor1(CResourceManager::instance().getSurface(CResourceManager::T_SURFACE_CURSOR1)),
m_cursor2(CResourceManager::instance().getSurface(CResourceManager::T_SURFACE_CURSOR2)),
m_font(CResourceManager::instance().getFont())
{
// List the given path
if (m_fileLister.list(p_path))
{
// Path OK
m_currentPath = p_path;
}
else
{
// The path is wrong => take default
m_fileLister.list(PATH_DEFAULT);
m_currentPath = PATH_DEFAULT;
}
}
CPanel::~CPanel(void)
{
}
void CPanel::render(const bool p_active) const
{
// Draw cursor
SDL_utils::applySurface(m_x - 1, Y_LIST + (m_highlightedLine - m_camera) * LINE_HEIGHT, p_active ? m_cursor1 : m_cursor2, Globals::g_screen);
// Draw panel
const Sint16 l_x = m_x + m_iconDir->w + 2;
const unsigned int l_nbTotal = m_fileLister.getNbTotal();
Sint16 l_y = Y_LIST;
SDL_Surface *l_surfaceTmp = NULL;
const SDL_Color *l_color = NULL;
SDL_Rect l_rect;
// Current dir
l_surfaceTmp = SDL_utils::renderText(m_font, m_currentPath, Globals::g_colorTextTitle);
if (l_surfaceTmp->w > PANEL_SIZE)
{
l_rect.x = l_surfaceTmp->w - PANEL_SIZE;
l_rect.y = 0;
l_rect.w = PANEL_SIZE;
l_rect.h = l_surfaceTmp->h;
SDL_utils::applySurface(m_x, Y_HEADER - Y_OFFSET, l_surfaceTmp, Globals::g_screen, &l_rect);
}
else
{
SDL_utils::applySurface(m_x, Y_HEADER - Y_OFFSET, l_surfaceTmp, Globals::g_screen);
}
SDL_FreeSurface(l_surfaceTmp);
// Content
for (unsigned int l_i = m_camera; l_i < m_camera + NB_VISIBLE_LINES && l_i < l_nbTotal; ++l_i)
{
// Icon and color
if (m_fileLister.isDirectory(l_i))
{
// Icon
if (m_fileLister[l_i].m_name == "..")
l_surfaceTmp = m_iconUp;
else
l_surfaceTmp = m_iconDir;
// Color
if (m_selectList.find(l_i) != m_selectList.end())
l_color = &Globals::g_colorTextSelected;
else
l_color = &Globals::g_colorTextDir;
}
else
{
// Icon
l_surfaceTmp = m_iconFile;
// Color
if (m_selectList.find(l_i) != m_selectList.end())
l_color = &Globals::g_colorTextSelected;
else
l_color = &Globals::g_colorTextNormal;
}
SDL_utils::applySurface(m_x, l_y, l_surfaceTmp, Globals::g_screen);
// Text
l_surfaceTmp = SDL_utils::renderText(m_font, m_fileLister[l_i].m_name, *l_color);
if (l_surfaceTmp->w > NAME_SIZE)
{
l_rect.x = 0;
l_rect.y = 0;
l_rect.w = NAME_SIZE;
l_rect.h = l_surfaceTmp->h;
SDL_utils::applySurface(l_x, l_y + 2 - Y_OFFSET, l_surfaceTmp, Globals::g_screen, &l_rect);
}
else
{
SDL_utils::applySurface(l_x, l_y + 2 - Y_OFFSET, l_surfaceTmp, Globals::g_screen);
}
SDL_FreeSurface(l_surfaceTmp);
// Next line
l_y += LINE_HEIGHT;
}
// Footer
std::string l_footer("-");
if (!m_fileLister.isDirectory(m_highlightedLine))
{
std::ostringstream l_s;
l_s << m_fileLister[m_highlightedLine].m_size;
l_footer = l_s.str();
File_utils::formatSize(l_footer);
}
SDL_utils::applyText(m_x + 2, Y_FOOTER - Y_OFFSET, Globals::g_screen, m_font, "Size:", Globals::g_colorTextTitle);
SDL_utils::applyText(m_x + PANEL_SIZE - 2, Y_FOOTER - Y_OFFSET, Globals::g_screen, m_font, l_footer, Globals::g_colorTextTitle, SDL_utils::T_TEXT_ALIGN_RIGHT);
}
const bool CPanel::moveCursorUp(unsigned char p_step)
{
bool l_ret(false);
if (m_highlightedLine)
{
// Move cursor
if (m_highlightedLine > p_step)
m_highlightedLine -= p_step;
else
m_highlightedLine = 0;
// Adjust camera
adjustCamera();
// Return true for new render
l_ret = true;
}
return l_ret;
}
const bool CPanel::moveCursorDown(unsigned char p_step)
{
bool l_ret(false);
const unsigned int l_nb = m_fileLister.getNbTotal();
if (m_highlightedLine < l_nb - 1)
{
// Move cursor
if (m_highlightedLine + p_step > l_nb - 1)
m_highlightedLine = l_nb - 1;
else
m_highlightedLine += p_step;
// Adjust camera
adjustCamera();
// Return true for new render
l_ret = true;
}
return l_ret;
}
const bool CPanel::open(const std::string &p_path)
{
bool l_ret(false);
std::string l_newPath("");
std::string l_oldDir("");
if (p_path.empty())
{
// Open highlighted dir
if (m_fileLister[m_highlightedLine].m_name == "..")
{
// Go to parent dir
size_t l_pos = m_currentPath.rfind('/');
// Remove the last dir in the path
l_newPath = m_currentPath.substr(0, l_pos);
if (l_newPath.empty())
// We're at /
l_newPath = "/";
l_oldDir = m_currentPath.substr(l_pos + 1);
}
else
{
l_newPath = m_currentPath + (m_currentPath == "/" ? "" : "/") + m_fileLister[m_highlightedLine].m_name;
}
}
else
{
// Open given dir
if (p_path == m_currentPath)
return false;
l_newPath = p_path;
}
// List the new path
if (m_fileLister.list(l_newPath))
{
// Path OK
m_currentPath = l_newPath;
// If it's a back movement, restore old dir
if (!l_oldDir.empty())
m_highlightedLine = m_fileLister.searchDir(l_oldDir);
else
m_highlightedLine = 0;
// Camera
adjustCamera();
// Clear select list
m_selectList.clear();
// New render
l_ret = true;
}
INHIBIT(std::cout << "open - new current path: " << m_currentPath << std::endl;)
return l_ret;
}
const bool CPanel::goToParentDir(void)
{
bool l_ret(false);
// Select ".." and open it
if (m_currentPath != "/")
{
m_highlightedLine = 0;
l_ret = open();
}
return l_ret;
}
void CPanel::adjustCamera(void)
{
if (m_fileLister.getNbTotal() <= NB_VISIBLE_LINES)
m_camera = 0;
else if (m_highlightedLine < m_camera)
m_camera = m_highlightedLine;
else if (m_highlightedLine > m_camera + NB_VISIBLE_LINES - 1)
m_camera = m_highlightedLine - NB_VISIBLE_LINES + 1;
}
const std::string &CPanel::getHighlightedItem(void) const
{
return m_fileLister[m_highlightedLine].m_name;
}
const std::string CPanel::getHighlightedItemFull(void) const
{
return m_currentPath + (m_currentPath == "/" ? "" : "/") + m_fileLister[m_highlightedLine].m_name;
}
const std::string &CPanel::getCurrentPath(void) const
{
return m_currentPath;
}
const unsigned int &CPanel::getHighlightedIndex(void) const
{
return m_highlightedLine;
}
const unsigned int CPanel::getHighlightedIndexRelative(void) const
{
return m_highlightedLine - m_camera;
}
void CPanel::refresh(void)
{
// List current path
if (m_fileLister.list(m_currentPath))
{
// Adjust selected line
if (m_highlightedLine > m_fileLister.getNbTotal() - 1)
m_highlightedLine = m_fileLister.getNbTotal() - 1;
}
else
{
// Current path doesn't exist anymore => default
m_fileLister.list(PATH_DEFAULT);
m_currentPath = PATH_DEFAULT;
m_highlightedLine = 0;
}
// Camera
adjustCamera();
// Clear select list
m_selectList.clear();
}
const bool CPanel::addToSelectList(const bool p_step)
{
if (m_fileLister[m_highlightedLine].m_name != "..")
{
// Search highlighted element in select list
std::set<unsigned int>::iterator l_it = m_selectList.find(m_highlightedLine);
if (l_it == m_selectList.end())
// Element not present => we add it
m_selectList.insert(m_highlightedLine);
else
// Element present => we remove it from the list
m_selectList.erase(m_highlightedLine);
if (p_step)
moveCursorDown(1);
return true;
}
else
{
return false;
}
}
const std::set<unsigned int> &CPanel::getSelectList(void) const
{
return m_selectList;
}
void CPanel::getSelectList(std::vector<std::string> &p_list) const
{
p_list.clear();
// Insert full path of selected files
for (std::set<unsigned int>::const_iterator l_it = m_selectList.begin(); l_it != m_selectList.end(); ++l_it)
{
if (m_currentPath == "/")
p_list.push_back(m_currentPath + m_fileLister[*l_it].m_name);
else
p_list.push_back(m_currentPath + "/" + m_fileLister[*l_it].m_name);
}
}
void CPanel::selectAll(void)
{
const unsigned int l_nb = m_fileLister.getNbTotal();
for (unsigned int l_i = 1; l_i < l_nb; ++l_i)
m_selectList.insert(l_i);
}
void CPanel::selectNone(void)
{
m_selectList.clear();
}
const bool CPanel::isDirectoryHighlighted(void) const
{
return m_fileLister.isDirectory(m_highlightedLine);
}

View file

@ -0,0 +1,101 @@
#ifndef _PANEL_H_
#define _PANEL_H_
#include <string>
#include <set>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include "fileLister.h"
#include "def.h"
class CPanel
{
public:
// Constructor
CPanel(const std::string &p_path, const Sint16 p_x);
// Destructor
virtual ~CPanel(void);
// Draw the panel on the screen
void render(const bool p_active) const;
// Move cursor
const bool moveCursorUp(unsigned char p_step);
const bool moveCursorDown(unsigned char p_step);
// Open selected item
const bool open(const std::string &p_path = "");
// Refresh current directory
void refresh(void);
// Go to parent dir
const bool goToParentDir(void);
// Selected file with just the name
const std::string &getHighlightedItem(void) const;
// Selected file with full path
const std::string getHighlightedItemFull(void) const;
// Current path
const std::string &getCurrentPath(void) const;
// Selected index
const unsigned int &getHighlightedIndex(void) const;
const unsigned int getHighlightedIndexRelative(void) const;
// True => directory, false => file, or dir ".."
const bool isDirectoryHighlighted(void) const;
// Add/remove current file to the select list
const bool addToSelectList(const bool p_step);
// Get select list
const std::set<unsigned int> &getSelectList(void) const;
void getSelectList(std::vector<std::string> &p_list) const;
// Clear select list
void selectAll(void);
void selectNone(void);
private:
// Forbidden
CPanel(void);
CPanel(const CPanel &p_source);
const CPanel &operator =(const CPanel &p_source);
// Adjust camera
void adjustCamera(void);
// File lister
CFileLister m_fileLister;
// Current path
std::string m_currentPath;
// Index of the first displayed line
unsigned int m_camera;
// X coordinate
const Sint16 m_x;
// Highlighted line
unsigned int m_highlightedLine;
// Selection list
std::set<unsigned int> m_selectList;
// Pointers to resources
SDL_Surface *m_iconDir;
SDL_Surface *m_iconFile;
SDL_Surface *m_iconUp;
SDL_Surface *m_cursor1;
SDL_Surface *m_cursor2;
TTF_Font *m_font;
};
#endif

View file

@ -0,0 +1,71 @@
#include <iostream>
#include "resourceManager.h"
#include "def.h"
#include "sdlutils.h"
#include "def.h"
CResourceManager& CResourceManager::instance(void)
{
static CResourceManager l_singleton;
return l_singleton;
}
CResourceManager::CResourceManager(void) :
m_font(NULL)
{
// Load images
m_surfaces[T_SURFACE_BG] = SDL_utils::loadImage(RESDIR "/background.png");
m_surfaces[T_SURFACE_FILE] = SDL_utils::loadImage(RESDIR "/file.png");
m_surfaces[T_SURFACE_FOLDER] = SDL_utils::loadImage(RESDIR "/folder.png");
m_surfaces[T_SURFACE_UP] = SDL_utils::loadImage(RESDIR "/up.png");
m_surfaces[T_SURFACE_CURSOR1] = SDL_utils::createImage(159, 15, SDL_MapRGB(Globals::g_screen->format, COLOR_CURSOR_1));
m_surfaces[T_SURFACE_CURSOR2] = SDL_utils::createImage(159, 15, SDL_MapRGB(Globals::g_screen->format, COLOR_CURSOR_2));
// Load fonts
#if defined(PLATFORM_TRIMUI)
m_font = SDL_utils::loadFont(RESDIR "/Commander-11.ttf", 11);
#else
m_font = SDL_utils::loadFont(RESDIR "/Fiery_Turk.ttf", 8);
#endif
m_font_sm = SDL_utils::loadFont(RESDIR "/Fiery_Turk.ttf", 8);
}
void CResourceManager::sdlCleanup(void)
{
INHIBIT(std::cout << "CResourceManager::sdlCleanup" << std::endl;)
int l_i(0);
// Free surfaces
for (l_i = 0; l_i < NB_SURFACES; ++l_i)
{
if (m_surfaces[l_i] != NULL)
{
SDL_FreeSurface(m_surfaces[l_i]);
m_surfaces[l_i] = NULL;
}
}
// Free fonts
if (m_font != NULL)
{
TTF_CloseFont(m_font);
m_font = NULL;
}
if (m_font_sm != NULL)
{
TTF_CloseFont(m_font_sm);
m_font_sm = NULL;
}
}
SDL_Surface *CResourceManager::getSurface(const T_SURFACE p_surface) const
{
return m_surfaces[p_surface];
}
TTF_Font *CResourceManager::getFont(void) const
{
return m_font;
}
TTF_Font *CResourceManager::getSmallFont(void) const
{
return m_font_sm;
}

View file

@ -0,0 +1,52 @@
#ifndef _RESOURCEMANAGER_H_
#define _RESOURCEMANAGER_H_
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#define NB_SURFACES 6
class CResourceManager
{
public:
typedef enum
{
T_SURFACE_BG = 0,
T_SURFACE_FILE,
T_SURFACE_FOLDER,
T_SURFACE_UP,
T_SURFACE_CURSOR1,
T_SURFACE_CURSOR2
}
T_SURFACE;
// Method to get the instance
static CResourceManager& instance(void);
// Cleanup all resources
void sdlCleanup(void);
// Get a loaded surface
SDL_Surface *getSurface(const T_SURFACE p_surface) const;
// Get the loaded fonts
TTF_Font *getFont(void) const;
TTF_Font *getSmallFont(void) const;
private:
// Forbidden
CResourceManager(void);
CResourceManager(const CResourceManager &p_source);
const CResourceManager &operator =(const CResourceManager &p_source);
// Images
SDL_Surface *m_surfaces[NB_SURFACES];
// Fonts
TTF_Font *m_font;
TTF_Font *m_font_sm;
};
#endif

View file

@ -0,0 +1,322 @@
#include <iostream>
#include "sdlutils.h"
#include <SDL/SDL_image.h>
#include "def.h"
#include "resourceManager.h"
#if defined(PLATFORM_RG350) || defined(PLATFORM_ODBETA)
#include <dirent.h>
std::string ipuscaling = "NONE";
// copied from gambatte-dms
void SDL_utils::checkIPU(void) {
FILE *aspect_ratio_file = NULL;
DIR *ipu_dir = NULL;
std::string ipu_OpenDinguxLegacy = ("/sys/devices/platform/jz-lcd.0/keep_aspect_ratio");
std::string ipu_RetroFW10 = ("/proc/jz/ipu_ratio");
std::string ipu_RetroFW20 = ("/proc/jz/ipu");
std::string ipu_OpenDingux = ("/sys/devices/platform/13080000.ipu");
aspect_ratio_file = fopen(ipu_OpenDinguxLegacy.c_str(), "r+");
if (aspect_ratio_file != NULL) {
fclose(aspect_ratio_file);
ipuscaling = ipu_OpenDinguxLegacy;
printf("Detected IPU scaling - OpenDinguxLegacy\n");
return;
}
aspect_ratio_file = fopen(ipu_RetroFW10.c_str(), "r+");
if (aspect_ratio_file != NULL) {
fclose(aspect_ratio_file);
ipuscaling = ipu_RetroFW10;
printf("Detected IPU scaling - RetroFW 1.X\n");
return;
}
aspect_ratio_file = fopen("/proc/jz/gpio", "r+"); //workaround to check if the fw is retrofw2
if (aspect_ratio_file != NULL) {
fclose(aspect_ratio_file);
ipuscaling = ipu_RetroFW20;
printf("Detected IPU scaling - RetroFW 2.X\n");
return;
}
ipu_dir = opendir("/sys/devices/platform/13080000.ipu");
if (ipu_dir != NULL) {
closedir(ipu_dir);
ipuscaling = "NEW_OD_IPU";
printf("Detected IPU scaling - OpenDingux\n");
return;
}
printf("Could not detect IPU scaling\n");
return;
}
void SDL_utils::setIPUSharpness(const char *svalue){
if (ipuscaling == "NONE") return;
else if (ipuscaling == "NEW_OD_IPU") {
if(svalue == "0"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=0");
} else if(svalue == "1"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=1");
} else if(svalue == "2"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=2");
} else if(svalue == "3"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=3");
} else if(svalue == "4"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=4");
} else if(svalue == "5"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=5");
} else if(svalue == "6"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=6");
} else if(svalue == "7"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=7");
} else if(svalue == "8"){
SDL_putenv("SDL_VIDEO_KMSDRM_SCALING_SHARPNESS=8");
}
return;
}
FILE *sharpness_file = NULL;
sharpness_file = fopen("/sys/devices/platform/jz-lcd.0/sharpness_upscaling", "r+");
if (sharpness_file != NULL) {
fclose(sharpness_file);
sharpness_file = fopen("/sys/devices/platform/jz-lcd.0/sharpness_upscaling", "w");
fwrite(svalue, 1, 1, sharpness_file);
fclose(sharpness_file);
}
}
#endif
// Load an image using SDL_image
SDL_Surface *SDL_utils::loadImage(const std::string &p_filename)
{
INHIBIT(std::cout << "SDL_utils::loadImage(" << p_filename << ")" << std::endl;)
// Load image
SDL_Surface* l_img = IMG_Load(p_filename.c_str());
SDL_Surface* l_img2 = NULL;
if(l_img != NULL)
{
// Optimize the image
l_img2 = SDL_DisplayFormat(l_img);
// Free the first image
SDL_FreeSurface(l_img);
// Set color key
if (l_img2 != NULL)
SDL_SetColorKey(l_img2, SDL_SRCCOLORKEY, SDL_MapRGB(l_img2->format, COLOR_KEY));
}
// Check errors
if (l_img2 == NULL)
std::cerr << "SDL_utils::loadImage: " << SDL_GetError() << std::endl;
return l_img2;
}
void SDL_utils::applySurface(const Sint16 p_x, const Sint16 p_y, SDL_Surface* p_source, SDL_Surface* p_destination, SDL_Rect *p_clip)
{
// Rectangle to hold the offsets
SDL_Rect l_offset;
// Set offsets
l_offset.x = p_x;
l_offset.y = p_y;
//Blit the surface
SDL_BlitSurface(p_source, p_clip, p_destination, &l_offset);
}
TTF_Font *SDL_utils::loadFont(const std::string &p_font, const int p_size)
{
INHIBIT(std::cout << "SDL_utils::loadFont(" << p_font << ", " << p_size << ")" << std::endl;)
TTF_Font *l_font = TTF_OpenFont(p_font.c_str(), p_size);
if (l_font == NULL)
std::cerr << "SDL_utils::loadFont: " << SDL_GetError() << std::endl;
return l_font;
}
SDL_Surface *SDL_utils::renderText(TTF_Font *p_font, const std::string &p_text, const SDL_Color &p_fg)
{
return TTF_RenderUTF8_Solid(p_font, p_text.c_str(), p_fg);
}
void SDL_utils::applyText(const Sint16 p_x, const Sint16 p_y, SDL_Surface* p_destination, TTF_Font *p_font, const std::string &p_text, const SDL_Color &p_fg, const T_TEXT_ALIGN p_align)
{
SDL_Surface *l_text = renderText(p_font, p_text, p_fg);
switch (p_align)
{
case T_TEXT_ALIGN_LEFT:
applySurface(p_x, p_y, l_text, p_destination);
break;
case T_TEXT_ALIGN_RIGHT:
applySurface(p_x - l_text->w, p_y, l_text, p_destination);
break;
case T_TEXT_ALIGN_CENTER:
applySurface(p_x - l_text->w / 2, p_y, l_text, p_destination);
break;
default:
break;
}
SDL_FreeSurface(l_text);
}
SDL_Surface *SDL_utils::createImage(const int p_width, const int p_height, const Uint32 p_color)
{
// Create image in the same format as the screen
SDL_Surface *l_ret = SDL_CreateRGBSurface(SURFACE_FLAGS, p_width, p_height, Globals::g_screen->format->BitsPerPixel, Globals::g_screen->format->Rmask, Globals::g_screen->format->Gmask, Globals::g_screen->format->Bmask, Globals::g_screen->format->Amask);
if (l_ret == NULL)
std::cerr << "SDL_utils::createImage: " << SDL_GetError() << std::endl;
// Fill image with the given color
SDL_FillRect(l_ret, NULL, p_color);
return l_ret;
}
void SDL_utils::renderAll(void)
{
if (Globals::g_windows.empty())
return;
// First window to draw is the last fullscreen
unsigned int l_i = Globals::g_windows.size() - 1;
while (l_i && !Globals::g_windows[l_i]->isFullScreen())
--l_i;
// Draw windows
for (std::vector<CWindow *>::iterator l_it = Globals::g_windows.begin() + l_i; l_it != Globals::g_windows.end(); ++l_it)
(*l_it)->render(l_it + 1 == Globals::g_windows.end());
}
void SDL_utils::hastalavista(void)
{
// Destroy all dialogs except the first one (the commander)
while (Globals::g_windows.size() > 1)
delete Globals::g_windows.back();
// Free resources
CResourceManager::instance().sdlCleanup();
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
SDL_FreeSurface(Globals::g_screen);
#endif
// Quit SDL
TTF_Quit();
SDL_Quit();
}
void SDL_utils::pleaseWait(void)
{
SDL_Surface *l_surfaceTmp = renderText(CResourceManager::instance().getFont(), "Please wait...", Globals::g_colorTextNormal);
SDL_Rect l_rect;
l_rect.x = (SCREEN_WIDTH - (l_surfaceTmp->w + 2 * DIALOG_MARGIN + 2 * DIALOG_BORDER)) >> 1;
l_rect.y = (SCREEN_HEIGHT - (l_surfaceTmp->h + 9)) >> 1;
l_rect.w = l_surfaceTmp->w + 2 * DIALOG_MARGIN + 2 * DIALOG_BORDER;
l_rect.h = l_surfaceTmp->h + 9;
SDL_FillRect(Globals::g_screen, &l_rect, SDL_MapRGB(Globals::g_screen->format, COLOR_BORDER));
l_rect.x += DIALOG_BORDER;
l_rect.y += DIALOG_BORDER;
l_rect.w -= 2 * DIALOG_BORDER;
l_rect.h -= 2 * DIALOG_BORDER;
SDL_FillRect(Globals::g_screen, &l_rect, SDL_MapRGB(Globals::g_screen->format, COLOR_BG_1));
applySurface((SCREEN_WIDTH - l_surfaceTmp->w) >> 1, (SCREEN_HEIGHT - l_surfaceTmp->h) >> 1, l_surfaceTmp, Globals::g_screen);
SDL_FreeSurface(l_surfaceTmp);
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
upscale16NEON(Globals::g_screen->pixels, Globals::g_scaled->pixels);
SDL_Flip(Globals::g_scaled);
#else
SDL_Flip(Globals::g_screen);
#endif
}
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
// eggs amazing SW integer scaler
void upscale16NEON(void* src, void* dst) {
asm volatile (
" vmov.u64 d1,#0xFFFFFFFF ;"
" vmvn.u64 d0,#0xFFFFFFFF ;"
" add r2,%0,#(320*240*2) ;"
"1: add r3,%1,#(640*1*2) ;"
" add lr,%1,#(640*2*2) ;"
"2: vldmia %0!,{q8-q11} ;"
" vdup.16 d2,d23[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d23[2] ;"
" vand d3,d3,d1 ;"
" vorr d31,d2,d3 ;"
" vdup.16 d2,d23[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d23[0] ;"
" vand d3,d3,d1 ;"
" vorr d30,d2,d3 ;"
" vdup.16 d2,d22[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d22[2] ;"
" vand d3,d3,d1 ;"
" vorr d29,d2,d3 ;"
" vdup.16 d2,d22[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d22[0] ;"
" vand d3,d3,d1 ;"
" vorr d28,d2,d3 ;"
" vdup.16 d2,d21[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d21[2] ;"
" vand d3,d3,d1 ;"
" vorr d27,d2,d3 ;"
" vdup.16 d2,d21[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d21[0] ;"
" vand d3,d3,d1 ;"
" vorr d26,d2,d3 ;"
" vdup.16 d2,d20[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d20[2] ;"
" vand d3,d3,d1 ;"
" vorr d25,d2,d3 ;"
" vdup.16 d2,d20[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d20[0] ;"
" vand d3,d3,d1 ;"
" vorr d24,d2,d3 ;"
" vdup.16 d2,d19[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d19[2] ;"
" vand d3,d3,d1 ;"
" vorr d23,d2,d3 ;"
" vdup.16 d2,d19[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d19[0] ;"
" vand d3,d3,d1 ;"
" vorr d22,d2,d3 ;"
" vdup.16 d2,d18[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d18[2] ;"
" vand d3,d3,d1 ;"
" vorr d21,d2,d3 ;"
" vdup.16 d2,d18[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d18[0] ;"
" vand d3,d3,d1 ;"
" vorr d20,d2,d3 ;"
" vdup.16 d2,d17[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d17[2] ;"
" vand d3,d3,d1 ;"
" vorr d19,d2,d3 ;"
" vdup.16 d2,d17[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d17[0] ;"
" vand d3,d3,d1 ;"
" vorr d18,d2,d3 ;"
" vdup.16 d2,d16[3] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d16[2] ;"
" vand d3,d3,d1 ;"
" vorr d17,d2,d3 ;"
" vdup.16 d2,d16[1] ;"
" vand d2,d2,d0 ;"
" vdup.16 d3,d16[0] ;"
" vand d3,d3,d1 ;"
" vorr d16,d2,d3 ;"
" vstmia %1!,{q8-q15} ;"
" vstmia r3!,{q8-q15} ;"
" cmp r3,lr ;"
" bne 2b ;"
" cmp %0,r2 ;"
" mov %1,lr ;"
" bne 1b "
:: "r"(src), "r"(dst)
: "r2","r3","lr","q0","q1","q8","q9","q10","q11","q12","q13","q14","q15","memory","cc"
);
}
#endif

View file

@ -0,0 +1,76 @@
#ifndef _SDLUTILS_H_
#define _SDLUTILS_H_
#include <string>
#include <vector>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include "window.h"
namespace SDL_utils
{
// Text alignment
typedef enum
{
T_TEXT_ALIGN_LEFT = 0,
T_TEXT_ALIGN_RIGHT,
T_TEXT_ALIGN_CENTER
}
T_TEXT_ALIGN;
#if defined(PLATFORM_RG350) || defined(PLATFORM_ODBETA)
// identify IPU if available
void checkIPU(void);
void setIPUSharpness(const char *svalue);
#endif
// Load an image using SDL_image
SDL_Surface *loadImage(const std::string &p_filename);
// Load a TTF font
TTF_Font *loadFont(const std::string &p_font, const int p_size);
// Apply a surface on another surface
void applySurface(const Sint16 p_x, const Sint16 p_y, SDL_Surface* p_source, SDL_Surface* p_destination, SDL_Rect *p_clip = NULL);
// Render a text
SDL_Surface *renderText(TTF_Font *p_font, const std::string &p_text, const SDL_Color &p_fg);
// Render a text and apply on a given surface
void applyText(const Sint16 p_x, const Sint16 p_y, SDL_Surface* p_destination, TTF_Font *p_font, const std::string &p_text, const SDL_Color &p_fg, const T_TEXT_ALIGN p_align = T_TEXT_ALIGN_LEFT);
// Create an image filled with the given color
SDL_Surface *createImage(const int p_width, const int p_height, const Uint32 p_color);
// Render all opened windows
void renderAll(void);
// Cleanup and quit
void hastalavista(void);
// Display a waiting window
void pleaseWait(void);
}
// Globals
namespace Globals
{
// Screen
extern SDL_Surface *g_screen;
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
extern SDL_Surface *g_scaled;
#endif
// Colors
extern const SDL_Color g_colorTextNormal;
extern const SDL_Color g_colorTextTitle;
extern const SDL_Color g_colorTextDir;
extern const SDL_Color g_colorTextSelected;
// The list of opened windows
extern std::vector<CWindow *> g_windows;
}
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
void upscale16NEON(void* src, void* dst);
#endif
#endif

View file

@ -0,0 +1,219 @@
#include <iostream>
#include <fstream>
#include "viewer.h"
#include "resourceManager.h"
#include "def.h"
#include "sdlutils.h"
CViewer::CViewer(const std::string &p_fileName):
CWindow(),
m_fileName(p_fileName),
m_firstLine(0),
m_image(NULL),
m_font(CResourceManager::instance().getFont())
{
// Init clip rect
m_clip.x = 0;
m_clip.y = 0;
m_clip.w = SCREEN_WIDTH - 2 * VIEWER_MARGIN;
// Create background image
m_image = SDL_utils::createImage(SCREEN_WIDTH, SCREEN_HEIGHT, SDL_MapRGB(Globals::g_screen->format, COLOR_BG_1));
{
SDL_Rect l_rect;
l_rect.x = 0;
l_rect.y = 0;
l_rect.w = SCREEN_WIDTH;
l_rect.h = Y_LIST;
SDL_FillRect(m_image, &l_rect, SDL_MapRGB(m_image->format, COLOR_BORDER));
}
// Print title
SDL_Surface *l_surfaceTmp = SDL_utils::renderText(m_font, m_fileName, Globals::g_colorTextTitle);
if (l_surfaceTmp->w > m_image->w - 2 * VIEWER_MARGIN)
{
SDL_Rect l_rect;
l_rect.x = l_surfaceTmp->w - (m_image->w - 2 * VIEWER_MARGIN);
l_rect.y = 0;
l_rect.w = m_image->w - 2 * VIEWER_MARGIN;
l_rect.h = l_surfaceTmp->h;
SDL_utils::applySurface(VIEWER_MARGIN, Y_HEADER, l_surfaceTmp, m_image, &l_rect);
}
else
{
SDL_utils::applySurface(VIEWER_MARGIN, Y_HEADER, l_surfaceTmp, m_image);
}
m_clip.h = l_surfaceTmp->h;
SDL_FreeSurface(l_surfaceTmp);
// Read file
std::ifstream l_file(m_fileName.c_str());
if (l_file.is_open())
{
std::string l_line("");
while (std::getline(l_file, l_line))
{
m_lines.push_back(l_line);
}
l_file.close();
}
else
std::cerr << "Error: unable to open file " << m_fileName << std::endl;
INHIBIT(std::cout << "CViewer: " << m_lines.size() << " lines read" << std::endl;)
}
CViewer::~CViewer(void)
{
// Free surfaces
if (m_image != NULL)
{
SDL_FreeSurface(m_image);
m_image = NULL;
}
}
void CViewer::render(const bool p_focus) const
{
INHIBIT(std::cout << "CViewer::render fullscreen: " << isFullScreen() << " focus: " << p_focus << std::endl;)
// Draw background
SDL_utils::applySurface(0, 0, m_image, Globals::g_screen);
// Draw lines
SDL_Surface *l_surfaceTmp(NULL);
unsigned int l_i(0);
std::vector<std::string>::const_iterator l_it = m_lines.begin() + m_firstLine;
while (l_it != m_lines.end() && l_i < VIEWER_NB_LINES)
{
if (!l_it->empty())
{
l_surfaceTmp = SDL_utils::renderText(m_font, *l_it, Globals::g_colorTextNormal);
SDL_utils::applySurface(VIEWER_MARGIN, VIEWER_Y_LIST + l_i * VIEWER_LINE_HEIGHT - Y_OFFSET, l_surfaceTmp, Globals::g_screen, &m_clip);
SDL_FreeSurface(l_surfaceTmp);
}
// Next line
++l_it;
++l_i;
}
}
const bool CViewer::keyPress(const SDL_Event &p_event)
{
CWindow::keyPress(p_event);
bool l_ret(false);
switch (p_event.key.keysym.sym)
{
case MYKEY_PARENT:
case MYKEY_MENU:
m_retVal = -1;
l_ret = true;
break;
case MYKEY_UP:
l_ret = moveUp(1);
break;
case MYKEY_DOWN:
l_ret = moveDown(1);
break;
case MYKEY_PAGEUP:
l_ret = moveUp(VIEWER_NB_LINES - 1);
break;
case MYKEY_PAGEDOWN:
l_ret = moveDown(VIEWER_NB_LINES - 1);
break;
case MYKEY_LEFT:
l_ret = moveLeft();
break;
case MYKEY_RIGHT:
moveRight();
l_ret = true;
break;
default:
break;
}
return l_ret;
}
const bool CViewer::keyHold(void)
{
bool l_ret(false);
switch(m_lastPressed)
{
case MYKEY_UP:
if (tick(SDL_GetKeyState(NULL)[MYKEY_UP]))
l_ret = moveUp(1);
break;
case MYKEY_DOWN:
if (tick(SDL_GetKeyState(NULL)[MYKEY_DOWN]))
l_ret = moveDown(1);
break;
case MYKEY_PAGEUP:
if (tick(SDL_GetKeyState(NULL)[MYKEY_PAGEUP]))
l_ret = moveUp(VIEWER_NB_LINES - 1);
break;
case MYKEY_PAGEDOWN:
if (tick(SDL_GetKeyState(NULL)[MYKEY_PAGEDOWN]))
l_ret = moveDown(VIEWER_NB_LINES - 1);
break;
case MYKEY_LEFT:
if (tick(SDL_GetKeyState(NULL)[MYKEY_LEFT]))
l_ret = moveLeft();
break;
case MYKEY_RIGHT:
if (tick(SDL_GetKeyState(NULL)[MYKEY_RIGHT]))
{
moveRight();
l_ret = true;
}
break;
default:
break;
}
return l_ret;
}
const bool CViewer::isFullScreen(void) const
{
return true;
}
const bool CViewer::moveUp(const unsigned int p_step)
{
bool l_ret(false);
if (m_firstLine)
{
if (m_firstLine > p_step)
m_firstLine -= p_step;
else
m_firstLine = 0;
l_ret = true;
}
return l_ret;
}
const bool CViewer::moveDown(const unsigned int p_step)
{
bool l_ret(false);
if (m_firstLine + VIEWER_NB_LINES + 1 < m_lines.size())
{
if (m_firstLine + VIEWER_NB_LINES + 1 + p_step > m_lines.size())
m_firstLine = m_lines.size() - VIEWER_NB_LINES - 1;
else
m_firstLine += p_step;
l_ret = true;
}
return l_ret;
}
const bool CViewer::moveLeft(void)
{
bool l_ret(false);
if (m_clip.x > 0)
{
if (m_clip.x > VIEWER_X_STEP)
m_clip.x -= VIEWER_X_STEP;
else
m_clip.x = 0;
l_ret = true;
}
return l_ret;
}
void CViewer::moveRight(void)
{
m_clip.x += VIEWER_X_STEP;
}

View file

@ -0,0 +1,69 @@
#ifndef _VIEWER_H_
#define _VIEWER_H_
#include <string>
#include <vector>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include "window.h"
#define VIEWER_LINE_HEIGHT 13
#define VIEWER_NB_LINES 17
#define VIEWER_Y_LIST 18
#define VIEWER_MARGIN 1
#define VIEWER_X_STEP 32
#define VIEWER_SIZE_MAX 16777216 // = 16 MB
class CViewer : public CWindow
{
public:
// Constructor
CViewer(const std::string &p_fileName);
// Destructor
virtual ~CViewer(void);
private:
// Forbidden
CViewer(void);
CViewer(const CViewer &p_source);
const CViewer &operator =(const CViewer &p_source);
// Key press management
virtual const bool keyPress(const SDL_Event &p_event);
// Key hold management
virtual const bool keyHold(void);
// Draw
virtual void render(const bool p_focus) const;
// Is window full screen?
virtual const bool isFullScreen(void) const;
// Scroll
const bool moveUp(const unsigned int p_step);
const bool moveDown(const unsigned int p_step);
const bool moveLeft(void);
void moveRight(void);
// The viewed file name
std::string m_fileName;
// Coordinates
unsigned int m_firstLine;
mutable SDL_Rect m_clip;
// Background image
SDL_Surface *m_image;
// List of read lines
std::vector<std::string> m_lines;
// Pointers to resources
TTF_Font *m_font;
};
#endif

View file

@ -0,0 +1,138 @@
#include <iostream>
#include "window.h"
#include "def.h"
#include "sdlutils.h"
#define KEYHOLD_TIMER_FIRST 6
#define KEYHOLD_TIMER 2
CWindow::CWindow(void):
m_timer(0),
#if defined(PLATFORM_RG35XX)
m_lastPressed(0),
#else
m_lastPressed(SDLK_0),
#endif
m_retVal(0)
{
// Add window to the lists for render
Globals::g_windows.push_back(this);
}
CWindow::~CWindow(void)
{
// Remove last window
Globals::g_windows.pop_back();
}
const int CWindow::execute(void)
{
m_retVal = 0;
Uint32 l_time(0);
SDL_Event l_event;
bool l_loop(true);
bool l_render(true);
// Main loop
while (l_loop)
{
l_time = SDL_GetTicks();
// Handle key press
while (SDL_PollEvent(&l_event))
{
if (l_event.type == SDL_KEYDOWN)
{
l_render = this->keyPress(l_event);
if (m_retVal)
l_loop = false;
}
else if (l_event.type == SDL_QUIT)
{
// Re-insert event so we exit from nested menus
SDL_PushEvent(&l_event);
l_loop = false;
break;
}
}
// Handle key hold
if (l_loop)
l_render = this->keyHold() || l_render;
// Render if necessary
if (l_render && l_loop)
{
SDL_utils::renderAll();
#if defined(PLATFORM_MIYOOMINI) || defined(PLATFORM_RG35XX)
upscale16NEON(Globals::g_screen->pixels, Globals::g_scaled->pixels);
SDL_Flip(Globals::g_scaled);
#else
SDL_Flip(Globals::g_screen);
#endif
l_render = false;
INHIBIT(std::cout << "Render time: " << SDL_GetTicks() - l_time << "ms"<< std::endl;)
}
// Cap the framerate
l_time = MS_PER_FRAME - (SDL_GetTicks() - l_time);
if (l_time <= MS_PER_FRAME) SDL_Delay(l_time);
}
return m_retVal;
}
const bool CWindow::keyPress(const SDL_Event &p_event)
{
// Reset timer if running
if (m_timer)
m_timer = 0;
#if defined(PLATFORM_RG35XX)
m_lastPressed = p_event.key.keysym.scancode;
#else
m_lastPressed = p_event.key.keysym.sym;
#endif
return false;
}
const bool CWindow::keyHold(void)
{
// Default behavior
return false;
}
const bool CWindow::tick(const Uint8 p_held)
{
bool l_ret(false);
if (p_held)
{
if (m_timer)
{
--m_timer;
if (!m_timer)
{
// Trigger!
l_ret = true;
// Timer continues
m_timer = KEYHOLD_TIMER;
}
}
else
{
// Start timer
m_timer = KEYHOLD_TIMER_FIRST;
}
}
else
{
// Stop timer if running
if (m_timer)
m_timer = 0;
}
return l_ret;
}
const int CWindow::getReturnValue(void) const
{
return m_retVal;
}
const bool CWindow::isFullScreen(void) const
{
// Default behavior
return false;
}

View file

@ -0,0 +1,58 @@
#ifndef _WINDOW_H_
#define _WINDOW_H_
#include <SDL/SDL.h>
class CWindow
{
public:
// Destructor
virtual ~CWindow(void);
// Execute main loop of the window
const int execute(void);
// Return value
const int getReturnValue(void) const;
// Draw window
virtual void render(const bool p_focus) const = 0;
// Is window full screen?
virtual const bool isFullScreen(void) const;
protected:
// Constructor
CWindow(void);
// Key press management
virtual const bool keyPress(const SDL_Event &p_event);
// Key hold management
virtual const bool keyHold(void);
// Timer tick
const bool tick(const Uint8 p_held);
// Timer for key hold
unsigned int m_timer;
#if defined(PLATFORM_RG35XX)
uint8_t m_lastPressed;
#else
SDLKey m_lastPressed;
#endif
// Return value
int m_retVal;
private:
// Forbidden
CWindow(const CWindow &p_source);
const CWindow &operator =(const CWindow &p_source);
};
#endif

@ -1 +0,0 @@
Subproject commit 458bcd842bc48f730e12732fe8b3280e834d45ff

View file

@ -0,0 +1,6 @@
glsm/
*.[od]
*.dll
*.so
*.dylib
*.exe

View file

@ -0,0 +1,58 @@
OBJDIR = ../obj-unix
TEST_UNIT_CFLAGS = $(CFLAGS) -Iinclude $(LDFLAGS) -lcheck $(LIBCHECK_CFLAGS) -Werror -Wdeclaration-after-statement -fsanitize=address -fsanitize=undefined -ftest-coverage -fprofile-arcs -ggdb
TEST_GENERIC_QUEUE = test/queues/test_generic_queue
TEST_GENERIC_QUEUE_SRC = test/queues/test_generic_queue.c queues/generic_queue.c
TEST_LINKED_LIST = test/lists/test_linked_list
TEST_LINKED_LIST_SRC = test/lists/test_linked_list.c lists/linked_list.c
TEST_STDSTRING = test/string/test_stdstring
TEST_STDSTRING_SRC = test/string/test_stdstring.c string/stdstring.c encodings/encoding_utf.c \
compat/compat_strl.c
TEST_UTILS = test/utils/test_utils
TEST_UTILS_SRC = test/utils/test_utils.c utils/md5.c encodings/encoding_crc32.c \
streams/file_stream.c vfs/vfs_implementation.c file/file_path.c \
compat/compat_strl.c time/rtime.c string/stdstring.c encodings/encoding_utf.c
TEST_HASH = test/hash/test_hash
TEST_HASH_SRC = test/hash/test_hash.c hash/lrc_hash.c \
streams/file_stream.c vfs/vfs_implementation.c file/file_path.c \
compat/compat_strl.c time/rtime.c string/stdstring.c encodings/encoding_utf.c
all:
# Build and execute tests in order, to avoid coverage file collision
# string
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_STDSTRING_SRC) -o $(TEST_STDSTRING)
$(TEST_STDSTRING)
lcov -c -d . -o `dirname $(TEST_STDSTRING)`/coverage.info
# utils
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_UTILS_SRC) -o $(TEST_UTILS)
$(TEST_UTILS)
lcov -c -d . -o `dirname $(TEST_UTILS)`/coverage.info
# utils
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_HASH_SRC) -o $(TEST_HASH)
$(TEST_HASH)
lcov -c -d . -o `dirname $(TEST_HASH)`/coverage.info
# list
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_LINKED_LIST_SRC) -o $(TEST_LINKED_LIST)
$(TEST_LINKED_LIST)
lcov -c -d . -o `dirname $(TEST_LINKED_LIST)`/coverage.info
# queue
$(CC) $(TEST_UNIT_CFLAGS) $(TEST_GENERIC_QUEUE_SRC) -o $(TEST_GENERIC_QUEUE)
$(TEST_GENERIC_QUEUE)
lcov -c -d . -o `dirname $(TEST_GENERIC_QUEUE)`/coverage.info
lcov -o test/coverage.info \
-a test/utils/coverage.info \
-a test/string/coverage.info \
-a test/lists/coverage.info \
-a test/queues/coverage.info
genhtml -o test/coverage/ test/coverage.info
clean:
rm -f *.gcda *.gcno

View file

@ -0,0 +1,377 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (audio_mix.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <memalign.h>
#include <retro_environment.h>
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif
#include <retro_miscellaneous.h>
#include <audio/audio_mix.h>
#include <streams/file_stream.h>
#include <audio/conversion/float_to_s16.h>
#include <audio/conversion/s16_to_float.h>
void audio_mix_volume_C(float *out, const float *in, float vol, size_t samples)
{
size_t i;
for (i = 0; i < samples; i++)
out[i] += in[i] * vol;
}
#ifdef __SSE2__
void audio_mix_volume_SSE2(float *out, const float *in, float vol, size_t samples)
{
size_t i, remaining_samples;
__m128 volume = _mm_set1_ps(vol);
for (i = 0; i + 16 <= samples; i += 16, out += 16, in += 16)
{
unsigned j;
__m128 input[4];
__m128 additive[4];
input[0] = _mm_loadu_ps(out + 0);
input[1] = _mm_loadu_ps(out + 4);
input[2] = _mm_loadu_ps(out + 8);
input[3] = _mm_loadu_ps(out + 12);
additive[0] = _mm_mul_ps(volume, _mm_loadu_ps(in + 0));
additive[1] = _mm_mul_ps(volume, _mm_loadu_ps(in + 4));
additive[2] = _mm_mul_ps(volume, _mm_loadu_ps(in + 8));
additive[3] = _mm_mul_ps(volume, _mm_loadu_ps(in + 12));
for (j = 0; j < 4; j++)
_mm_storeu_ps(out + 4 * j, _mm_add_ps(input[j], additive[j]));
}
remaining_samples = samples - i;
for (i = 0; i < remaining_samples; i++)
out[i] += in[i] * vol;
}
#endif
void audio_mix_free_chunk(audio_chunk_t *chunk)
{
if (!chunk)
return;
#ifdef HAVE_RWAV
if (chunk->rwav && chunk->rwav->samples)
{
/* rwav_free only frees the samples */
rwav_free(chunk->rwav);
free(chunk->rwav);
}
#endif
if (chunk->buf)
free(chunk->buf);
if (chunk->upsample_buf)
memalign_free(chunk->upsample_buf);
if (chunk->float_buf)
memalign_free(chunk->float_buf);
if (chunk->float_resample_buf)
memalign_free(chunk->float_resample_buf);
if (chunk->resample_buf)
memalign_free(chunk->resample_buf);
if (chunk->resampler && chunk->resampler_data)
chunk->resampler->free(chunk->resampler_data);
free(chunk);
}
audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate,
const char *resampler_ident, enum resampler_quality quality)
{
#ifdef HAVE_RWAV
int sample_size;
int64_t len = 0;
void *buf = NULL;
audio_chunk_t *chunk = (audio_chunk_t*)malloc(sizeof(*chunk));
if (!chunk)
return NULL;
chunk->buf = NULL;
chunk->upsample_buf = NULL;
chunk->float_buf = NULL;
chunk->float_resample_buf = NULL;
chunk->resample_buf = NULL;
chunk->len = 0;
chunk->resample_len = 0;
chunk->sample_rate = sample_rate;
chunk->resample = false;
chunk->resampler = NULL;
chunk->resampler_data = NULL;
chunk->ratio = 0.00f;
chunk->rwav = (rwav_t*)malloc(sizeof(rwav_t));
if (!chunk->rwav)
goto error;
chunk->rwav->bitspersample = 0;
chunk->rwav->numchannels = 0;
chunk->rwav->samplerate = 0;
chunk->rwav->numsamples = 0;
chunk->rwav->subchunk2size = 0;
chunk->rwav->samples = NULL;
if (!filestream_read_file(path, &buf, &len))
goto error;
chunk->buf = buf;
chunk->len = len;
if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR)
goto error;
/* numsamples does not know or care about
* multiple channels, but we need space for 2 */
chunk->upsample_buf = (int16_t*)memalign_alloc(128,
chunk->rwav->numsamples * 2 * sizeof(int16_t));
sample_size = chunk->rwav->bitspersample / 8;
if (sample_size == 1)
{
unsigned i;
if (chunk->rwav->numchannels == 1)
{
for (i = 0; i < chunk->rwav->numsamples; i++)
{
uint8_t *sample = (
(uint8_t*)chunk->rwav->samples) + i;
chunk->upsample_buf[i * 2] =
(int16_t)((sample[0] - 128) << 8);
chunk->upsample_buf[(i * 2) + 1] =
(int16_t)((sample[0] - 128) << 8);
}
}
else if (chunk->rwav->numchannels == 2)
{
for (i = 0; i < chunk->rwav->numsamples; i++)
{
uint8_t *sample = (
(uint8_t*)chunk->rwav->samples) +
(i * 2);
chunk->upsample_buf[i * 2] =
(int16_t)((sample[0] - 128) << 8);
chunk->upsample_buf[(i * 2) + 1] =
(int16_t)((sample[1] - 128) << 8);
}
}
}
else if (sample_size == 2)
{
if (chunk->rwav->numchannels == 1)
{
unsigned i;
for (i = 0; i < chunk->rwav->numsamples; i++)
{
int16_t sample = ((int16_t*)
chunk->rwav->samples)[i];
chunk->upsample_buf[i * 2] = sample;
chunk->upsample_buf[(i * 2) + 1] = sample;
}
}
else if (chunk->rwav->numchannels == 2)
memcpy(chunk->upsample_buf, chunk->rwav->samples,
chunk->rwav->subchunk2size);
}
else if (sample_size != 2)
{
/* we don't support any other sample size besides 8 and 16-bit yet */
goto error;
}
if (sample_rate != (int)chunk->rwav->samplerate)
{
chunk->resample = true;
chunk->ratio = (double)sample_rate / chunk->rwav->samplerate;
retro_resampler_realloc(&chunk->resampler_data,
&chunk->resampler,
resampler_ident,
quality,
chunk->ratio);
if (chunk->resampler && chunk->resampler_data)
{
struct resampler_data info;
chunk->float_buf = (float*)memalign_alloc(128,
chunk->rwav->numsamples * 2 *
chunk->ratio * sizeof(float));
/* why is *3 needed instead of just *2? Does the
* sinc driver require more space than we know about? */
chunk->float_resample_buf = (float*)memalign_alloc(128,
chunk->rwav->numsamples * 3 *
chunk->ratio * sizeof(float));
convert_s16_to_float(chunk->float_buf,
chunk->upsample_buf, chunk->rwav->numsamples * 2, 1.0);
info.data_in = (const float*)chunk->float_buf;
info.data_out = chunk->float_resample_buf;
/* a 'frame' consists of two channels, so we set this
* to the number of samples irrespective of channel count */
info.input_frames = chunk->rwav->numsamples;
info.output_frames = 0;
info.ratio = chunk->ratio;
chunk->resampler->process(chunk->resampler_data, &info);
/* number of output_frames does not increase with
* multiple channels, but assume we need space for 2 */
chunk->resample_buf = (int16_t*)memalign_alloc(128,
info.output_frames * 2 * sizeof(int16_t));
chunk->resample_len = info.output_frames;
convert_float_to_s16(chunk->resample_buf,
chunk->float_resample_buf, info.output_frames * 2);
}
}
return chunk;
error:
audio_mix_free_chunk(chunk);
#endif
return NULL;
}
size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
{
if (!chunk)
return 0;
#ifdef HAVE_RWAV
if (chunk->rwav)
{
if (chunk->resample)
return chunk->resample_len;
return chunk->rwav->numsamples;
}
#endif
/* no other filetypes supported yet */
return 0;
}
/**
* audio_mix_get_chunk_sample:
* @chunk : audio chunk instance
* @channel : channel of the sample (0=left, 1=right)
* @index : index of the sample
*
* Get a sample from an audio chunk.
*
* Returns: A signed 16-bit audio sample.
**/
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk,
unsigned channel, size_t index)
{
if (!chunk)
return 0;
#ifdef HAVE_RWAV
if (chunk->rwav)
{
int sample_size = chunk->rwav->bitspersample / 8;
int16_t sample_out = 0;
/* 0 is the first/left channel */
uint8_t *sample = NULL;
if (chunk->resample)
sample = (uint8_t*)chunk->resample_buf +
(sample_size * index * chunk->rwav->numchannels)
+ (channel * sample_size);
else
sample = (uint8_t*)chunk->upsample_buf +
(sample_size * index * chunk->rwav->numchannels)
+ (channel * sample_size);
sample_out = (int16_t)*sample;
return sample_out;
}
#endif
/* no other filetypes supported yet */
return 0;
}
int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
{
if (!chunk)
return 0;
#ifdef HAVE_RWAV
if (chunk->rwav)
{
int16_t *sample;
if (chunk->resample)
sample = chunk->resample_buf;
else
sample = chunk->upsample_buf;
return sample;
}
#endif
return NULL;
}
int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk)
{
if (!chunk)
return 0;
#ifdef HAVE_RWAV
if (chunk->rwav)
return chunk->rwav->numchannels;
#endif
/* don't support other formats yet */
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,178 @@
/* Copyright (C) 2010-2021 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (float_to_s16.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stddef.h>
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif
#include <features/features_cpu.h>
#include <audio/conversion/float_to_s16.h>
#if (defined(__ARM_NEON__) || defined(HAVE_NEON))
static bool float_to_s16_neon_enabled = false;
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
void convert_float_s16_asm(int16_t *out,
const float *in, size_t samples);
#else
#include <arm_neon.h>
#endif
void convert_float_to_s16(int16_t *out,
const float *in, size_t samples)
{
size_t i = 0;
if (float_to_s16_neon_enabled)
{
float gf = (1<<15);
float32x4_t vgf = {gf, gf, gf, gf};
while (samples >= 8)
{
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
size_t aligned_samples = samples & ~7;
if (aligned_samples)
convert_float_s16_asm(out, in, aligned_samples);
out += aligned_samples;
in += aligned_samples;
samples -= aligned_samples;
i = 0;
#else
int16x4x2_t oreg;
int32x4x2_t creg;
float32x4x2_t inreg = vld2q_f32(in);
creg.val[0] = vcvtq_s32_f32(vmulq_f32(inreg.val[0], vgf));
creg.val[1] = vcvtq_s32_f32(vmulq_f32(inreg.val[1], vgf));
oreg.val[0] = vqmovn_s32(creg.val[0]);
oreg.val[1] = vqmovn_s32(creg.val[1]);
vst2_s16(out, oreg);
in += 8;
out += 8;
samples -= 8;
#endif
}
}
for (; i < samples; i++)
{
int32_t val = (int32_t)(in[i] * 0x8000);
out[i] = (val > 0x7FFF) ? 0x7FFF :
(val < -0x8000 ? -0x8000 : (int16_t)val);
}
}
void convert_float_to_s16_init_simd(void)
{
uint64_t cpu = cpu_features_get();
if (cpu & RETRO_SIMD_NEON)
float_to_s16_neon_enabled = true;
}
#else
void convert_float_to_s16(int16_t *out,
const float *in, size_t samples)
{
size_t i = 0;
#if defined(__SSE2__)
__m128 factor = _mm_set1_ps((float)0x8000);
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
{
__m128 input_l = _mm_loadu_ps(in + 0);
__m128 input_r = _mm_loadu_ps(in + 4);
__m128 res_l = _mm_mul_ps(input_l, factor);
__m128 res_r = _mm_mul_ps(input_r, factor);
__m128i ints_l = _mm_cvtps_epi32(res_l);
__m128i ints_r = _mm_cvtps_epi32(res_r);
__m128i packed = _mm_packs_epi32(ints_l, ints_r);
_mm_storeu_si128((__m128i *)out, packed);
}
samples = samples - i;
i = 0;
#elif defined(__ALTIVEC__)
int samples_in = samples;
/* Unaligned loads/store is a bit expensive,
* so we optimize for the good path (very likely). */
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
{
size_t i;
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
{
vector float input0 = vec_ld( 0, in);
vector float input1 = vec_ld(16, in);
vector signed int result0 = vec_cts(input0, 15);
vector signed int result1 = vec_cts(input1, 15);
vec_st(vec_packs(result0, result1), 0, out);
}
samples_in -= i;
}
samples = samples_in;
i = 0;
#elif defined(_MIPS_ARCH_ALLEGREX)
#ifdef DEBUG
/* Make sure the buffers are 16 byte aligned, this should be
* the default behaviour of malloc in the PSPSDK.
* Assume alignment. */
retro_assert(((uintptr_t)in & 0xf) == 0);
retro_assert(((uintptr_t)out & 0xf) == 0);
#endif
for (i = 0; i + 8 <= samples; i += 8)
{
__asm__ (
".set push \n"
".set noreorder \n"
"lv.q c100, 0(%0) \n"
"lv.q c110, 16(%0) \n"
"vf2in.q c100, c100, 31 \n"
"vf2in.q c110, c110, 31 \n"
"vi2s.q c100, c100 \n"
"vi2s.q c102, c110 \n"
"sv.q c100, 0(%1) \n"
".set pop \n"
:: "r"(in + i), "r"(out + i));
}
#endif
for (; i < samples; i++)
{
int32_t val = (int32_t)(in[i] * 0x8000);
out[i] = (val > 0x7FFF)
? 0x7FFF
: (val < -0x8000 ? -0x8000 : (int16_t)val);
}
}
void convert_float_to_s16_init_simd(void) { }
#endif

View file

@ -0,0 +1,69 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (float_to_s16_neon.S).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
#ifndef __MACH__
.arm
#endif
.align 4
.globl convert_float_s16_asm
#ifndef __MACH__
.type convert_float_s16_asm, %function
#endif
.globl _convert_float_s16_asm
#ifndef __MACH__
.type _convert_float_s16_asm, %function
#endif
# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)
convert_float_s16_asm:
_convert_float_s16_asm:
# Hacky way to get a constant of 2^15.
# ((2^4)^2)^2 * 0.5 = 2^15
vmov.f32 q8, #16.0
vmov.f32 q9, #0.5
vmul.f32 q8, q8, q8
vmul.f32 q8, q8, q8
vmul.f32 q8, q8, q9
1:
# Preload here?
vld1.f32 {q0-q1}, [r1]!
vmul.f32 q0, q0, q8
vmul.f32 q1, q1, q8
vcvt.s32.f32 q0, q0
vcvt.s32.f32 q1, q1
vqmovn.s32 d4, q0
vqmovn.s32 d5, q1
vst1.f32 {d4-d5}, [r0]!
# Guaranteed to get samples in multiples of 8.
subs r2, r2, #8
bne 1b
bx lr
#endif

View file

@ -0,0 +1,64 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (float_to_s16_neon.S).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
#if defined(__thumb__)
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
#else
#define DECL_ARMMODE(x) " .align 4\n" " .global " x "\n" " .arm\n" x ":\n"
#endif
asm(
DECL_ARMMODE("convert_float_s16_asm")
DECL_ARMMODE("_convert_float_s16_asm")
"# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)\n"
" # Hacky way to get a constant of 2^15.\n"
" # ((2^4)^2)^2 * 0.5 = 2^15\n"
" vmov.f32 q8, #16.0\n"
" vmov.f32 q9, #0.5\n"
" vmul.f32 q8, q8, q8\n"
" vmul.f32 q8, q8, q8\n"
" vmul.f32 q8, q8, q9\n"
"\n"
"1:\n"
" # Preload here?\n"
" vld1.f32 {q0-q1}, [r1]!\n"
"\n"
" vmul.f32 q0, q0, q8\n"
" vmul.f32 q1, q1, q8\n"
"\n"
" vcvt.s32.f32 q0, q0\n"
" vcvt.s32.f32 q1, q1\n"
"\n"
" vqmovn.s32 d4, q0\n"
" vqmovn.s32 d5, q1\n"
"\n"
" vst1.f32 {d4-d5}, [r0]!\n"
"\n"
" # Guaranteed to get samples in multiples of 8.\n"
" subs r2, r2, #8\n"
" bne 1b\n"
"\n"
" bx lr\n"
"\n"
);
#endif

View file

@ -0,0 +1,205 @@
/* Copyright (C) 2010-2021 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (s16_to_float.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif
#include <boolean.h>
#include <features/features_cpu.h>
#include <audio/conversion/s16_to_float.h>
#if (defined(__ARM_NEON__) || defined(HAVE_NEON))
static bool s16_to_float_neon_enabled = false;
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
/* Avoid potential hard-float/soft-float ABI issues. */
void convert_s16_float_asm(float *out, const int16_t *in,
size_t samples, const float *gain);
#else
#include <arm_neon.h>
#endif
void convert_s16_to_float(float *out,
const int16_t *in, size_t samples, float gain)
{
unsigned i = 0;
if (s16_to_float_neon_enabled)
{
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
size_t aligned_samples = samples & ~7;
if (aligned_samples)
convert_s16_float_asm(out, in, aligned_samples, &gain);
/* Could do all conversion in ASM, but keep it simple for now. */
out += aligned_samples;
in += aligned_samples;
samples -= aligned_samples;
i = 0;
#else
float gf = gain / (1 << 15);
float32x4_t vgf = {gf, gf, gf, gf};
while (samples >= 8)
{
float32x4x2_t oreg;
int16x4x2_t inreg = vld2_s16(in);
int32x4_t p1 = vmovl_s16(inreg.val[0]);
int32x4_t p2 = vmovl_s16(inreg.val[1]);
oreg.val[0] = vmulq_f32(vcvtq_f32_s32(p1), vgf);
oreg.val[1] = vmulq_f32(vcvtq_f32_s32(p2), vgf);
vst2q_f32(out, oreg);
in += 8;
out += 8;
samples -= 8;
}
#endif
}
gain /= 0x8000;
for (; i < samples; i++)
out[i] = (float)in[i] * gain;
}
void convert_s16_to_float_init_simd(void)
{
uint64_t cpu = cpu_features_get();
if (cpu & RETRO_SIMD_NEON)
s16_to_float_neon_enabled = true;
}
#else
void convert_s16_to_float(float *out,
const int16_t *in, size_t samples, float gain)
{
unsigned i = 0;
#if defined(__SSE2__)
float fgain = gain / UINT32_C(0x80000000);
__m128 factor = _mm_set1_ps(fgain);
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
{
__m128i input = _mm_loadu_si128((const __m128i *)in);
__m128i regs_l = _mm_unpacklo_epi16(_mm_setzero_si128(), input);
__m128i regs_r = _mm_unpackhi_epi16(_mm_setzero_si128(), input);
__m128 output_l = _mm_mul_ps(_mm_cvtepi32_ps(regs_l), factor);
__m128 output_r = _mm_mul_ps(_mm_cvtepi32_ps(regs_r), factor);
_mm_storeu_ps(out + 0, output_l);
_mm_storeu_ps(out + 4, output_r);
}
samples = samples - i;
i = 0;
#elif defined(__ALTIVEC__)
size_t samples_in = samples;
/* Unaligned loads/store is a bit expensive, so we
* optimize for the good path (very likely). */
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
{
const vector float gain_vec = { gain, gain , gain, gain };
const vector float zero_vec = { 0.0f, 0.0f, 0.0f, 0.0f};
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
{
vector signed short input = vec_ld(0, in);
vector signed int hi = vec_unpackh(input);
vector signed int lo = vec_unpackl(input);
vector float out_hi = vec_madd(vec_ctf(hi, 15), gain_vec, zero_vec);
vector float out_lo = vec_madd(vec_ctf(lo, 15), gain_vec, zero_vec);
vec_st(out_hi, 0, out);
vec_st(out_lo, 16, out);
}
samples_in -= i;
}
samples = samples_in;
i = 0;
#endif
gain /= 0x8000;
#if defined(_MIPS_ARCH_ALLEGREX)
#ifdef DEBUG
/* Make sure the buffer is 16 byte aligned, this should be the
* default behaviour of malloc in the PSPSDK.
* Only the output buffer can be assumed to be 16-byte aligned. */
retro_assert(((uintptr_t)out & 0xf) == 0);
#endif
__asm__ (
".set push \n"
".set noreorder \n"
"mtv %0, s200 \n"
".set pop \n"
::"r"(gain));
for (i = 0; i + 16 <= samples; i += 16)
{
__asm__ (
".set push \n"
".set noreorder \n"
"lv.s s100, 0(%0) \n"
"lv.s s101, 4(%0) \n"
"lv.s s110, 8(%0) \n"
"lv.s s111, 12(%0) \n"
"lv.s s120, 16(%0) \n"
"lv.s s121, 20(%0) \n"
"lv.s s130, 24(%0) \n"
"lv.s s131, 28(%0) \n"
"vs2i.p c100, c100 \n"
"vs2i.p c110, c110 \n"
"vs2i.p c120, c120 \n"
"vs2i.p c130, c130 \n"
"vi2f.q c100, c100, 16 \n"
"vi2f.q c110, c110, 16 \n"
"vi2f.q c120, c120, 16 \n"
"vi2f.q c130, c130, 16 \n"
"vmscl.q e100, e100, s200 \n"
"sv.q c100, 0(%1) \n"
"sv.q c110, 16(%1) \n"
"sv.q c120, 32(%1) \n"
"sv.q c130, 48(%1) \n"
".set pop \n"
:: "r"(in + i), "r"(out + i));
}
#endif
for (; i < samples; i++)
out[i] = (float)in[i] * gain;
}
void convert_s16_to_float_init_simd(void) { }
#endif

View file

@ -0,0 +1,76 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (s16_to_float_neon.S).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
#ifndef __MACH__
.arm
#endif
.align 4
.globl convert_s16_float_asm
#ifndef __MACH__
.type convert_s16_float_asm, %function
#endif
.globl _convert_s16_float_asm
#ifndef __MACH__
.type _convert_s16_float_asm, %function
#endif
# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)
convert_s16_float_asm:
_convert_s16_float_asm:
# Hacky way to get a constant of 2^-15.
# Might be faster to just load a constant from memory.
# It's just done once however ...
vmov.f32 q8, #0.25
vmul.f32 q8, q8, q8
vmul.f32 q8, q8, q8
vmul.f32 q8, q8, q8
vadd.f32 q8, q8, q8
# Apply gain
vld1.f32 {d6[0]}, [r3]
vmul.f32 q8, q8, d6[0]
1:
# Preload here?
vld1.s16 {q0}, [r1]!
# Widen to 32-bit
vmovl.s16 q1, d0
vmovl.s16 q2, d1
# Convert to float
vcvt.f32.s32 q1, q1
vcvt.f32.s32 q2, q2
vmul.f32 q1, q1, q8
vmul.f32 q2, q2, q8
vst1.f32 {q1-q2}, [r0]!
# Guaranteed to get samples in multiples of 8.
subs r2, r2, #8
bne 1b
bx lr
#endif

View file

@ -0,0 +1,71 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (s16_to_float_neon.S).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
#if defined(__thumb__)
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
#else
#define DECL_ARMMODE(x) " .align 4\n" " .global " x "\n" " .arm\n" x ":\n"
#endif
asm(
DECL_ARMMODE("convert_s16_float_asm")
DECL_ARMMODE("_convert_s16_float_asm")
"# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)\n"
" # Hacky way to get a constant of 2^-15.\n"
" # Might be faster to just load a constant from memory.\n"
" # It's just done once however ...\n"
" vmov.f32 q8, #0.25\n"
" vmul.f32 q8, q8, q8\n"
" vmul.f32 q8, q8, q8\n"
" vmul.f32 q8, q8, q8\n"
" vadd.f32 q8, q8, q8\n"
"\n"
" # Apply gain\n"
" vld1.f32 {d6[0]}, [r3]\n"
" vmul.f32 q8, q8, d6[0]\n"
"\n"
"1:\n"
" # Preload here?\n"
" vld1.s16 {q0}, [r1]!\n"
"\n"
" # Widen to 32-bit\n"
" vmovl.s16 q1, d0\n"
" vmovl.s16 q2, d1\n"
"\n"
" # Convert to float\n"
" vcvt.f32.s32 q1, q1\n"
" vcvt.f32.s32 q2, q2\n"
"\n"
" vmul.f32 q1, q1, q8\n"
" vmul.f32 q2, q2, q8\n"
"\n"
" vst1.f32 {q1-q2}, [r0]!\n"
"\n"
" # Guaranteed to get samples in multiples of 8.\n"
" subs r2, r2, #8\n"
" bne 1b\n"
"\n"
" bx lr\n"
"\n"
);
#endif

View file

@ -0,0 +1,324 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (dsp_filter.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <retro_miscellaneous.h>
#include <compat/posix_string.h>
#include <dynamic/dylib.h>
#include <file/file_path.h>
#include <file/config_file_userdata.h>
#include <features/features_cpu.h>
#include <lists/string_list.h>
#include <string/stdstring.h>
#include <libretro_dspfilter.h>
#include <audio/dsp_filter.h>
struct retro_dsp_plug
{
#ifdef HAVE_DYLIB
dylib_t lib;
#endif
const struct dspfilter_implementation *impl;
};
struct retro_dsp_instance
{
const struct dspfilter_implementation *impl;
void *impl_data;
};
struct retro_dsp_filter
{
config_file_t *conf;
struct retro_dsp_plug *plugs;
unsigned num_plugs;
struct retro_dsp_instance *instances;
unsigned num_instances;
};
static const struct dspfilter_implementation *find_implementation(
retro_dsp_filter_t *dsp, const char *ident)
{
unsigned i;
for (i = 0; i < dsp->num_plugs; i++)
{
if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
return dsp->plugs[i].impl;
}
return NULL;
}
static const struct dspfilter_config dspfilter_config = {
config_userdata_get_float,
config_userdata_get_int,
config_userdata_get_float_array,
config_userdata_get_int_array,
config_userdata_get_string,
config_userdata_free,
};
static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate)
{
unsigned i;
struct retro_dsp_instance *instances = NULL;
unsigned filters = 0;
if (!config_get_uint(dsp->conf, "filters", &filters))
return false;
instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances));
if (!instances)
return false;
dsp->instances = instances;
dsp->num_instances = filters;
for (i = 0; i < filters; i++)
{
struct config_file_userdata userdata;
struct dspfilter_info info;
char key[64];
char name[64];
key[0] = name[0] = '\0';
info.input_rate = sample_rate;
snprintf(key, sizeof(key), "filter%u", i);
if (!config_get_array(dsp->conf, key, name, sizeof(name)))
return false;
dsp->instances[i].impl = find_implementation(dsp, name);
if (!dsp->instances[i].impl)
return false;
userdata.conf = dsp->conf;
/* Index-specific configs take priority over ident-specific. */
userdata.prefix[0] = key;
userdata.prefix[1] = dsp->instances[i].impl->short_ident;
dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
&dspfilter_config, &userdata);
if (!dsp->instances[i].impl_data)
return false;
}
return true;
}
#if defined(HAVE_FILTERS_BUILTIN)
extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
static const dspfilter_get_implementation_t dsp_plugs_builtin[] = {
panning_dspfilter_get_implementation,
iir_dspfilter_get_implementation,
echo_dspfilter_get_implementation,
phaser_dspfilter_get_implementation,
wahwah_dspfilter_get_implementation,
eq_dspfilter_get_implementation,
chorus_dspfilter_get_implementation,
};
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
{
unsigned i;
dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
struct retro_dsp_plug *plugs = (struct retro_dsp_plug*)
calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs));
if (!plugs)
return false;
dsp->plugs = plugs;
dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);
for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
{
dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
if (!dsp->plugs[i].impl)
return false;
}
return true;
}
#elif defined(HAVE_DYLIB)
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
{
unsigned i;
dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
unsigned list_size = list ? (unsigned)list->size : 0;
for (i = 0; i < list_size; i++)
{
dspfilter_get_implementation_t cb;
const struct dspfilter_implementation *impl = NULL;
struct retro_dsp_plug *new_plugs = NULL;
dylib_t lib =
dylib_load(list->elems[i].data);
if (!lib)
continue;
cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
if (!cb)
{
dylib_close(lib);
continue;
}
impl = cb(mask);
if (!impl)
{
dylib_close(lib);
continue;
}
if (impl->api_version != DSPFILTER_API_VERSION)
{
dylib_close(lib);
continue;
}
new_plugs = (struct retro_dsp_plug*)
realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
if (!new_plugs)
{
dylib_close(lib);
return false;
}
/* Found plug. */
dsp->plugs = new_plugs;
dsp->plugs[dsp->num_plugs].lib = lib;
dsp->plugs[dsp->num_plugs].impl = impl;
dsp->num_plugs++;
}
return true;
}
#endif
retro_dsp_filter_t *retro_dsp_filter_new(
const char *filter_config,
void *string_data,
float sample_rate)
{
config_file_t *conf = NULL;
struct string_list *plugs = NULL;
retro_dsp_filter_t *dsp = (retro_dsp_filter_t*)calloc(1, sizeof(*dsp));
if (!dsp)
return NULL;
if (!(conf = config_file_new_from_path_to_string(filter_config)))
goto error;
dsp->conf = conf;
if (string_data)
plugs = (struct string_list*)string_data;
#if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
if (!append_plugs(dsp, plugs))
goto error;
#endif
if (plugs)
string_list_free(plugs);
plugs = NULL;
if (!create_filter_graph(dsp, sample_rate))
goto error;
return dsp;
error:
if (plugs)
string_list_free(plugs);
retro_dsp_filter_free(dsp);
return NULL;
}
void retro_dsp_filter_free(retro_dsp_filter_t *dsp)
{
unsigned i;
if (!dsp)
return;
for (i = 0; i < dsp->num_instances; i++)
{
if (dsp->instances[i].impl_data && dsp->instances[i].impl)
dsp->instances[i].impl->free(dsp->instances[i].impl_data);
}
free(dsp->instances);
#ifdef HAVE_DYLIB
for (i = 0; i < dsp->num_plugs; i++)
{
if (dsp->plugs[i].lib)
dylib_close(dsp->plugs[i].lib);
}
free(dsp->plugs);
#endif
if (dsp->conf)
config_file_free(dsp->conf);
free(dsp);
}
void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
struct retro_dsp_data *data)
{
unsigned i;
struct dspfilter_output output = {0};
struct dspfilter_input input = {0};
output.samples = data->input;
output.frames = data->input_frames;
for (i = 0; i < dsp->num_instances; i++)
{
input.samples = output.samples;
input.frames = output.frames;
dsp->instances[i].impl->process(
dsp->instances[i].impl_data, &output, &input);
}
data->output = output.samples;
data->output_frames = output.frames;
}

View file

@ -0,0 +1,11 @@
filters = 2
filter0 = iir
filter1 = panning
iir_gain = 10.0
iir_type = BBOOST
iir_frequency = 200.0
# Avoids clipping.
panning_left_mix = "0.3 0.0"
panning_right_mix = "0.0 0.3"

View file

@ -0,0 +1,7 @@
filters = 1
filter0 = iir
iir_frequency = 8600.0
iir_quality = 0.707
iir_gain = 6.0
iir_type = LPF

View file

@ -0,0 +1,22 @@
filters = 4
filter0 = eq
filter1 = reverb
filter2 = iir
filter3 = panning
eq_frequencies = "32 64 125 250 500 1000 2000 4000 8000 16000 20000"
eq_gains = "6 9 12 7 6 5 7 9 11 6 0"
# Reverb - slight reverb
reverb_drytime = 0.5
reverb_wettime = 0.15
reverb_damping = 0.8
reverb_roomwidth = 0.25
reverb_roomsize = 0.25
# IIR - filters out some harsh sounds on the upper end
iir_type = RIAA_CD
# Panning - cut the volume a bit
panning_left_mix = "0.75 0.0"
panning_right_mix = "0.0 0.75"

View file

@ -0,0 +1,14 @@
filters = 1
filter0 = chorus
# Controls the base delay of the chorus (milliseconds).
# chorus_delay_ms = 25.0
#
# Controls the depth of the delay. The delay will vary between delay_ms +/- depth_ms.
# chorus_depth_ms = 1.0
#
# Frequency of LFO which controls delay.
# chorus_lfo_freq = 0.5
#
# Controls dry/wet-ness of effect. 1.0 = full chorus, 0.0 = no chorus.
# chorus_drywet = 0.8

View file

@ -0,0 +1,4 @@
filters = 1
filter0 = crystalizer
# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.
crystalizer_intensity = 5.0

View file

@ -0,0 +1,41 @@
filters = 1
filter0 = eq
# Defaults
# Beta factor for Kaiser window.
# Lower values will allow better frequency resolution, but more ripple.
# eq_window_beta = 4.0
# The block size on which FFT is done.
# Too high value requires more processing as well as longer latency but
# allows finer-grained control over the spectrum.
# eq_block_size_log2 = 8
# An array of which frequencies to control.
# You can create an arbitrary amount of these sampling points.
# The EQ will try to create a frequency response which fits well to these points.
# The filter response is linearly interpolated between sampling points here.
#
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
#
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
# eq_frequencies = "500 1000 2000"
# eq_gains = "0 3 0"
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
# By default, this filter has a flat frequency response.
# Dumps the impulse response generated by the EQ as a plain-text file
# with one coefficient per line.
# eq_impulse_response_output = "eq_impulse.txt"
#
# Using GNU Octave or Matlab, you can plot the response with:
#
# f = fopen('/path/to/eq_impulse.txt');
# l = textscan(f, '%f');
# res = l{1};
# freqz(res, 1, 4096, 48000);
#
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch

View file

@ -0,0 +1,19 @@
filters = 1
filter0 = echo
# Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) and feedback factors.
# Echo output from all channels can be fed back into each other to create a somewhat reverb-like effect if desired.
# Defaults, 200 ms delay echo with feedback:
# Delay in ms. Takes an array with multiple channels.
# echo_delay = "200"
# Feedback factor for echo.
# echo_feedback = "0.5"
# Overall echo amplification. If too high, the echo becomes unstable due to feedback.
# echo_amp = "0.2"
# Reverby preset.
# echo_delay = " 60 80 120 172 200 320 380"
# echo_feedback = "0.5 0.5 0.4 0.3 0.5 0.3 0.2"
# echo_amp = "0.12"

View file

@ -0,0 +1,12 @@
filters = 2
filter0 = echo
filter1 = reverb
echo_delay = "200"
echo_feedback = "0.6"
echo_amp = "0.25"
reverb_roomwidth = 0.75
reverb_roomsize = 0.75
reverb_damping = 1.0
reverb_wettime = 0.3

View file

@ -0,0 +1,6 @@
filters = 1
filter0 = iir
iir_gain = -12.0
iir_type = HSH
iir_frequency = 8000.0

View file

@ -0,0 +1,22 @@
filters = 1
filter0 = iir
# Defaults.
#iir_frequency = 1024.0
#iir_quality = 0.707
#iir_gain = 0.0
#iir_type = LPF
# Filter types:
# LPF: Low-pass
# HPF: High-pass
# BPCSGF: Band-pass #1
# BPZPGF: Band-pass #2
# APF: Allpass
# NOTCH: Notch filter
# RIAA_phono: RIAA record/tape deemphasis
# PEQ: peaking band EQ
# BBOOST: Bassboost
# LSH: Low-shelf
# HSH: High-shelf
# RIAA_CD: CD de-emphasis

View file

@ -0,0 +1,47 @@
filters = 1
filter0 = eq
eq_frequencies = "8000 10000 12500 16000 20000"
eq_gains = "0 -30 -30 -30 -30"
# Low pass filter for the QSound chip from CPS-1/2.
# Some games have aliasing due low quality samples, so you can hear some annoying noisy near 11 kHz
# Defaults
# Beta factor for Kaiser window.
# Lower values will allow better frequency resolution, but more ripple.
# eq_window_beta = 4.0
# The block size on which FFT is done.
# Too high value requires more processing as well as longer latency but
# allows finer-grained control over the spectrum.
# eq_block_size_log2 = 8
# An array of which frequencies to control.
# You can create an arbitrary amount of these sampling points.
# The EQ will try to create a frequency response which fits well to these points.
# The filter response is linearly interpolated between sampling points here.
#
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
#
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
# eq_frequencies = "500 1000 2000"
# eq_gains = "0 3 0"
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
# By default, this filter has a low pass response with cuttof frequency at ~8600 Hz.
# Dumps the impulse response generated by the EQ as a plain-text file
# with one coefficient per line.
# eq_impulse_response_output = "eq_impulse.txt"
#
# Using GNU Octave or Matlab, you can plot the response with:
#
# f = fopen('/path/to/eq_impulse.txt');
# l = textscan(f, '%f');
# res = l{1};
# freqz(res, 1, 4096, 48000);
#
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch

View file

@ -0,0 +1,135 @@
compiler := gcc
extra_flags :=
use_neon := 0
build = release
DYLIB := so
PREFIX := /usr
INSTALLDIR := $(PREFIX)/lib/retroarch/filters/audio
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -s),)
platform = win
else ifneq ($(findstring Darwin,$(shell uname -s)),)
platform = osx
arch = intel
ifeq ($(shell uname -p),powerpc)
arch = ppc
endif
else ifneq ($(findstring MINGW,$(shell uname -s)),)
platform = win
endif
endif
ifeq ($(platform),gcc)
extra_rules_gcc := $(shell $(compiler) -dumpmachine)
endif
ifneq (,$(findstring armv7,$(extra_rules_gcc)))
extra_flags += -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon
use_neon := 1
endif
ifneq (,$(findstring hardfloat,$(extra_rules_gcc)))
extra_flags += -mfloat-abi=hard
endif
ifeq (release,$(build))
extra_flags += -O2
endif
ifeq (debug,$(build))
extra_flags += -O0 -g
endif
ldflags := $(LDFLAGS) -shared -lm -Wl,--version-script=link.T
ifeq ($(platform), unix)
DYLIB = so
else ifeq ($(platform), osx)
compiler := $(CC)
DYLIB = dylib
ldflags := -dynamiclib
ARCHFLAGS=
MINVERFLAGS=
ifeq ($(shell uname -p),arm)
MINVERFLAGS = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS (Metal, ARM 64bit)
else ifeq ($(HAVE_METAL),1)
MINVERFLAGS = -mmacosx-version-min=10.13 -stdlib=libc++ # macOS (Metal, x86 64bit)
else ifeq ($(shell uname -p),powerpc)
MINVERFLAGS = -mmacosx-version-min=10.5 # macOSX (PowerPC 32-bit)
else ifeq ($(shell uname -m),i386)
MINVERFLAGS = -mmacosx-version-min=10.6 # macOSX (OpenGL, x86 32bit)
else
MINVERFLAGS = -mmacosx-version-min=10.7 -stdlib=libc++ # macOSX (OpenGL, x86 64bit)
endif
# Build for a specific architecture when ARCH is defined as a switch
ifeq ($(ARCH),arm64)
MINVERFLAGS = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS (Metal, ARM 64bit)
ARCHFLAGS = -arch arm64
else ifeq ($(ARCH),x86_64)
ifeq ($(HAVE_METAL),1)
MINVERFLAGS = -mmacosx-version-min=10.13 -stdlib=libc++
else
MINVERFLAGS = -mmacosx-version-min=10.7 -stdlib=libc++
endif
ARCHFLAGS = -arch x86_64
else ifeq ($(ARCH),x86)
MINVERFLAGS = -mmacosx-version-min=10.6
ARCHFLAGS = -arch x86
else ifeq ($(ARCH),ppc)
MINVERFLAGS = -mmacosx-version-min=10.5
ARCHFLAGS = -arch ppc
endif
ifeq ($(BUILDBOT),1)
ARCHFLAGS = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT)
endif
extraflags += $(MINVERFLAGS) $(ARCHFLAGS)
ldflags += $(MINVERFLAGS) $(ARCHFLAGS)
else
extra_flags += -static-libgcc -static-libstdc++
DYLIB = dll
endif
CC := $(compiler) -Wall
CXX := $(subst CC,++,$(compiler)) -std=gnu++0x -Wall
flags := $(CPPFLAGS) $(CFLAGS) -fPIC $(extra_flags) -I../../include
asflags := $(ASFLAGS) -fPIC $(extra_flags)
objects :=
ifeq (1,$(use_neon))
ASMFLAGS := -INEON/asm
asflags += -mfpu=neon
endif
plugs := $(wildcard *.c)
objects := $(plugs:.c=.o)
targets := $(objects:.o=.$(DYLIB))
all: build;
%.o: %.S
$(CC) -c -o $@ $(asflags) $(ASMFLAGS) $<
%.o: %.c
$(CC) -c -o $@ $(flags) $<
%.$(DYLIB): %.o
$(CC) -o $@ $(ldflags) $(flags) $^
build: $(targets)
clean:
rm -f *.o
rm -f *.$(DYLIB)
strip:
strip -s *.$(DYLIB)
install:
mkdir -p $(DESTDIR)$(INSTALLDIR)
cp -t $(DESTDIR)$(INSTALLDIR) $(targets) *.dsp
test-install:
DESTDIR=/tmp/build $(MAKE) install

View file

@ -0,0 +1,12 @@
filters = 1
filter0 = panning
# Gains are linear.
# Stereo Mono:
panning_left_mix = "0.5 0.5"
panning_right_mix = "0.5 0.5"
# Mono on one speaker:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.0 0.0"

View file

@ -0,0 +1,22 @@
filters = 1
filter0 = panning
# Gains are linear.
# The default. Left and right channels map to each other.
panning_left_mix = "1.0 0.0"
panning_right_mix = "0.0 1.0"
# Some examples:
#
# Mono:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.5 0.5"
# Swap left and right channels:
# panning_left_mix = "0.0 1.0"
# panning_right_mix = "1.0 0.0"
#
# Mono on one speaker:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.0 0.0"

View file

@ -0,0 +1,10 @@
filters = 1
filter0 = phaser
# Defaults.
# phaser_lfo_freq = 0.4
# phaser_lfo_start_phase = 0.0
# phaser_feedback = 0.0
# phaser_depth = 0.4
# phaser_dry_wet = 0.5
# phaser_stages = 2

View file

@ -0,0 +1,9 @@
filters = 1
filter0 = reverb
# Defaults.
# reverb_drytime = 0.43
# reverb_wettime = 0.4
# reverb_damping = 0.8
# reverb_roomwidth = 0.56
# reverb_roomsize = 0.56

View file

@ -0,0 +1,6 @@
filters = 1
filter0 = tremolo
# Defaults.
#tremolo_frequency = 4.0
#tremolo_depth = 0.9

View file

@ -0,0 +1,6 @@
filters = 1
filter0 = vibrato
# Defaults.
#vibrato_frequency = 5.0
#vibrato_depth = 0.5

View file

@ -0,0 +1,9 @@
filters = 1
filter0 = wahwah
# Defaults.
# wahwah_lfo_freq = 1.5
# wahwah_lfo_start_phase = 0.0
# wahwah_freq_offset = 0.3
# wahwah_depth = 0.7
# wahwah_resonance = 2.5

View file

@ -0,0 +1,160 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (chorus.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#define CHORUS_MAX_DELAY 4096
#define CHORUS_DELAY_MASK (CHORUS_MAX_DELAY - 1)
struct chorus_data
{
float old[2][CHORUS_MAX_DELAY];
unsigned old_ptr;
float delay;
float depth;
float input_rate;
float mix_dry;
float mix_wet;
unsigned lfo_ptr;
unsigned lfo_period;
};
static void chorus_free(void *data)
{
if (data)
free(data);
}
static void chorus_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out = NULL;
struct chorus_data *ch = (struct chorus_data*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
unsigned delay_int;
float delay_frac, l_a, l_b, r_a, r_b;
float chorus_l, chorus_r;
float in[2] = { out[0], out[1] };
float delay = ch->delay + ch->depth * sin((2.0 * M_PI * ch->lfo_ptr++) / ch->lfo_period);
delay *= ch->input_rate;
if (ch->lfo_ptr >= ch->lfo_period)
ch->lfo_ptr = 0;
delay_int = (unsigned)delay;
if (delay_int >= CHORUS_MAX_DELAY - 1)
delay_int = CHORUS_MAX_DELAY - 2;
delay_frac = delay - delay_int;
ch->old[0][ch->old_ptr] = in[0];
ch->old[1][ch->old_ptr] = in[1];
l_a = ch->old[0][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
l_b = ch->old[0][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
r_a = ch->old[1][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
r_b = ch->old[1][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
/* Lerp introduces aliasing of the chorus component,
* but doing full polyphase here is probably overkill. */
chorus_l = l_a * (1.0f - delay_frac) + l_b * delay_frac;
chorus_r = r_a * (1.0f - delay_frac) + r_b * delay_frac;
out[0] = ch->mix_dry * in[0] + ch->mix_wet * chorus_l;
out[1] = ch->mix_dry * in[1] + ch->mix_wet * chorus_r;
ch->old_ptr = (ch->old_ptr + 1) & CHORUS_DELAY_MASK;
}
}
static void *chorus_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float delay, depth, lfo_freq, drywet;
struct chorus_data *ch = (struct chorus_data*)calloc(1, sizeof(*ch));
if (!ch)
return NULL;
config->get_float(userdata, "delay_ms", &delay, 25.0f);
config->get_float(userdata, "depth_ms", &depth, 1.0f);
config->get_float(userdata, "lfo_freq", &lfo_freq, 0.5f);
config->get_float(userdata, "drywet", &drywet, 0.8f);
delay /= 1000.0f;
depth /= 1000.0f;
if (depth > delay)
depth = delay;
if (drywet < 0.0f)
drywet = 0.0f;
else if (drywet > 1.0f)
drywet = 1.0f;
ch->mix_dry = 1.0f - 0.5f * drywet;
ch->mix_wet = 0.5f * drywet;
ch->delay = delay;
ch->depth = depth;
ch->lfo_period = (1.0f / lfo_freq) * info->input_rate;
ch->input_rate = info->input_rate;
if (!ch->lfo_period)
ch->lfo_period = 1;
return ch;
}
static const struct dspfilter_implementation chorus_plug = {
chorus_init,
chorus_process,
chorus_free,
DSPFILTER_API_VERSION,
"Chorus",
"chorus",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation chorus_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &chorus_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,3 @@
#!/bin/sh
PACKAGE_NAME=retroarch-filters-audio

View file

@ -0,0 +1,90 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (echo.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
struct delta_data
{
float intensity;
float old[2];
};
static void delta_free(void *data)
{
free(data);
}
static void delta_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i, c;
struct delta_data *d = (struct delta_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++)
{
for (c = 0; c < 2; c++)
{
float current = *out;
*out++ = current + (current - d->old[c]) * d->intensity;
d->old[c] = current;
}
}
}
static void *delta_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
struct delta_data *d = (struct delta_data*)calloc(1, sizeof(*d));
if (!d)
return NULL;
config->get_float(userdata, "intensity", &d->intensity, 5.0f);
return d;
}
static const struct dspfilter_implementation delta_plug = {
delta_init,
delta_process,
delta_free,
DSPFILTER_API_VERSION,
"Delta Sharpening",
"crystalizer",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation delta_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &delta_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,180 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (echo.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
struct echo_channel
{
float *buffer;
unsigned ptr;
unsigned frames;
float feedback;
};
struct echo_data
{
struct echo_channel *channels;
unsigned num_channels;
float amp;
};
static void echo_free(void *data)
{
unsigned i;
struct echo_data *echo = (struct echo_data*)data;
for (i = 0; i < echo->num_channels; i++)
free(echo->channels[i].buffer);
free(echo->channels);
free(echo);
}
static void echo_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i, c;
float *out = NULL;
struct echo_data *echo = (struct echo_data*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
float left, right;
float echo_left = 0.0f;
float echo_right = 0.0f;
for (c = 0; c < echo->num_channels; c++)
{
echo_left += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0];
echo_right += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1];
}
echo_left *= echo->amp;
echo_right *= echo->amp;
left = out[0] + echo_left;
right = out[1] + echo_right;
for (c = 0; c < echo->num_channels; c++)
{
float feedback_left = out[0] + echo->channels[c].feedback * echo_left;
float feedback_right = out[1] + echo->channels[c].feedback * echo_right;
echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0] = feedback_left;
echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1] = feedback_right;
echo->channels[c].ptr = (echo->channels[c].ptr + 1) % echo->channels[c].frames;
}
out[0] = left;
out[1] = right;
}
}
static void *echo_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
unsigned i, channels;
struct echo_channel *echo_channels = NULL;
float *delay = NULL;
float *feedback = NULL;
unsigned num_delay = 0;
unsigned num_feedback = 0;
static const float default_delay[] = { 200.0f };
static const float default_feedback[] = { 0.5f };
struct echo_data *echo = (struct echo_data*)
calloc(1, sizeof(*echo));
if (!echo)
return NULL;
config->get_float_array(userdata, "delay", &delay,
&num_delay, default_delay, 1);
config->get_float_array(userdata, "feedback", &feedback,
&num_feedback, default_feedback, 1);
config->get_float(userdata, "amp", &echo->amp, 0.2f);
channels = num_feedback = num_delay = MIN(num_delay, num_feedback);
echo_channels = (struct echo_channel*)calloc(channels,
sizeof(*echo_channels));
if (!echo_channels)
goto error;
echo->channels = echo_channels;
echo->num_channels = channels;
for (i = 0; i < channels; i++)
{
unsigned frames = (unsigned)(delay[i] * info->input_rate / 1000.0f + 0.5f);
if (!frames)
goto error;
echo->channels[i].buffer = (float*)calloc(frames, 2 * sizeof(float));
if (!echo->channels[i].buffer)
goto error;
echo->channels[i].frames = frames;
echo->channels[i].feedback = feedback[i];
}
config->free(delay);
config->free(feedback);
return echo;
error:
config->free(delay);
config->free(feedback);
echo_free(echo);
return NULL;
}
static const struct dspfilter_implementation echo_plug = {
echo_init,
echo_process,
echo_free,
DSPFILTER_API_VERSION,
"Multi-Echo",
"echo",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation echo_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &echo_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,352 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (eq.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <retro_inline.h>
#include <retro_miscellaneous.h>
#include <filters.h>
#include <libretro_dspfilter.h>
#include "fft/fft.c"
struct eq_data
{
fft_t *fft;
float buffer[8 * 1024];
float *save;
float *block;
fft_complex_t *filter;
fft_complex_t *fftblock;
unsigned block_size;
unsigned block_ptr;
};
struct eq_gain
{
float freq;
float gain; /* Linear. */
};
static void eq_free(void *data)
{
struct eq_data *eq = (struct eq_data*)data;
if (!eq)
return;
fft_free(eq->fft);
free(eq->save);
free(eq->block);
free(eq->fftblock);
free(eq->filter);
free(eq);
}
static void eq_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
float *out;
const float *in;
unsigned input_frames;
struct eq_data *eq = (struct eq_data*)data;
output->samples = eq->buffer;
output->frames = 0;
out = eq->buffer;
in = input->samples;
input_frames = input->frames;
while (input_frames)
{
unsigned write_avail = eq->block_size - eq->block_ptr;
if (input_frames < write_avail)
write_avail = input_frames;
memcpy(eq->block + eq->block_ptr * 2, in, write_avail * 2 * sizeof(float));
in += write_avail * 2;
input_frames -= write_avail;
eq->block_ptr += write_avail;
// Convolve a new block.
if (eq->block_ptr == eq->block_size)
{
unsigned i, c;
for (c = 0; c < 2; c++)
{
fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2);
for (i = 0; i < 2 * eq->block_size; i++)
eq->fftblock[i] = fft_complex_mul(eq->fftblock[i], eq->filter[i]);
fft_process_inverse(eq->fft, out + c, eq->fftblock, 2);
}
// Overlap add method, so add in saved block now.
for (i = 0; i < 2 * eq->block_size; i++)
out[i] += eq->save[i];
// Save block for later.
memcpy(eq->save, out + 2 * eq->block_size, 2 * eq->block_size * sizeof(float));
out += eq->block_size * 2;
output->frames += eq->block_size;
eq->block_ptr = 0;
}
}
}
static int gains_cmp(const void *a_, const void *b_)
{
const struct eq_gain *a = (const struct eq_gain*)a_;
const struct eq_gain *b = (const struct eq_gain*)b_;
if (a->freq < b->freq)
return -1;
if (a->freq > b->freq)
return 1;
return 0;
}
static void generate_response(fft_complex_t *response,
const struct eq_gain *gains, unsigned num_gains, unsigned samples)
{
unsigned i;
float start_freq = 0.0f;
float start_gain = 1.0f;
float end_freq = 1.0f;
float end_gain = 1.0f;
if (num_gains)
{
end_freq = gains->freq;
end_gain = gains->gain;
num_gains--;
gains++;
}
/* Create a response by linear interpolation between
* known frequency sample points. */
for (i = 0; i <= samples; i++)
{
float gain;
float lerp = 0.5f;
float freq = (float)i / samples;
while (freq >= end_freq)
{
if (num_gains)
{
start_freq = end_freq;
start_gain = end_gain;
end_freq = gains->freq;
end_gain = gains->gain;
gains++;
num_gains--;
}
else
{
start_freq = end_freq;
start_gain = end_gain;
end_freq = 1.0f;
end_gain = 1.0f;
break;
}
}
/* Edge case where i == samples. */
if (end_freq > start_freq)
lerp = (freq - start_freq) / (end_freq - start_freq);
gain = (1.0f - lerp) * start_gain + lerp * end_gain;
response[i].real = gain;
response[i].imag = 0.0f;
response[2 * samples - i].real = gain;
response[2 * samples - i].imag = 0.0f;
}
}
static void create_filter(struct eq_data *eq, unsigned size_log2,
struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path)
{
int i;
int half_block_size = eq->block_size >> 1;
double window_mod = 1.0 / kaiser_window_function(0.0, beta);
fft_t *fft = fft_new(size_log2);
float *time_filter = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter));
if (!fft || !time_filter)
goto end;
/* Make sure bands are in correct order. */
qsort(gains, num_gains, sizeof(*gains), gains_cmp);
/* Compute desired filter response. */
generate_response(eq->filter, gains, num_gains, half_block_size);
/* Get equivalent time-domain filter. */
fft_process_inverse(fft, time_filter, eq->filter, 1);
/* ifftshift() to create the correct linear phase filter.
* The filter response was designed with zero phase, which
* won't work unless we compensate
* for the repeating property of the FFT here
* by flipping left and right blocks. */
for (i = 0; i < half_block_size; i++)
{
float tmp = time_filter[i + half_block_size];
time_filter[i + half_block_size] = time_filter[i];
time_filter[i] = tmp;
}
/* Apply a window to smooth out the frequency repsonse. */
for (i = 0; i < (int)eq->block_size; i++)
{
/* Kaiser window. */
double phase = (double)i / eq->block_size;
phase = 2.0 * (phase - 0.5);
time_filter[i] *= window_mod * kaiser_window_function(phase, beta);
}
#ifdef DEBUG
/* Debugging. */
if (filter_path)
{
FILE *file = fopen(filter_path, "w");
if (file)
{
for (i = 0; i < (int)eq->block_size - 1; i++)
fprintf(file, "%.8f\n", time_filter[i + 1]);
fclose(file);
}
}
#endif
/* Padded FFT to create our FFT filter.
* Make our even-length filter odd by discarding the first coefficient.
* For some interesting reason, this allows us to design an odd-length linear phase filter.
*/
fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1);
end:
fft_free(fft);
free(time_filter);
}
static void *eq_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float *frequencies, *gain;
unsigned num_freq, num_gain, i, size;
int size_log2;
float beta;
struct eq_gain *gains = NULL;
char *filter_path = NULL;
const float default_freq[] = { 0.0f, info->input_rate };
const float default_gain[] = { 0.0f, 0.0f };
struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq));
if (!eq)
return NULL;
config->get_float(userdata, "window_beta", &beta, 4.0f);
config->get_int(userdata, "block_size_log2", &size_log2, 8);
size = 1 << size_log2;
config->get_float_array(userdata, "frequencies", &frequencies, &num_freq, default_freq, 2);
config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2);
if (!config->get_string(userdata, "impulse_response_output", &filter_path, ""))
{
config->free(filter_path);
filter_path = NULL;
}
num_gain = num_freq = MIN(num_gain, num_freq);
gains = (struct eq_gain*)calloc(num_gain, sizeof(*gains));
if (!gains)
goto error;
for (i = 0; i < num_gain; i++)
{
gains[i].freq = frequencies[i] / (0.5f * info->input_rate);
gains[i].gain = pow(10.0, gain[i] / 20.0);
}
config->free(frequencies);
config->free(gain);
eq->block_size = size;
eq->save = (float*)calloc( size, 2 * sizeof(*eq->save));
eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block));
eq->fftblock = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock));
eq->filter = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter));
/* Use an FFT which is twice the block size with zero-padding
* to make circular convolution => proper convolution.
*/
eq->fft = fft_new(size_log2 + 1);
if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter)
goto error;
create_filter(eq, size_log2, gains, num_gain, beta, filter_path);
config->free(filter_path);
filter_path = NULL;
free(gains);
return eq;
error:
free(gains);
eq_free(eq);
return NULL;
}
static const struct dspfilter_implementation eq_plug = {
eq_init,
eq_process,
eq_free,
DSPFILTER_API_VERSION,
"Linear-Phase FFT Equalizer",
"eq",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation eq_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &eq_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,204 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fft.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include "fft.h"
#include <retro_miscellaneous.h>
struct fft
{
fft_complex_t *interleave_buffer;
fft_complex_t *phase_lut;
unsigned *bitinverse_buffer;
unsigned size;
};
static unsigned bitswap(unsigned x, unsigned size_log2)
{
unsigned i;
unsigned ret = 0;
for (i = 0; i < size_log2; i++)
ret |= ((x >> i) & 1) << (size_log2 - i - 1);
return ret;
}
static void build_bitinverse(unsigned *bitinverse, unsigned size_log2)
{
unsigned i;
unsigned size = 1 << size_log2;
for (i = 0; i < size; i++)
bitinverse[i] = bitswap(i, size_log2);
}
static fft_complex_t exp_imag(double phase)
{
fft_complex_t out = { cos(phase), sin(phase) };
return out;
}
static void build_phase_lut(fft_complex_t *out, int size)
{
int i;
out += size;
for (i = -size; i <= size; i++)
out[i] = exp_imag((M_PI * i) / size);
}
static void interleave_complex(const unsigned *bitinverse,
fft_complex_t *out, const fft_complex_t *in,
unsigned samples, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in += step)
out[bitinverse[i]] = *in;
}
static void interleave_float(const unsigned *bitinverse,
fft_complex_t *out, const float *in,
unsigned samples, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in += step)
{
unsigned inv_i = bitinverse[i];
out[inv_i].real = *in;
out[inv_i].imag = 0.0f;
}
}
static void resolve_float(float *out, const fft_complex_t *in, unsigned samples,
float gain, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in++, out += step)
*out = gain * in->real;
}
fft_t *fft_new(unsigned block_size_log2)
{
unsigned size;
fft_t *fft = (fft_t*)calloc(1, sizeof(*fft));
if (!fft)
return NULL;
size = 1 << block_size_log2;
fft->interleave_buffer = (fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer));
fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer));
fft->phase_lut = (fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut));
if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut)
goto error;
fft->size = size;
build_bitinverse(fft->bitinverse_buffer, block_size_log2);
build_phase_lut(fft->phase_lut, size);
return fft;
error:
fft_free(fft);
return NULL;
}
void fft_free(fft_t *fft)
{
if (!fft)
return;
free(fft->interleave_buffer);
free(fft->bitinverse_buffer);
free(fft->phase_lut);
free(fft);
}
static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod)
{
mod = fft_complex_mul(mod, *b);
*b = fft_complex_sub(*a, mod);
*a = fft_complex_add(*a, mod);
}
static void butterflies(fft_complex_t *butterfly_buf,
const fft_complex_t *phase_lut,
int phase_dir, unsigned step_size, unsigned samples)
{
unsigned i, j;
for (i = 0; i < samples; i += step_size << 1)
{
int phase_step = (int)samples * phase_dir / (int)step_size;
for (j = i; j < i + step_size; j++)
butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size],
phase_lut[phase_step * (int)(j - i)]);
}
}
void fft_process_forward_complex(fft_t *fft,
fft_complex_t *out, const fft_complex_t *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_complex(fft->bitinverse_buffer, out, in, samples, step);
for (step_size = 1; step_size < samples; step_size <<= 1)
{
butterflies(out,
fft->phase_lut + samples,
-1, step_size, samples);
}
}
void fft_process_forward(fft_t *fft,
fft_complex_t *out, const float *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_float(fft->bitinverse_buffer, out, in, samples, step);
for (step_size = 1; step_size < fft->size; step_size <<= 1)
{
butterflies(out,
fft->phase_lut + samples,
-1, step_size, samples);
}
}
void fft_process_inverse(fft_t *fft,
float *out, const fft_complex_t *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer,
in, samples, 1);
for (step_size = 1; step_size < samples; step_size <<= 1)
{
butterflies(fft->interleave_buffer,
fft->phase_lut + samples,
1, step_size, samples);
}
resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples, step);
}

View file

@ -0,0 +1,44 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fft.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef RARCH_FFT_H__
#define RARCH_FFT_H__
#include <retro_inline.h>
#include <math/complex.h>
typedef struct fft fft_t;
fft_t *fft_new(unsigned block_size_log2);
void fft_free(fft_t *fft);
void fft_process_forward_complex(fft_t *fft,
fft_complex_t *out, const fft_complex_t *in, unsigned step);
void fft_process_forward(fft_t *fft,
fft_complex_t *out, const float *in, unsigned step);
void fft_process_inverse(fft_t *fft,
float *out, const fft_complex_t *in, unsigned step);
#endif

View file

@ -0,0 +1,371 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (iir.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#include <string/stdstring.h>
#define sqr(a) ((a) * (a))
/* filter types */
enum IIRFilter
{
LPF, /* low pass filter */
HPF, /* High pass filter */
BPCSGF, /* band pass filter 1 */
BPZPGF, /* band pass filter 2 */
APF, /* Allpass filter*/
NOTCH, /* Notch Filter */
RIAA_phono, /* RIAA record/tape deemphasis */
PEQ, /* Peaking band EQ filter */
BBOOST, /* Bassboost filter */
LSH, /* Low shelf filter */
HSH, /* High shelf filter */
RIAA_CD /* CD de-emphasis */
};
struct iir_data
{
float b0, b1, b2;
float a0, a1, a2;
struct
{
float xn1, xn2;
float yn1, yn2;
} l, r;
};
static void iir_free(void *data)
{
free(data);
}
static void iir_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
struct iir_data *iir = (struct iir_data*)data;
float *out = output->samples;
float b0 = iir->b0;
float b1 = iir->b1;
float b2 = iir->b2;
float a0 = iir->a0;
float a1 = iir->a1;
float a2 = iir->a2;
float xn1_l = iir->l.xn1;
float xn2_l = iir->l.xn2;
float yn1_l = iir->l.yn1;
float yn2_l = iir->l.yn2;
float xn1_r = iir->r.xn1;
float xn2_r = iir->r.xn2;
float yn1_r = iir->r.yn1;
float yn2_r = iir->r.yn2;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float in_l = out[0];
float in_r = out[1];
float l = (b0 * in_l + b1 * xn1_l + b2 * xn2_l - a1 * yn1_l - a2 * yn2_l) / a0;
float r = (b0 * in_r + b1 * xn1_r + b2 * xn2_r - a1 * yn1_r - a2 * yn2_r) / a0;
xn2_l = xn1_l;
xn1_l = in_l;
yn2_l = yn1_l;
yn1_l = l;
xn2_r = xn1_r;
xn1_r = in_r;
yn2_r = yn1_r;
yn1_r = r;
out[0] = l;
out[1] = r;
}
iir->l.xn1 = xn1_l;
iir->l.xn2 = xn2_l;
iir->l.yn1 = yn1_l;
iir->l.yn2 = yn2_l;
iir->r.xn1 = xn1_r;
iir->r.xn2 = xn2_r;
iir->r.yn1 = yn1_r;
iir->r.yn2 = yn2_r;
}
#define CHECK(x) if (string_is_equal(str, #x)) return x
static enum IIRFilter str_to_type(const char *str)
{
CHECK(LPF);
CHECK(HPF);
CHECK(BPCSGF);
CHECK(BPZPGF);
CHECK(APF);
CHECK(NOTCH);
CHECK(RIAA_phono);
CHECK(PEQ);
CHECK(BBOOST);
CHECK(LSH);
CHECK(HSH);
CHECK(RIAA_CD);
return LPF; /* Fallback. */
}
static void make_poly_from_roots(
const double *roots, unsigned num_roots, float *poly)
{
unsigned i, j;
poly[0] = 1;
poly[1] = -roots[0];
memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly));
for (i = 1; i < num_roots; i++)
for (j = num_roots; j > 0; j--)
poly[j] -= poly[j - 1] * roots[i];
}
static void iir_filter_init(struct iir_data *iir,
float sample_rate, float freq, float qual, float gain, enum IIRFilter filter_type)
{
double omega = 2.0 * M_PI * freq / sample_rate;
double cs = cos(omega);
double sn = sin(omega);
double a1pha = sn / (2.0 * qual);
double A = exp(log(10.0) * gain / 40.0);
double beta = sqrt(A + A);
float b0 = 0.0, b1 = 0.0, b2 = 0.0, a0 = 0.0, a1 = 0.0, a2 = 0.0;
/* Set up filter coefficients according to type */
switch (filter_type)
{
case LPF:
b0 = (1.0 - cs) / 2.0;
b1 = 1.0 - cs ;
b2 = (1.0 - cs) / 2.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case HPF:
b0 = (1.0 + cs) / 2.0;
b1 = -(1.0 + cs);
b2 = (1.0 + cs) / 2.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case APF:
b0 = 1.0 - a1pha;
b1 = -2.0 * cs;
b2 = 1.0 + a1pha;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case BPZPGF:
b0 = a1pha;
b1 = 0.0;
b2 = -a1pha;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case BPCSGF:
b0 = sn / 2.0;
b1 = 0.0;
b2 = -sn / 2.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case NOTCH:
b0 = 1.0;
b1 = -2.0 * cs;
b2 = 1.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case RIAA_phono: /* http://www.dsprelated.com/showmessage/73300/3.php */
{
double y, b_re, a_re, b_im, a_im, g;
float b[3] = {0.0f};
float a[3] = {0.0f};
if ((int)sample_rate == 44100)
{
static const double zeros[] = {-0.2014898, 0.9233820};
static const double poles[] = {0.7083149, 0.9924091};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
else if ((int)sample_rate == 48000)
{
static const double zeros[] = {-0.1766069, 0.9321590};
static const double poles[] = {0.7396325, 0.9931330};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
else if ((int)sample_rate == 88200)
{
static const double zeros[] = {-0.1168735, 0.9648312};
static const double poles[] = {0.8590646, 0.9964002};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
else if ((int)sample_rate == 96000)
{
static const double zeros[] = {-0.1141486, 0.9676817};
static const double poles[] = {0.8699137, 0.9966946};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
b0 = b[0];
b1 = b[1];
b2 = b[2];
a0 = a[0];
a1 = a[1];
a2 = a[2];
/* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */
y = 2.0 * M_PI * 1000.0 / sample_rate;
b_re = b0 + b1 * cos(-y) + b2 * cos(-2.0 * y);
a_re = a0 + a1 * cos(-y) + a2 * cos(-2.0 * y);
b_im = b1 * sin(-y) + b2 * sin(-2.0 * y);
a_im = a1 * sin(-y) + a2 * sin(-2.0 * y);
g = 1.0 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im)));
b0 *= g; b1 *= g; b2 *= g;
break;
}
case PEQ:
b0 = 1.0 + a1pha * A;
b1 = -2.0 * cs;
b2 = 1.0 - a1pha * A;
a0 = 1.0 + a1pha / A;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha / A;
break;
case BBOOST:
beta = sqrt((A * A + 1) / 1.0 - (pow((A - 1), 2)));
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
a0 = ((A + 1) + (A - 1) * cs + beta * sn);
a1 = -2 * ((A - 1) + (A + 1) * cs);
a2 = (A + 1) + (A - 1) * cs - beta * sn;
break;
case LSH:
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
a0 = (A + 1) + (A - 1) * cs + beta * sn;
a1 = -2 * ((A - 1) + (A + 1) * cs);
a2 = (A + 1) + (A - 1) * cs - beta * sn;
break;
case RIAA_CD:
omega = 2.0 * M_PI * 5283.0 / sample_rate;
cs = cos(omega);
sn = sin(omega);
a1pha = sn / (2.0 * 0.4845);
A = exp(log(10.0) * -9.477 / 40.0);
beta = sqrt(A + A);
(void)a1pha;
case HSH:
b0 = A * ((A + 1.0) + (A - 1.0) * cs + beta * sn);
b1 = -2.0 * A * ((A - 1.0) + (A + 1.0) * cs);
b2 = A * ((A + 1.0) + (A - 1.0) * cs - beta * sn);
a0 = (A + 1.0) - (A - 1.0) * cs + beta * sn;
a1 = 2.0 * ((A - 1.0) - (A + 1.0) * cs);
a2 = (A + 1.0) - (A - 1.0) * cs - beta * sn;
break;
default:
break;
}
iir->b0 = b0;
iir->b1 = b1;
iir->b2 = b2;
iir->a0 = a0;
iir->a1 = a1;
iir->a2 = a2;
}
static void *iir_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float freq, qual, gain;
enum IIRFilter filter = LPF;
char *type = NULL;
struct iir_data *iir = (struct iir_data*)calloc(1, sizeof(*iir));
if (!iir)
return NULL;
config->get_float(userdata, "frequency", &freq, 1024.0f);
config->get_float(userdata, "quality", &qual, 0.707f);
config->get_float(userdata, "gain", &gain, 0.0f);
config->get_string(userdata, "type", &type, "LPF");
filter = str_to_type(type);
config->free(type);
iir_filter_init(iir, info->input_rate, freq, qual, gain, filter);
return iir;
}
static const struct dspfilter_implementation iir_plug = {
iir_init,
iir_process,
iir_free,
DSPFILTER_API_VERSION,
"IIR",
"iir",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation iir_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &iir_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,4 @@
{
global: dspfilter_get_implementation;
local: *;
};

View file

@ -0,0 +1,111 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (panning.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <libretro_dspfilter.h>
struct panning_data
{
float left[2];
float right[2];
};
static void panning_free(void *data)
{
free(data);
}
static void panning_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
struct panning_data *pan = (struct panning_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float left = out[0];
float right = out[1];
out[0] = left * pan->left[0] + right * pan->left[1];
out[1] = left * pan->right[0] + right * pan->right[1];
}
}
static void *panning_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
static const float default_left[] = { 1.0f, 0.0f };
static const float default_right[] = { 0.0f, 1.0f };
float *left = NULL;
float *right = NULL;
unsigned num_left = 0;
unsigned num_right = 0;
struct panning_data *pan = (struct panning_data*)
calloc(1, sizeof(*pan));
if (!pan)
return NULL;
config->get_float_array(userdata, "left_mix",
&left, &num_left, default_left, 2);
config->get_float_array(userdata, "right_mix",
&right, &num_right, default_right, 2);
memcpy(pan->left, (num_left == 2) ?
left : default_left, sizeof(pan->left));
memcpy(pan->right, (num_right == 2) ?
right : default_right, sizeof(pan->right));
config->free(left);
config->free(right);
return pan;
}
static const struct dspfilter_implementation panning = {
panning_init,
panning_process,
panning_free,
DSPFILTER_API_VERSION,
"Panning",
"panning",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation panning_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &panning;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,145 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (phaser.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#define phaserlfoshape 4.0
#define phaserlfoskipsamples 20
struct phaser_data
{
float freq;
float startphase;
float fb;
float depth;
float drywet;
float old[2][24];
float gain;
float fbout[2];
float lfoskip;
float phase;
int stages;
unsigned long skipcount;
};
static void phaser_free(void *data)
{
free(data);
}
static void phaser_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i, c;
int s;
float m[2], tmp[2];
struct phaser_data *ph = (struct phaser_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float in[2] = { out[0], out[1] };
for (c = 0; c < 2; c++)
m[c] = in[c] + ph->fbout[c] * ph->fb * 0.01f;
if ((ph->skipcount++ % phaserlfoskipsamples) == 0)
{
ph->gain = 0.5 * (1.0 + cos(ph->skipcount * ph->lfoskip + ph->phase));
ph->gain = (exp(ph->gain * phaserlfoshape) - 1.0) / (exp(phaserlfoshape) - 1);
ph->gain = 1.0 - ph->gain * ph->depth;
}
for (s = 0; s < ph->stages; s++)
{
for (c = 0; c < 2; c++)
{
tmp[c] = ph->old[c][s];
ph->old[c][s] = ph->gain * tmp[c] + m[c];
m[c] = tmp[c] - ph->gain * ph->old[c][s];
}
}
for (c = 0; c < 2; c++)
{
ph->fbout[c] = m[c];
out[c] = m[c] * ph->drywet + in[c] * (1.0f - ph->drywet);
}
}
}
static void *phaser_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float lfo_freq, lfo_start_phase;
struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph));
if (!ph)
return NULL;
config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f);
config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f);
config->get_float(userdata, "feedback", &ph->fb, 0.0f);
config->get_float(userdata, "depth", &ph->depth, 0.4f);
config->get_float(userdata, "dry_wet", &ph->drywet, 0.5f);
config->get_int(userdata, "stages", &ph->stages, 2);
if (ph->stages < 1)
ph->stages = 1;
else if (ph->stages > 24)
ph->stages = 24;
ph->lfoskip = lfo_freq * 2.0 * M_PI / info->input_rate;
ph->phase = lfo_start_phase * M_PI / 180.0;
return ph;
}
static const struct dspfilter_implementation phaser_plug = {
phaser_init,
phaser_process,
phaser_free,
DSPFILTER_API_VERSION,
"Phaser",
"phaser",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation phaser_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &phaser_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,318 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (reverb.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_inline.h>
#include <libretro_dspfilter.h>
struct comb
{
float *buffer;
unsigned bufsize;
unsigned bufidx;
float feedback;
float filterstore;
float damp1, damp2;
};
struct allpass
{
float *buffer;
float feedback;
unsigned bufsize;
unsigned bufidx;
};
static INLINE float comb_process(struct comb *c, float input)
{
float output = c->buffer[c->bufidx];
c->filterstore = (output * c->damp2) + (c->filterstore * c->damp1);
c->buffer[c->bufidx] = input + (c->filterstore * c->feedback);
c->bufidx++;
if (c->bufidx >= c->bufsize)
c->bufidx = 0;
return output;
}
static INLINE float allpass_process(struct allpass *a, float input)
{
float bufout = a->buffer[a->bufidx];
float output = -input + bufout;
a->buffer[a->bufidx] = input + bufout * a->feedback;
a->bufidx++;
if (a->bufidx >= a->bufsize)
a->bufidx = 0;
return output;
}
#define numcombs 8
#define numallpasses 4
static const float muted = 0;
static const float fixedgain = 0.015f;
static const float scalewet = 3;
static const float scaledry = 2;
static const float scaledamp = 0.4f;
static const float scaleroom = 0.28f;
static const float offsetroom = 0.7f;
static const float initialroom = 0.5f;
static const float initialdamp = 0.5f;
static const float initialwet = 1.0f / 3.0f;
static const float initialdry = 0;
static const float initialwidth = 1;
static const float initialmode = 0;
static const float freezemode = 0.5f;
struct revmodel
{
struct comb combL[numcombs];
struct allpass allpassL[numallpasses];
float *bufcomb[numcombs];
float *bufallpass[numallpasses];
float gain;
float roomsize, roomsize1;
float damp, damp1;
float wet, wet1, wet2;
float dry;
float width;
float mode;
};
static float revmodel_process(struct revmodel *rev, float in)
{
int i;
float mono_out = 0.0f;
float mono_in = in;
float input = mono_in * rev->gain;
for (i = 0; i < numcombs; i++)
mono_out += comb_process(&rev->combL[i], input);
for (i = 0; i < numallpasses; i++)
mono_out = allpass_process(&rev->allpassL[i], mono_out);
return mono_in * rev->dry + mono_out * rev->wet1;
}
static void revmodel_update(struct revmodel *rev)
{
int i;
rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f);
if (rev->mode >= freezemode)
{
rev->roomsize1 = 1.0f;
rev->damp1 = 0.0f;
rev->gain = muted;
}
else
{
rev->roomsize1 = rev->roomsize;
rev->damp1 = rev->damp;
rev->gain = fixedgain;
}
for (i = 0; i < numcombs; i++)
{
rev->combL[i].feedback = rev->roomsize1;
rev->combL[i].damp1 = rev->damp1;
rev->combL[i].damp2 = 1.0f - rev->damp1;
}
}
static void revmodel_setroomsize(struct revmodel *rev, float value)
{
rev->roomsize = value * scaleroom + offsetroom;
revmodel_update(rev);
}
static void revmodel_setdamp(struct revmodel *rev, float value)
{
rev->damp = value * scaledamp;
revmodel_update(rev);
}
static void revmodel_setwet(struct revmodel *rev, float value)
{
rev->wet = value * scalewet;
revmodel_update(rev);
}
static void revmodel_setdry(struct revmodel *rev, float value)
{
rev->dry = value * scaledry;
revmodel_update(rev);
}
static void revmodel_setwidth(struct revmodel *rev, float value)
{
rev->width = value;
revmodel_update(rev);
}
static void revmodel_setmode(struct revmodel *rev, float value)
{
rev->mode = value;
revmodel_update(rev);
}
static void revmodel_init(struct revmodel *rev,int srate,bool right)
{
static const int comb_lengths[8] = { 1116,1188,1277,1356,1422,1491,1557,1617 };
static const int allpass_lengths[4] = { 225,341,441,556 };
double r = srate * (1 / 44100.0);
int stereosep = right ? 23 : 0;
unsigned c;
for (c = 0; c < numcombs; ++c)
{
rev->bufcomb[c] = malloc(r*comb_lengths[c]+stereosep*sizeof(float));
rev->combL[c].buffer = rev->bufcomb[c];
memset(rev->combL[c].buffer,0,r*comb_lengths[c]+stereosep*sizeof(float));
rev->combL[c].bufsize=r*comb_lengths[c]+stereosep;
}
for (c = 0; c < numallpasses; ++c)
{
rev->bufallpass[c] = malloc(r*allpass_lengths[c]+stereosep*sizeof(float));
rev->allpassL[c].buffer = rev->bufallpass[c];
memset(rev->allpassL[c].buffer,0,r*allpass_lengths[c]+stereosep*sizeof(float));
rev->allpassL[c].bufsize=r*allpass_lengths[c]+stereosep;
rev->allpassL[c].feedback = 0.5f;
}
revmodel_setwet(rev, initialwet);
revmodel_setroomsize(rev, initialroom);
revmodel_setdry(rev, initialdry);
revmodel_setdamp(rev, initialdamp);
revmodel_setwidth(rev, initialwidth);
revmodel_setmode(rev, initialmode);
}
struct reverb_data
{
struct revmodel left, right;
};
static void reverb_free(void *data)
{
struct reverb_data *rev = (struct reverb_data*)data;
unsigned i;
for (i = 0; i < numcombs; i++) {
free(rev->left.bufcomb[i]);
free(rev->right.bufcomb[i]);
}
for (i = 0; i < numallpasses; i++) {
free(rev->left.bufallpass[i]);
free(rev->right.bufallpass[i]);
}
free(data);
}
static void reverb_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out;
struct reverb_data *rev = (struct reverb_data*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
float in[2] = { out[0], out[1] };
out[0] = revmodel_process(&rev->left, in[0]);
out[1] = revmodel_process(&rev->right, in[1]);
}
}
static void *reverb_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float drytime, wettime, damping, roomwidth, roomsize;
struct reverb_data *rev = (struct reverb_data*)
calloc(1, sizeof(*rev));
if (!rev)
return NULL;
config->get_float(userdata, "drytime", &drytime, 0.43f);
config->get_float(userdata, "wettime", &wettime, 0.4f);
config->get_float(userdata, "damping", &damping, 0.8f);
config->get_float(userdata, "roomwidth", &roomwidth, 0.56f);
config->get_float(userdata, "roomsize", &roomsize, 0.56f);
revmodel_init(&rev->left,info->input_rate,false);
revmodel_init(&rev->right,info->input_rate,true);
revmodel_setdamp(&rev->left, damping);
revmodel_setdry(&rev->left, drytime);
revmodel_setwet(&rev->left, wettime);
revmodel_setwidth(&rev->left, roomwidth);
revmodel_setroomsize(&rev->left, roomsize);
revmodel_setdamp(&rev->right, damping);
revmodel_setdry(&rev->right, drytime);
revmodel_setwet(&rev->right, wettime);
revmodel_setwidth(&rev->right, roomwidth);
revmodel_setroomsize(&rev->right, roomsize);
return rev;
}
static const struct dspfilter_implementation reverb_plug = {
reverb_init,
reverb_process,
reverb_free,
DSPFILTER_API_VERSION,
"Reverb",
"reverb",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation reverb_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &reverb_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,132 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (tremolo.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#include <string/stdstring.h>
#define sqr(a) ((a) * (a))
struct tremolo_core
{
float freq;
float depth;
float* wavetable;
int index;
int maxindex;
};
struct tremolo
{
struct tremolo_core left, right;
};
static void tremolo_free(void *data)
{
struct tremolo *tre = (struct tremolo*)data;
free(tre->left.wavetable);
free(tre->right.wavetable);
free(data);
}
static void tremolocore_init(struct tremolo_core *core,float depth,int samplerate,float freq)
{
const double offset = 1. - depth / 2.;
unsigned i;
double env;
core->index = 0;
core->maxindex = samplerate/freq;
core->wavetable = malloc(core->maxindex*sizeof(float));
memset(core->wavetable, 0, core->maxindex * sizeof(float));
for (i = 0; i < core->maxindex; i++) {
env = freq * i / samplerate;
env = sin((M_PI*2) * fmod(env + 0.25, 1.0));
core->wavetable[i] = env * (1 - fabs(offset)) + offset;
}
}
float tremolocore_core(struct tremolo_core *core,float in)
{
core->index = core->index % core->maxindex;
return in * core->wavetable[core->index++];
}
static void tremolo_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out;
struct tremolo *tre = (struct tremolo*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
float in[2] = { out[0], out[1] };
out[0] = tremolocore_core(&tre->left, in[0]);
out[1] = tremolocore_core(&tre->right, in[1]);
}
}
static void *tremolo_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float freq, depth;
struct tremolo *tre = (struct tremolo*)calloc(1, sizeof(*tre));
if (!tre)
return NULL;
config->get_float(userdata, "freq", &freq,4.0f);
config->get_float(userdata, "depth", &depth, 0.9f);
tremolocore_init(&tre->left,depth,info->input_rate,freq);
tremolocore_init(&tre->right,depth,info->input_rate,freq);
return tre;
}
static const struct dspfilter_implementation tremolo_plug = {
tremolo_init,
tremolo_process,
tremolo_free,
DSPFILTER_API_VERSION,
"Tremolo",
"tremolo",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation tremolo_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &tremolo_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,167 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (vibrato.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#include <string/stdstring.h>
#define sqr(a) ((a) * (a))
const float BASE_DELAY_SEC = 0.002; // 2 ms
const float VIBRATO_FREQUENCY_DEFAULT_HZ = 2;
const float VIBRATO_FREQUENCY_MAX_HZ = 14;
const float VIBRATO_DEPTH_DEFAULT_PERCENT = 50;
const int add_delay = 3;
float hermite_interp(float x, float *y)
{
float c0, c1, c2, c3;
c0 = y[1];
c1 = (1.0 / 2.0)*(y[2] - y[0]);
c2 = (y[0] - (5.0 / 2.0)*y[1]) + (2.0*y[2] - (1.0 / 2.0)*y[3]);
c3 = (1.0 / 2.0)*(y[3] - y[0]) + (3.0 / 2.0)*(y[1] - y[2]);
return ((c3*x + c2)*x + c1)*x + c0;
}
struct vibrato_core
{
float freq;
float samplerate;
int phase;
float depth;
float* buffer;
int writeindex;
int size;
};
struct vibrato
{
struct vibrato_core left, right;
};
static void vibrato_free(void *data)
{
struct vibrato *vib = (struct vibrato*)data;
free(vib->left.buffer);
free(vib->right.buffer);
free(data);
}
static void vibratocore_init(struct vibrato_core *core,float depth,int samplerate,float freq)
{
core->size = BASE_DELAY_SEC * samplerate * 2;
core->buffer = malloc((core->size + add_delay)*sizeof(float));
memset(core->buffer, 0, (core->size + add_delay) * sizeof(float));
core->samplerate = samplerate;
core->freq = freq;
core->depth = depth;
core->phase = 0;
core->writeindex = 0;
}
float vibratocore_core(struct vibrato_core *core,float in)
{
float M = core->freq / core->samplerate;
int maxphase = core->samplerate / core->freq;
float lfo = sin(M * 2. * M_PI * core->phase++);
core->phase = core->phase % maxphase;
lfo = (lfo + 1) * 1.; // transform from [-1; 1] to [0; 1]
int maxdelay = BASE_DELAY_SEC * core->samplerate;
float delay = lfo * core->depth * maxdelay;
delay += add_delay;
float readindex = core->writeindex - 1 - delay;
while (readindex < 0)readindex += core->size;
while (readindex >= core->size)readindex -= core->size;
int ipart = (int)readindex; // integer part of the delay
float fpart = readindex - ipart; // fractional part of the delay
float value = hermite_interp(fpart, &(core->buffer[ipart]));
core->buffer[core->writeindex] = in;
if (core->writeindex < add_delay){
core->buffer[core->size + core->writeindex] = in;
}
core->writeindex++;
if (core->writeindex == core->size) {
core->writeindex = 0;
}
return value;
}
static void vibrato_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out;
struct vibrato *vib = (struct vibrato*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
float in[2] = { out[0], out[1] };
out[0] = vibratocore_core(&vib->left, in[0]);
out[1] = vibratocore_core(&vib->right, in[1]);
}
}
static void *vibrato_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float freq, depth;
struct vibrato *vib = (struct vibrato*)calloc(1, sizeof(*vib));
if (!vib)
return NULL;
config->get_float(userdata, "freq", &freq,5.0f);
config->get_float(userdata, "depth", &depth, 0.5f);
vibratocore_init(&vib->left,depth,info->input_rate,freq);
vibratocore_init(&vib->right,depth,info->input_rate,freq);
return vib;
}
static const struct dspfilter_implementation vibrato_plug = {
vibrato_init,
vibrato_process,
vibrato_free,
DSPFILTER_API_VERSION,
"Vibrato",
"vibrato",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation vibrato_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &vibrato_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,147 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (wahwah.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#define WAHWAH_LFO_SKIP_SAMPLES 30
struct wahwah_data
{
float phase;
float lfoskip;
float b0, b1, b2, a0, a1, a2;
float freq, startphase;
float depth, freqofs, res;
unsigned long skipcount;
struct
{
float xn1, xn2, yn1, yn2;
} l, r;
};
static void wahwah_free(void *data)
{
if (data)
free(data);
}
static void wahwah_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
struct wahwah_data *wah = (struct wahwah_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float out_l, out_r;
float in[2] = { out[0], out[1] };
if ((wah->skipcount++ % WAHWAH_LFO_SKIP_SAMPLES) == 0)
{
float omega, sn, cs, alpha;
float frequency = (1.0 + cos(wah->skipcount * wah->lfoskip + wah->phase)) / 2.0;
frequency = frequency * wah->depth * (1.0 - wah->freqofs) + wah->freqofs;
frequency = exp((frequency - 1.0) * 6.0);
omega = M_PI * frequency;
sn = sin(omega);
cs = cos(omega);
alpha = sn / (2.0 * wah->res);
wah->b0 = (1.0 - cs) / 2.0;
wah->b1 = 1.0 - cs;
wah->b2 = (1.0 - cs) / 2.0;
wah->a0 = 1.0 + alpha;
wah->a1 = -2.0 * cs;
wah->a2 = 1.0 - alpha;
}
out_l = (wah->b0 * in[0] + wah->b1 * wah->l.xn1 + wah->b2 * wah->l.xn2 - wah->a1 * wah->l.yn1 - wah->a2 * wah->l.yn2) / wah->a0;
out_r = (wah->b0 * in[1] + wah->b1 * wah->r.xn1 + wah->b2 * wah->r.xn2 - wah->a1 * wah->r.yn1 - wah->a2 * wah->r.yn2) / wah->a0;
wah->l.xn2 = wah->l.xn1;
wah->l.xn1 = in[0];
wah->l.yn2 = wah->l.yn1;
wah->l.yn1 = out_l;
wah->r.xn2 = wah->r.xn1;
wah->r.xn1 = in[1];
wah->r.yn2 = wah->r.yn1;
wah->r.yn1 = out_r;
out[0] = out_l;
out[1] = out_r;
}
}
static void *wahwah_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
struct wahwah_data *wah = (struct wahwah_data*)calloc(1, sizeof(*wah));
if (!wah)
return NULL;
config->get_float(userdata, "lfo_freq", &wah->freq, 1.5f);
config->get_float(userdata, "lfo_start_phase", &wah->startphase, 0.0f);
config->get_float(userdata, "freq_offset", &wah->freqofs, 0.3f);
config->get_float(userdata, "depth", &wah->depth, 0.7f);
config->get_float(userdata, "resonance", &wah->res, 2.5f);
wah->lfoskip = wah->freq * 2.0 * M_PI / info->input_rate;
wah->phase = wah->startphase * M_PI / 180.0;
return wah;
}
static const struct dspfilter_implementation wahwah_plug = {
wahwah_init,
wahwah_process,
wahwah_free,
DSPFILTER_API_VERSION,
"Wah-Wah",
"wahwah",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation wahwah_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &wahwah_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,189 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (audio_resampler.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <string/stdstring.h>
#include <features/features_cpu.h>
#include <file/config_file_userdata.h>
#include <audio/audio_resampler.h>
static void resampler_null_process(void *a, struct resampler_data *b) { }
static void resampler_null_free(void *a) { }
static void *resampler_null_init(const struct resampler_config *a, double b,
enum resampler_quality c, resampler_simd_mask_t d) { return (void*)0; }
retro_resampler_t null_resampler = {
resampler_null_init,
resampler_null_process,
resampler_null_free,
RESAMPLER_API_VERSION,
"null",
"null"
};
static const retro_resampler_t *resampler_drivers[] = {
&sinc_resampler,
#ifdef HAVE_CC_RESAMPLER
&CC_resampler,
#endif
#ifdef HAVE_NEAREST_RESAMPLER
&nearest_resampler,
#endif
&null_resampler,
NULL,
};
static const struct resampler_config resampler_config = {
config_userdata_get_float,
config_userdata_get_int,
config_userdata_get_float_array,
config_userdata_get_int_array,
config_userdata_get_string,
config_userdata_free,
};
/**
* find_resampler_driver_index:
* @ident : Identifier of resampler driver to find.
*
* Finds resampler driver index by @ident name.
*
* Returns: resampler driver index if resampler driver was found, otherwise
* -1.
**/
static int find_resampler_driver_index(const char *ident)
{
unsigned i;
for (i = 0; resampler_drivers[i]; i++)
if (string_is_equal_noncase(ident, resampler_drivers[i]->ident))
return i;
return -1;
}
/**
* find_resampler_driver:
* @ident : Identifier of resampler driver to find.
*
* Finds resampler by @ident name.
*
* Returns: resampler driver if resampler driver was found, otherwise
* NULL.
**/
static const retro_resampler_t *find_resampler_driver(const char *ident)
{
int i = find_resampler_driver_index(ident);
if (i >= 0)
return resampler_drivers[i];
return resampler_drivers[0];
}
/**
* resampler_append_plugs:
* @re : Resampler handle
* @backend : Resampler backend that is about to be set.
* @bw_ratio : Bandwidth ratio.
*
* Initializes resampler driver based on queried CPU features.
*
* Returns: true (1) if successfully initialized, otherwise false (0).
**/
static bool resampler_append_plugs(void **re,
const retro_resampler_t **backend,
enum resampler_quality quality,
double bw_ratio)
{
resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get();
if (*backend)
*re = (*backend)->init(&resampler_config, bw_ratio, quality, mask);
if (!*re)
return false;
return true;
}
/**
* audio_resampler_driver_find_handle:
* @idx : index of driver to get handle to.
*
* Returns: handle to audio resampler driver at index. Can be NULL
* if nothing found.
**/
const void *audio_resampler_driver_find_handle(int idx)
{
const void *drv = resampler_drivers[idx];
if (!drv)
return NULL;
return drv;
}
/**
* audio_resampler_driver_find_ident:
* @idx : index of driver to get handle to.
*
* Returns: Human-readable identifier of audio resampler driver at index.
* Can be NULL if nothing found.
**/
const char *audio_resampler_driver_find_ident(int idx)
{
const retro_resampler_t *drv = resampler_drivers[idx];
if (!drv)
return NULL;
return drv->ident;
}
/**
* retro_resampler_realloc:
* @re : Resampler handle
* @backend : Resampler backend that is about to be set.
* @ident : Identifier name for resampler we want.
* @bw_ratio : Bandwidth ratio.
*
* Reallocates resampler. Will free previous handle before
* allocating a new one. If ident is NULL, first resampler will be used.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool retro_resampler_realloc(void **re, const retro_resampler_t **backend,
const char *ident, enum resampler_quality quality, double bw_ratio)
{
if (*re && *backend)
(*backend)->free(*re);
*re = NULL;
*backend = find_resampler_driver(ident);
if (!resampler_append_plugs(re, backend, quality, bw_ratio))
{
if (!*re)
*backend = NULL;
return false;
}
return true;
}

View file

@ -0,0 +1,84 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nearest_resampler.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <audio/audio_resampler.h>
typedef struct rarch_nearest_resampler
{
float fraction;
} rarch_nearest_resampler_t;
static void resampler_nearest_process(
void *re_, struct resampler_data *data)
{
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
audio_frame_float_t *inp = (audio_frame_float_t*)data->data_in;
audio_frame_float_t *inp_max = (audio_frame_float_t*)inp + data->input_frames;
audio_frame_float_t *outp = (audio_frame_float_t*)data->data_out;
float ratio = 1.0 / data->ratio;
while (inp != inp_max)
{
while (re->fraction > 1)
{
*outp++ = *inp;
re->fraction -= ratio;
}
re->fraction++;
inp++;
}
data->output_frames = (outp - (audio_frame_float_t*)data->data_out);
}
static void resampler_nearest_free(void *re_)
{
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
if (re)
free(re);
}
static void *resampler_nearest_init(const struct resampler_config *config,
double bandwidth_mod,
enum resampler_quality quality,
resampler_simd_mask_t mask)
{
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)
calloc(1, sizeof(rarch_nearest_resampler_t));
if (!re)
return NULL;
re->fraction = 0;
return re;
}
retro_resampler_t nearest_resampler = {
resampler_nearest_init,
resampler_nearest_process,
resampler_nearest_free,
RESAMPLER_API_VERSION,
"nearest",
"nearest"
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (sinc_resampler_neon.S).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
#ifndef __MACH__
.arm
#endif
.align 4
.globl process_sinc_neon_asm
#ifndef __MACH__
.type process_sinc_neon_asm, %function
#endif
.globl _process_sinc_neon_asm
#ifndef __MACH__
.type _process_sinc_neon_asm, %function
#endif
# void process_sinc_neon(float *out, const float *left, const float *right, const float *coeff, unsigned taps)
# Assumes taps is >= 8, and a multiple of 8.
process_sinc_neon_asm:
_process_sinc_neon_asm:
push {r4, lr}
vmov.f32 q0, #0.0
vmov.f32 q8, #0.0
# Taps argument (r4) goes on stack in armeabi.
ldr r4, [sp, #8]
1:
# Left
vld1.f32 {q2-q3}, [r1]!
# Right
vld1.f32 {q10-q11}, [r2]!
# Coeff
vld1.f32 {q12-q13}, [r3, :128]!
# Left / Right
vmla.f32 q0, q2, q12
vmla.f32 q8, q10, q12
vmla.f32 q0, q3, q13
vmla.f32 q8, q11, q13
subs r4, r4, #8
bne 1b
# Add everything together
vadd.f32 d0, d0, d1
vadd.f32 d16, d16, d17
vpadd.f32 d0, d0, d16
vst1.f32 d0, [r0]
pop {r4, pc}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_fnmatch.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stddef.h>
#include <compat/fnmatch.h>
/* Implemnentation of fnmatch(3) so it can be
* distributed to non *nix platforms.
*
* No flags are implemented ATM.
* We don't use them. Add flags as needed. */
int rl_fnmatch(const char *pattern, const char *string, int flags)
{
int rv;
const char *c = NULL;
int charmatch = 0;
for (c = pattern; *c != '\0'; c++)
{
/* String ended before pattern */
if ((*c != '*') && (*string == '\0'))
return FNM_NOMATCH;
switch (*c)
{
/* Match any number of unknown chars */
case '*':
/* Find next node in the pattern
* ignoring multiple asterixes
*/
do {
c++;
if (*c == '\0')
return 0;
} while (*c == '*');
/* Match the remaining pattern
* ignoring more and more characters. */
do {
/* We reached the end of the string without a
* match. There is a way to optimize this by
* calculating the minimum chars needed to
* match the remaining pattern but I don't
* think it is worth the work ATM.
*/
if (*string == '\0')
return FNM_NOMATCH;
rv = rl_fnmatch(c, string, flags);
string++;
} while (rv != 0);
return 0;
/* Match char from list */
case '[':
charmatch = 0;
for (c++; *c != ']'; c++)
{
/* Bad format */
if (*c == '\0')
return FNM_NOMATCH;
/* Match already found */
if (charmatch)
continue;
if (*c == *string)
charmatch = 1;
}
/* No match in list */
if (!charmatch)
return FNM_NOMATCH;
string++;
break;
/* Has any character */
case '?':
string++;
break;
/* Match following character verbatim */
case '\\':
c++;
/* Dangling escape at end of pattern.
* FIXME: Was c == '\0' (makes no sense).
* Not sure if c == NULL or *c == '\0'
* is intended. Assuming *c due to c++ right before. */
if (*c == '\0')
return FNM_NOMATCH;
default:
if (*c != *string)
return FNM_NOMATCH;
string++;
}
}
/* End of string and end of pattend */
if (*string == '\0')
return 0;
return FNM_NOMATCH;
}

View file

@ -0,0 +1,238 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_getopt.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <boolean.h>
#include <stddef.h>
#include <stdlib.h>
#include <retro_miscellaneous.h>
#include <compat/getopt.h>
#include <compat/strl.h>
#include <compat/strcasestr.h>
#include <compat/posix_string.h>
#include <retro_assert.h>
char *optarg;
int optind, opterr, optopt;
static bool is_short_option(const char *str)
{
return str[0] == '-' && str[1] != '-';
}
static bool is_long_option(const char *str)
{
return str[0] == '-' && str[1] == '-';
}
static int find_short_index(char * const *argv)
{
int idx;
for (idx = 0; argv[idx]; idx++)
{
if (is_short_option(argv[idx]))
return idx;
}
return -1;
}
static int find_long_index(char * const *argv)
{
int idx;
for (idx = 0; argv[idx]; idx++)
{
if (is_long_option(argv[idx]))
return idx;
}
return -1;
}
static int parse_short(const char *optstring, char * const *argv)
{
bool extra_opt, takes_arg, embedded_arg;
const char *opt = NULL;
char arg = argv[0][1];
if (arg == ':')
return '?';
opt = strchr(optstring, arg);
if (!opt)
return '?';
extra_opt = argv[0][2];
takes_arg = opt[1] == ':';
/* If we take an argument, and we see additional characters,
* this is in fact the argument (i.e. -cfoo is same as -c foo). */
embedded_arg = extra_opt && takes_arg;
if (takes_arg)
{
if (embedded_arg)
{
optarg = argv[0] + 2;
optind++;
}
else
{
optarg = argv[1];
optind += 2;
}
return optarg ? opt[0] : '?';
}
if (embedded_arg)
{
/* If we see additional characters,
* and they don't take arguments, this
* means we have multiple flags in one. */
memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1);
return opt[0];
}
optind++;
return opt[0];
}
static int parse_long(const struct option *longopts, char * const *argv)
{
size_t indice;
char *save = NULL;
char *argv0 = strdup(&argv[0][2]);
char *token = strtok_r(argv0, "=", &save);
const struct option *opt = NULL;
for (indice = 0; longopts[indice].name; indice++)
{
if (token && !strcmp(longopts[indice].name, token))
{
opt = &longopts[indice];
break;
}
}
free(argv0);
argv0 = NULL;
if (!opt)
return '?';
/* Handle args with '=' instead of space */
if (opt->has_arg)
{
char *special_arg = strchr(argv[0], '=');
if (special_arg)
{
optarg = ++special_arg;
optind++;
return opt->val;
}
}
/* getopt_long has an "optional" arg, but we don't bother with that. */
if (opt->has_arg && !argv[1])
return '?';
if (opt->has_arg)
{
optarg = argv[1];
optind += 2;
}
else
optind++;
if (opt->flag)
{
*opt->flag = opt->val;
return 0;
}
return opt->val;
}
static void shuffle_block(char **begin, char **last, char **end)
{
ptrdiff_t len = last - begin;
const char **tmp = (const char**)calloc(len, sizeof(const char*));
retro_assert(tmp);
memcpy((void*)tmp, begin, len * sizeof(const char*));
memmove(begin, last, (end - last) * sizeof(const char*));
memcpy(end - len, tmp, len * sizeof(const char*));
free((void*)tmp);
}
int getopt_long(int argc, char *argv[],
const char *optstring, const struct option *longopts, int *longindex)
{
int short_index, long_index;
(void)longindex;
if (optind == 0)
optind = 1;
if (argc < 2)
return -1;
short_index = find_short_index(&argv[optind]);
long_index = find_long_index(&argv[optind]);
/* We're done here. */
if (short_index == -1 && long_index == -1)
return -1;
/* Reorder argv so that non-options come last.
* Non-POSIXy, but that's what getopt does by default. */
if ((short_index > 0) && ((short_index < long_index) || (long_index == -1)))
{
shuffle_block(&argv[optind], &argv[optind + short_index], &argv[argc]);
short_index = 0;
}
else if ((long_index > 0) && ((long_index < short_index)
|| (short_index == -1)))
{
shuffle_block(&argv[optind], &argv[optind + long_index], &argv[argc]);
long_index = 0;
}
retro_assert(short_index == 0 || long_index == 0);
if (short_index == 0)
return parse_short(optstring, &argv[optind]);
if (long_index == 0)
return parse_long(longopts, &argv[optind]);
return '?';
}

View file

@ -0,0 +1,654 @@
/*
Copyright (c) 2013, Kenneth MacKay
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <compat/ifaddrs.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
typedef struct NetlinkList
{
struct NetlinkList *m_next;
struct nlmsghdr *m_data;
unsigned int m_size;
} NetlinkList;
static int netlink_socket(void)
{
struct sockaddr_nl l_addr;
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (l_socket < 0)
return -1;
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
if (bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
{
close(l_socket);
return -1;
}
return l_socket;
}
static int netlink_send(int p_socket, int p_request)
{
struct
{
struct nlmsghdr m_hdr;
struct rtgenmsg m_msg;
} l_data;
struct sockaddr_nl l_addr;
memset(&l_data, 0, sizeof(l_data));
l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
l_data.m_hdr.nlmsg_type = p_request;
l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
l_data.m_hdr.nlmsg_pid = 0;
l_data.m_hdr.nlmsg_seq = p_socket;
l_data.m_msg.rtgen_family = AF_UNSPEC;
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
return (sendto(p_socket, &l_data.m_hdr, l_data.m_hdr.nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
}
static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
{
struct msghdr l_msg;
struct iovec l_iov = { p_buffer, p_len };
struct sockaddr_nl l_addr;
for (;;)
{
int l_result;
l_msg.msg_name = (void *)&l_addr;
l_msg.msg_namelen = sizeof(l_addr);
l_msg.msg_iov = &l_iov;
l_msg.msg_iovlen = 1;
l_msg.msg_control = NULL;
l_msg.msg_controllen = 0;
l_msg.msg_flags = 0;
l_result = recvmsg(p_socket, &l_msg, 0);
if (l_result < 0)
{
if (errno == EINTR)
continue;
return -2;
}
if (l_msg.msg_flags & MSG_TRUNC) /* buffer too small */
return -1;
return l_result;
}
}
static struct nlmsghdr *getNetlinkResponse(int p_socket,
int *p_size, int *p_done)
{
size_t l_size = 4096;
void *l_buffer = NULL;
for (;;)
{
int l_read;
free(l_buffer);
l_buffer = malloc(l_size);
if (!l_buffer)
return NULL;
l_read = netlink_recv(p_socket, l_buffer, l_size);
*p_size = l_read;
if (l_read == -2)
{
free(l_buffer);
return NULL;
}
if (l_read >= 0)
{
pid_t l_pid = getpid();
struct nlmsghdr *l_hdr;
for (l_hdr = (struct nlmsghdr *)l_buffer;
NLMSG_OK(l_hdr, (unsigned int)l_read);
l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
{
if ( (pid_t)l_hdr->nlmsg_pid != l_pid ||
(int)l_hdr->nlmsg_seq != p_socket)
continue;
if (l_hdr->nlmsg_type == NLMSG_DONE)
{
*p_done = 1;
break;
}
if (l_hdr->nlmsg_type == NLMSG_ERROR)
{
free(l_buffer);
return NULL;
}
}
return l_buffer;
}
l_size *= 2;
}
}
static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
{
NetlinkList *l_item = (NetlinkList*)malloc(sizeof(NetlinkList));
if (!l_item)
return NULL;
l_item->m_next = NULL;
l_item->m_data = p_data;
l_item->m_size = p_size;
return l_item;
}
static void freeResultList(NetlinkList *p_list)
{
NetlinkList *l_cur;
while (p_list)
{
l_cur = p_list;
p_list = p_list->m_next;
free(l_cur->m_data);
free(l_cur);
}
}
static NetlinkList *getResultList(int p_socket, int p_request)
{
int l_size;
NetlinkList *l_list = NULL;
NetlinkList *l_end = NULL;
int l_done = 0;
if (netlink_send(p_socket, p_request) < 0)
return NULL;
while (!l_done)
{
NetlinkList *l_item = NULL;
struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
if (!l_hdr)
goto error;
l_item = newListItem(l_hdr, l_size);
if (!l_item)
goto error;
if (!l_list)
l_list = l_item;
else
l_end->m_next = l_item;
l_end = l_item;
}
return l_list;
error:
freeResultList(l_list);
return NULL;
}
static size_t maxSize(size_t a, size_t b)
{
return (a > b ? a : b);
}
static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
{
switch(p_family)
{
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
case AF_PACKET:
return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
default:
break;
}
return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
}
static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
{
switch(p_family)
{
case AF_INET:
memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
break;
case AF_INET6:
memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
break;
case AF_PACKET:
memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
break;
default:
memcpy(p_dest->sa_data, p_data, p_size);
break;
}
p_dest->sa_family = p_family;
}
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
{
if (!*p_resultList)
*p_resultList = p_entry;
else
{
struct ifaddrs *l_cur = *p_resultList;
while (l_cur->ifa_next)
l_cur = l_cur->ifa_next;
l_cur->ifa_next = p_entry;
}
}
static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
{
struct ifaddrs *l_entry = NULL;
struct rtattr *l_rta = NULL;
struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
size_t l_nameSize = 0;
size_t l_addrSize = 0;
size_t l_dataSize = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
break;
case IFLA_IFNAME:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
case IFLA_STATS:
l_dataSize += NLMSG_ALIGN(l_rtaSize);
break;
default:
break;
}
}
l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
if (!l_entry)
return -1;
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = "";
char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
char *l_name = l_index + sizeof(int);
char *l_addr = l_name + l_nameSize;
char *l_data = l_addr + l_addrSize;
/* save the interface index so we can look
* it up when handling the addresses. */
memcpy(l_index, &l_info->ifi_index, sizeof(int));
l_entry->ifa_flags = l_info->ifi_flags;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
{
size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
if (l_rta->rta_type == IFLA_ADDRESS)
l_entry->ifa_addr = (struct sockaddr *)l_addr;
else
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFLA_IFNAME:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
case IFLA_STATS:
memcpy(l_data, l_rtaData, l_rtaDataSize);
l_entry->ifa_data = l_data;
break;
default:
break;
}
}
addToEnd(p_resultList, l_entry);
return 0;
}
static struct ifaddrs *findInterface(int p_index,
struct ifaddrs **p_links, int p_numLinks)
{
int l_num = 0;
struct ifaddrs *l_cur = *p_links;
while (l_cur && l_num < p_numLinks)
{
int l_index;
char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
memcpy(&l_index, l_indexPtr, sizeof(int));
if (l_index == p_index)
return l_cur;
l_cur = l_cur->ifa_next;
++l_num;
}
return NULL;
}
static int interpretAddr(struct nlmsghdr *p_hdr,
struct ifaddrs **p_resultList, int p_numLinks)
{
struct rtattr *l_rta;
size_t l_rtaSize;
size_t l_nameSize = 0;
size_t l_addrSize = 0;
int l_addedNetmask = 0;
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
if (l_info->ifa_family == AF_PACKET)
return 0;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_LOCAL:
if ((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
{
/* make room for netmask */
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
l_addedNetmask = 1;
}
case IFA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
break;
case IFA_LABEL:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
default:
break;
}
}
struct ifaddrs *l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
if (!l_entry)
return -1;
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
char *l_addr = l_name + l_nameSize;
l_entry->ifa_flags = l_info->ifa_flags;
if (l_interface)
l_entry->ifa_flags |= l_interface->ifa_flags;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_BROADCAST:
case IFA_LOCAL:
{
size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
if (l_info->ifa_family == AF_INET6)
{
if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
}
if (l_rta->rta_type == IFA_ADDRESS)
{
/* apparently in a point-to-point network IFA_ADDRESS
* contains the dest address and IFA_LOCAL contains the local address */
if (l_entry->ifa_addr)
l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
else
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else if (l_rta->rta_type == IFA_LOCAL)
{
if (l_entry->ifa_addr)
l_entry->ifa_dstaddr = l_entry->ifa_addr;
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFA_LABEL:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
default:
break;
}
}
if (l_entry->ifa_addr &&
( l_entry->ifa_addr->sa_family == AF_INET
|| l_entry->ifa_addr->sa_family == AF_INET6))
{
unsigned i;
char l_mask[16];
unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET
? 32 : 128);
unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix
? l_maxPrefix : l_info->ifa_prefixlen);
l_mask[0] = '\0';
for (i=0; i<(l_prefix/8); ++i)
l_mask[i] = 0xff;
if (l_prefix % 8)
l_mask[i] = 0xff << (8 - (l_prefix % 8));
makeSockaddr(l_entry->ifa_addr->sa_family,
(struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
l_entry->ifa_netmask = (struct sockaddr *)l_addr;
}
addToEnd(p_resultList, l_entry);
return 0;
}
static int interpretLinks(int p_socket, NetlinkList *p_netlinkList,
struct ifaddrs **p_resultList)
{
int l_numLinks = 0;
pid_t l_pid = getpid();
for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
struct nlmsghdr *l_hdr = NULL;
unsigned int l_nlsize = p_netlinkList->m_size;
for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize);
l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if ( (pid_t)l_hdr->nlmsg_pid != l_pid ||
(int)l_hdr->nlmsg_seq != p_socket)
continue;
if (l_hdr->nlmsg_type == NLMSG_DONE)
break;
if (l_hdr->nlmsg_type == RTM_NEWLINK)
{
if (interpretLink(l_hdr, p_resultList) == -1)
return -1;
++l_numLinks;
}
}
}
return l_numLinks;
}
static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList,
struct ifaddrs **p_resultList, int p_numLinks)
{
pid_t l_pid = getpid();
for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
struct nlmsghdr *l_hdr = NULL;
unsigned int l_nlsize = p_netlinkList->m_size;
for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize);
l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if ( (pid_t)l_hdr->nlmsg_pid != l_pid
|| (int)l_hdr->nlmsg_seq != p_socket)
continue;
if (l_hdr->nlmsg_type == NLMSG_DONE)
break;
if (l_hdr->nlmsg_type == RTM_NEWADDR)
{
if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
return -1;
}
}
}
return 0;
}
int getifaddrs(struct ifaddrs **ifap)
{
NetlinkList *l_linkResults;
NetlinkList *l_addrResults;
int l_numLinks;
int l_socket = 0;
int l_result = 0;
if (!ifap)
return -1;
*ifap = NULL;
l_socket = netlink_socket();
if (l_socket < 0)
return -1;
l_linkResults = getResultList(l_socket, RTM_GETLINK);
if (!l_linkResults)
{
close(l_socket);
return -1;
}
l_addrResults = getResultList(l_socket, RTM_GETADDR);
if (!l_addrResults)
{
close(l_socket);
freeResultList(l_linkResults);
return -1;
}
l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
if ( l_numLinks == -1 ||
interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
l_result = -1;
freeResultList(l_linkResults);
freeResultList(l_addrResults);
close(l_socket);
return l_result;
}
void freeifaddrs(struct ifaddrs *ifa)
{
struct ifaddrs *l_cur = NULL;
while (ifa)
{
l_cur = ifa;
ifa = ifa->ifa_next;
free(l_cur);
}
}

View file

@ -0,0 +1,104 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_posix_string.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include <compat/posix_string.h>
#ifdef _WIN32
#undef strcasecmp
#undef strdup
#undef isblank
#undef strtok_r
#include <ctype.h>
#include <stdlib.h>
#include <stddef.h>
#include <compat/strl.h>
#include <string.h>
int retro_strcasecmp__(const char *a, const char *b)
{
while (*a && *b)
{
int a_ = tolower(*a);
int b_ = tolower(*b);
if (a_ != b_)
return a_ - b_;
a++;
b++;
}
return tolower(*a) - tolower(*b);
}
char *retro_strdup__(const char *orig)
{
size_t len = strlen(orig) + 1;
char *ret = (char*)malloc(len);
if (!ret)
return NULL;
strlcpy(ret, orig, len);
return ret;
}
int retro_isblank__(int c)
{
return (c == ' ') || (c == '\t');
}
char *retro_strtok_r__(char *str, const char *delim, char **saveptr)
{
char *first = NULL;
if (!saveptr || !delim)
return NULL;
if (str)
*saveptr = str;
do
{
char *ptr = NULL;
first = *saveptr;
while (*first && strchr(delim, *first))
*first++ = '\0';
if (*first == '\0')
return NULL;
ptr = first + 1;
while (*ptr && !strchr(delim, *ptr))
ptr++;
*saveptr = ptr + (*ptr ? 1 : 0);
*ptr = '\0';
} while (strlen(first) == 0);
return first;
}
#endif

View file

@ -0,0 +1,83 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_snprintf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */
#ifdef _MSC_VER
#include <stdio.h>
#include <stdarg.h>
#if _MSC_VER < 1800
#define va_copy(dst, src) ((dst) = (src))
#endif
#if _MSC_VER < 1300
#define _vscprintf c89_vscprintf_retro__
static int c89_vscprintf_retro__(const char *fmt, va_list pargs)
{
int retval;
va_list argcopy;
va_copy(argcopy, pargs);
retval = vsnprintf(NULL, 0, fmt, argcopy);
va_end(argcopy);
return retval;
}
#endif
/* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */
int c99_vsnprintf_retro__(char *s, size_t len, const char *fmt, va_list ap)
{
int count = -1;
if (len != 0)
{
#if (_MSC_VER <= 1310)
count = _vsnprintf(s, len - 1, fmt, ap);
#else
count = _vsnprintf_s(s, len, len - 1, fmt, ap);
#endif
}
if (count == -1)
count = _vscprintf(fmt, ap);
/* there was no room for a NULL, so truncate the last character */
if (count == len && len)
s[len - 1] = '\0';
return count;
}
int c99_snprintf_retro__(char *s, size_t len, const char *fmt, ...)
{
int count;
va_list ap;
va_start(ap, fmt);
count = c99_vsnprintf_retro__(s, len, fmt, ap);
va_end(ap);
return count;
}
#endif

View file

@ -0,0 +1,58 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strcasestr.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include <compat/strcasestr.h>
/* Pretty much strncasecmp. */
static int casencmp(const char *a, const char *b, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
{
int a_lower = tolower(a[i]);
int b_lower = tolower(b[i]);
if (a_lower != b_lower)
return a_lower - b_lower;
}
return 0;
}
char *strcasestr_retro__(const char *haystack, const char *needle)
{
size_t i, search_off;
size_t hay_len = strlen(haystack);
size_t needle_len = strlen(needle);
if (needle_len > hay_len)
return NULL;
search_off = hay_len - needle_len;
for (i = 0; i <= search_off; i++)
if (!casencmp(haystack + i, needle, needle_len))
return (char*)haystack + i;
return NULL;
}

View file

@ -0,0 +1,62 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strl.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <ctype.h>
#include <compat/strl.h>
/* Implementation of strlcpy()/strlcat() based on OpenBSD. */
#ifndef __MACH__
size_t strlcpy(char *dest, const char *source, size_t size)
{
size_t src_size = 0;
size_t n = size;
if (n)
while (--n && (*dest++ = *source++)) src_size++;
if (!n)
{
if (size) *dest = '\0';
while (*source++) src_size++;
}
return src_size;
}
size_t strlcat(char *dest, const char *source, size_t size)
{
size_t len = strlen(dest);
dest += len;
if (len > size)
size = 0;
else
size -= len;
return len + strlcpy(dest, source, size);
}
#endif

View file

@ -0,0 +1,33 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strl.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <ctype.h>
#include <compat/strl.h>
char *strldup(const char *s, size_t n)
{
char *dst = (char*)malloc(sizeof(char) * (n + 1));
strlcpy(dst, s, n);
return dst;
}

View file

@ -0,0 +1,44 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_snprintf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* THIS FILE HAS NOT BEEN VALIDATED ON PLATFORMS BESIDES MSVC */
#ifdef _MSC_VER
#include <retro_common.h>
#include <stdio.h>
#include <stdarg.h>
#if defined(_MSC_VER) && _MSC_VER < 1800
#define va_copy(dst, src) ((dst) = (src))
#endif
int c89_vscprintf_retro__(const char *format, va_list pargs)
{
int retval;
va_list argcopy;
va_copy(argcopy, pargs);
retval = vsnprintf(NULL, 0, format, argcopy);
va_end(argcopy);
return retval;
}
#endif

View file

@ -0,0 +1,64 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fopen_utf8.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <compat/fopen_utf8.h>
#include <encodings/utf.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
#ifdef _WIN32
#undef fopen
void *fopen_utf8(const char * filename, const char * mode)
{
#if defined(LEGACY_WIN32)
char * filename_local = utf8_to_local_string_alloc(filename);
if (filename_local)
{
FILE *ret = fopen(filename_local, mode);
free(filename_local);
return ret;
}
#else
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
if (filename_w)
{
FILE *ret = NULL;
wchar_t *mode_w = utf8_to_utf16_string_alloc(mode);
if (mode_w)
{
ret = _wfopen(filename_w, mode_w);
free(mode_w);
}
free(filename_w);
return ret;
}
#endif
return NULL;
}
#endif

View file

@ -0,0 +1,10 @@
#ifndef __LIBRETRO_SDK_CRT_STRING_H_
#define __LIBRETRO_SDK_CRT_STRING_H_
#include <stdio.h>
void *memcpy(void *dst, const void *src, size_t len);
void *memset(void *b, int c, size_t len);
#endif

View file

@ -0,0 +1,34 @@
#ifdef _MSC_VER
#include <cruntime.h>
#endif
#include <stdio.h>
#include <string.h>
void *memset(void *dst, int val, size_t count)
{
void *start = dst;
#if defined(_M_IA64) || defined (_M_AMD64) || defined(_M_ALPHA) || defined (_M_PPC)
extern void RtlFillMemory(void *, size_t count, char);
RtlFillMemory(dst, count, (char)val);
#else
while (count--)
{
*(char*)dst = (char)val;
dst = (char*)dst + 1;
}
#endif
return start;
}
void *memcpy(void *dst, const void *src, size_t len)
{
size_t i;
for (i = 0; i < len; i++)
((unsigned char *)dst)[i] = ((unsigned char *)src)[i];
return dst;
}

View file

@ -0,0 +1,225 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (dylib.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <stdio.h>
#include <dynamic/dylib.h>
#include <encodings/utf.h>
#if defined(ORBIS)
#include <orbis/libkernel.h>
#endif
#ifdef NEED_DYNAMIC
#ifdef _WIN32
#include <compat/posix_string.h>
#include <windows.h>
#else
#if !defined(ORBIS)
#include <dlfcn.h>
#endif
#endif
/* Assume W-functions do not work below Win2K and Xbox platforms */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
#ifdef _WIN32
static char last_dyn_error[512];
static void set_dl_error(void)
{
DWORD err = GetLastError();
if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
err,
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
last_dyn_error,
sizeof(last_dyn_error) - 1,
NULL) == 0)
snprintf(last_dyn_error, sizeof(last_dyn_error) - 1,
"unknown error %lu", err);
}
#endif
/**
* dylib_load:
* @path : Path to libretro core library.
*
* Platform independent dylib loading.
*
* @return Library handle on success, otherwise NULL.
**/
dylib_t dylib_load(const char *path)
{
#ifdef _WIN32
#ifndef __WINRT__
int prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
#endif
#ifdef __WINRT__
dylib_t lib;
/* On UWP, you can only load DLLs inside your install directory, using a special function that takes a relative path */
char relative_path_abbrev[PATH_MAX_LENGTH];
char *relative_path = relative_path_abbrev;
wchar_t *path_wide = NULL;
relative_path_abbrev[0] = '\0';
if (!path_is_absolute(path))
RARCH_WARN("Relative path in dylib_load! This is likely an attempt to load a system library that will fail\n");
fill_pathname_abbreviate_special(relative_path_abbrev, path, sizeof(relative_path_abbrev));
/* Path to dylib_load is not inside app install directory.
* Loading will probably fail. */
if (relative_path[0] != ':' || !PATH_CHAR_IS_SLASH(relative_path[1])) { }
else
relative_path += 2;
path_wide = utf8_to_utf16_string_alloc(relative_path);
lib = LoadPackagedLibrary(path_wide, 0);
free(path_wide);
#elif defined(LEGACY_WIN32)
dylib_t lib = LoadLibrary(path);
#else
wchar_t *path_wide = utf8_to_utf16_string_alloc(path);
dylib_t lib = LoadLibraryW(path_wide);
free(path_wide);
#endif
#ifndef __WINRT__
SetErrorMode(prevmode);
#endif
if (!lib)
{
set_dl_error();
return NULL;
}
last_dyn_error[0] = 0;
#elif defined(ORBIS)
int res;
dylib_t lib = (dylib_t)sceKernelLoadStartModule(path, 0, NULL, 0, NULL, &res);
#else
dylib_t lib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
#endif
return lib;
}
char *dylib_error(void)
{
#ifdef _WIN32
if (last_dyn_error[0])
return last_dyn_error;
return NULL;
#else
return (char*)dlerror();
#endif
}
function_t dylib_proc(dylib_t lib, const char *proc)
{
function_t sym;
#ifdef _WIN32
HMODULE mod = (HMODULE)lib;
if (!mod)
{
#ifdef __WINRT__
/* GetModuleHandle is not available on UWP */
/* It's not possible to lookup symbols in current executable
* on UWP. */
DebugBreak();
return NULL;
#else
mod = GetModuleHandle(NULL);
#endif
}
if (!(sym = (function_t)GetProcAddress(mod, proc)))
{
set_dl_error();
return NULL;
}
last_dyn_error[0] = 0;
#elif defined(ORBIS)
void *ptr_sym = NULL;
sym = NULL;
if (lib)
{
sceKernelDlsym((SceKernelModule)lib, proc, &ptr_sym);
memcpy(&sym, &ptr_sym, sizeof(void*));
}
#else
void *ptr_sym = NULL;
if (lib)
ptr_sym = dlsym(lib, proc);
else
{
void *handle = dlopen(NULL, RTLD_LAZY);
if (handle)
{
ptr_sym = dlsym(handle, proc);
dlclose(handle);
}
}
/* Dirty hack to workaround the non-legality of
* (void*) -> fn-pointer casts. */
memcpy(&sym, &ptr_sym, sizeof(void*));
#endif
return sym;
}
/**
* dylib_close:
* @lib : Library handle.
*
* Frees library handle.
**/
void dylib_close(dylib_t lib)
{
#ifdef _WIN32
if (!FreeLibrary((HMODULE)lib))
set_dl_error();
last_dyn_error[0] = 0;
#elif defined(ORBIS)
int res;
sceKernelStopUnloadModule((SceKernelModule)lib, 0, NULL, 0, NULL, &res);
#else
#ifndef NO_DLCLOSE
dlclose(lib);
#endif
#endif
}
#endif

View file

@ -0,0 +1,183 @@
/*
https://github.com/superwills/NibbleAndAHalf
base64.h -- Fast base64 encoding and decoding.
version 1.0.0, April 17, 2013 143a
Copyright (C) 2013 William Sherif
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
William Sherif
will.sherif@gmail.com
YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
Modified for RetroArch formatting, logging, and header files.
*/
#include <stdio.h>
#include <stdlib.h>
#include <encodings/base64.h>
const static char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* maps A=>0,B=>1.. */
const static unsigned char unb64[]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 62, 0, 0, 0, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 0, 0,
0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
}; /* This array has 256 elements */
/*
Converts binary data of length=len to base64 characters.
Length of the resultant string is stored in flen
(you must pass pointer flen).
*/
char* base64(const void* binaryData, int len, int *flen)
{
const unsigned char* bin = (const unsigned char*) binaryData;
char* res;
int rc = 0; /* result counter */
int byteNo; /* I need this after the loop */
int modulusLen = len % 3 ;
/* 2 gives 1 and 1 gives 2, but 0 gives 0. */
int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1);
*flen = 4*(len + pad)/3;
res = (char*) malloc(*flen + 1); /* and one for the null */
if (!res)
{
/* ERROR: base64 could not allocate enough memory. */
return 0;
}
for (byteNo=0; byteNo <= len-3; byteNo+=3)
{
unsigned char BYTE0 = bin[byteNo];
unsigned char BYTE1 = bin[byteNo+1];
unsigned char BYTE2 = bin[byteNo+2];
res[rc++] = b64[BYTE0 >> 2];
res[rc++] = b64[((0x3&BYTE0)<<4) + (BYTE1 >> 4)];
res[rc++] = b64[((0x0f&BYTE1)<<2) + (BYTE2>>6)];
res[rc++] = b64[0x3f&BYTE2];
}
if (pad==2)
{
res[rc++] = b64[bin[byteNo] >> 2];
res[rc++] = b64[(0x3&bin[byteNo])<<4];
res[rc++] = '=';
res[rc++] = '=';
}
else if (pad==1)
{
res[rc++] = b64[bin[byteNo] >> 2];
res[rc++] = b64[((0x3&bin[byteNo])<<4) + (bin[byteNo+1] >> 4)];
res[rc++] = b64[(0x0f&bin[byteNo+1])<<2];
res[rc++] = '=';
}
res[rc]=0; /* NULL TERMINATOR! ;) */
return res;
}
unsigned char* unbase64(const char* ascii, int len, int *flen)
{
const unsigned char *safeAsciiPtr = (const unsigned char*) ascii;
unsigned char *bin;
int cb = 0;
int charNo;
int pad = 0;
if (len < 2) { /* 2 accesses below would be OOB. */
/* catch empty string, return NULL as result. */
/* ERROR: You passed an invalid base64 string (too short).
* You get NULL back. */
*flen = 0;
return 0;
}
if(safeAsciiPtr[len-1]=='=')
++pad;
if(safeAsciiPtr[len-2]=='=')
++pad;
*flen = 3*len/4 - pad;
bin = (unsigned char*)malloc(*flen);
if (!bin)
{
/* ERROR: unbase64 could not allocate enough memory. */
return 0;
}
for (charNo=0; charNo <= len-4-pad; charNo+=4)
{
int A = unb64[safeAsciiPtr[charNo]];
int B = unb64[safeAsciiPtr[charNo+1]];
int C = unb64[safeAsciiPtr[charNo+2]];
int D = unb64[safeAsciiPtr[charNo+3]];
bin[cb++] = (A<<2) | (B>>4);
bin[cb++] = (B<<4) | (C>>2);
bin[cb++] = (C<<6) | (D);
}
if (pad==1)
{
int A = unb64[safeAsciiPtr[charNo]];
int B = unb64[safeAsciiPtr[charNo+1]];
int C = unb64[safeAsciiPtr[charNo+2]];
bin[cb++] = (A<<2) | (B>>4);
bin[cb++] = (B<<4) | (C>>2);
}
else if (pad==2)
{
int A = unb64[safeAsciiPtr[charNo]];
int B = unb64[safeAsciiPtr[charNo+1]];
bin[cb++] = (A<<2) | (B>>4);
}
return bin;
}

Some files were not shown because too many files have changed in this diff Show more