build: Merge submodules into repo
This commit is contained in:
parent
540a30f719
commit
4c64279f90
422 changed files with 106715 additions and 8 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -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
1
other/DinguxCommander/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
output/
|
||||||
76
other/DinguxCommander/Makefile
Normal file
76
other/DinguxCommander/Makefile
Normal 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):
|
||||||
8
other/DinguxCommander/README.md
Normal file
8
other/DinguxCommander/README.md
Normal 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.
|
||||||
82
other/DinguxCommander/README.txt
Normal file
82
other/DinguxCommander/README.txt
Normal 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
|
||||||
BIN
other/DinguxCommander/res/Commander-11.ttf
Normal file
BIN
other/DinguxCommander/res/Commander-11.ttf
Normal file
Binary file not shown.
BIN
other/DinguxCommander/res/Fiery_Turk.ttf
Normal file
BIN
other/DinguxCommander/res/Fiery_Turk.ttf
Normal file
Binary file not shown.
BIN
other/DinguxCommander/res/background.png
Normal file
BIN
other/DinguxCommander/res/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 713 B |
BIN
other/DinguxCommander/res/file.png
Normal file
BIN
other/DinguxCommander/res/file.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 353 B |
BIN
other/DinguxCommander/res/folder.png
Normal file
BIN
other/DinguxCommander/res/folder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 307 B |
BIN
other/DinguxCommander/res/icon.png
Normal file
BIN
other/DinguxCommander/res/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
other/DinguxCommander/res/up.png
Normal file
BIN
other/DinguxCommander/res/up.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 272 B |
353
other/DinguxCommander/src/commander.cpp
Normal file
353
other/DinguxCommander/src/commander.cpp
Normal 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;
|
||||||
|
}
|
||||||
54
other/DinguxCommander/src/commander.h
Normal file
54
other/DinguxCommander/src/commander.h
Normal 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
|
||||||
124
other/DinguxCommander/src/def.h
Normal file
124
other/DinguxCommander/src/def.h
Normal 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
|
||||||
260
other/DinguxCommander/src/dialog.cpp
Normal file
260
other/DinguxCommander/src/dialog.cpp
Normal 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;
|
||||||
|
}
|
||||||
89
other/DinguxCommander/src/dialog.h
Normal file
89
other/DinguxCommander/src/dialog.h
Normal 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
|
||||||
115
other/DinguxCommander/src/fileLister.cpp
Normal file
115
other/DinguxCommander/src/fileLister.cpp
Normal 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;
|
||||||
|
}
|
||||||
58
other/DinguxCommander/src/fileLister.h
Normal file
58
other/DinguxCommander/src/fileLister.h
Normal 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
|
||||||
356
other/DinguxCommander/src/fileutils.cpp
Normal file
356
other/DinguxCommander/src/fileutils.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
other/DinguxCommander/src/fileutils.h
Normal file
50
other/DinguxCommander/src/fileutils.h
Normal 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
|
||||||
483
other/DinguxCommander/src/keyboard.cpp
Normal file
483
other/DinguxCommander/src/keyboard.cpp
Normal 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;
|
||||||
|
}
|
||||||
79
other/DinguxCommander/src/keyboard.h
Normal file
79
other/DinguxCommander/src/keyboard.h
Normal 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
|
||||||
89
other/DinguxCommander/src/main.cpp
Normal file
89
other/DinguxCommander/src/main.cpp
Normal 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;
|
||||||
|
}
|
||||||
339
other/DinguxCommander/src/panel.cpp
Normal file
339
other/DinguxCommander/src/panel.cpp
Normal 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);
|
||||||
|
}
|
||||||
101
other/DinguxCommander/src/panel.h
Normal file
101
other/DinguxCommander/src/panel.h
Normal 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
|
||||||
71
other/DinguxCommander/src/resourceManager.cpp
Normal file
71
other/DinguxCommander/src/resourceManager.cpp
Normal 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;
|
||||||
|
}
|
||||||
52
other/DinguxCommander/src/resourceManager.h
Normal file
52
other/DinguxCommander/src/resourceManager.h
Normal 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
|
||||||
322
other/DinguxCommander/src/sdlutils.cpp
Normal file
322
other/DinguxCommander/src/sdlutils.cpp
Normal 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
|
||||||
76
other/DinguxCommander/src/sdlutils.h
Normal file
76
other/DinguxCommander/src/sdlutils.h
Normal 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
|
||||||
219
other/DinguxCommander/src/viewer.cpp
Normal file
219
other/DinguxCommander/src/viewer.cpp
Normal 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;
|
||||||
|
}
|
||||||
69
other/DinguxCommander/src/viewer.h
Normal file
69
other/DinguxCommander/src/viewer.h
Normal 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
|
||||||
138
other/DinguxCommander/src/window.cpp
Normal file
138
other/DinguxCommander/src/window.cpp
Normal 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;
|
||||||
|
}
|
||||||
58
other/DinguxCommander/src/window.h
Normal file
58
other/DinguxCommander/src/window.h
Normal 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
|
|
||||||
6
src/minarch/libretro-common/.gitignore
vendored
Normal file
6
src/minarch/libretro-common/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
glsm/
|
||||||
|
*.[od]
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.exe
|
||||||
58
src/minarch/libretro-common/Makefile.test
Normal file
58
src/minarch/libretro-common/Makefile.test
Normal 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
|
||||||
|
|
||||||
377
src/minarch/libretro-common/audio/audio_mix.c
Normal file
377
src/minarch/libretro-common/audio/audio_mix.c
Normal 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;
|
||||||
|
}
|
||||||
1400
src/minarch/libretro-common/audio/audio_mixer.c
Normal file
1400
src/minarch/libretro-common/audio/audio_mixer.c
Normal file
File diff suppressed because it is too large
Load diff
178
src/minarch/libretro-common/audio/conversion/float_to_s16.c
Normal file
178
src/minarch/libretro-common/audio/conversion/float_to_s16.c
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
205
src/minarch/libretro-common/audio/conversion/s16_to_float.c
Normal file
205
src/minarch/libretro-common/audio/conversion/s16_to_float.c
Normal 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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
324
src/minarch/libretro-common/audio/dsp_filter.c
Normal file
324
src/minarch/libretro-common/audio/dsp_filter.c
Normal 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;
|
||||||
|
}
|
||||||
11
src/minarch/libretro-common/audio/dsp_filters/BassBoost.dsp
Normal file
11
src/minarch/libretro-common/audio/dsp_filters/BassBoost.dsp
Normal 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"
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = iir
|
||||||
|
|
||||||
|
iir_frequency = 8600.0
|
||||||
|
iir_quality = 0.707
|
||||||
|
iir_gain = 6.0
|
||||||
|
iir_type = LPF
|
||||||
|
|
@ -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"
|
||||||
14
src/minarch/libretro-common/audio/dsp_filters/Chorus.dsp
Normal file
14
src/minarch/libretro-common/audio/dsp_filters/Chorus.dsp
Normal 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
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = crystalizer
|
||||||
|
# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.
|
||||||
|
crystalizer_intensity = 5.0
|
||||||
41
src/minarch/libretro-common/audio/dsp_filters/EQ.dsp
Normal file
41
src/minarch/libretro-common/audio/dsp_filters/EQ.dsp
Normal 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
|
||||||
19
src/minarch/libretro-common/audio/dsp_filters/Echo.dsp
Normal file
19
src/minarch/libretro-common/audio/dsp_filters/Echo.dsp
Normal 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"
|
||||||
12
src/minarch/libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal file
12
src/minarch/libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal 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
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = iir
|
||||||
|
|
||||||
|
iir_gain = -12.0
|
||||||
|
iir_type = HSH
|
||||||
|
iir_frequency = 8000.0
|
||||||
22
src/minarch/libretro-common/audio/dsp_filters/IIR.dsp
Normal file
22
src/minarch/libretro-common/audio/dsp_filters/IIR.dsp
Normal 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
|
||||||
47
src/minarch/libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal file
47
src/minarch/libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal 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
|
||||||
135
src/minarch/libretro-common/audio/dsp_filters/Makefile
Normal file
135
src/minarch/libretro-common/audio/dsp_filters/Makefile
Normal 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
|
||||||
12
src/minarch/libretro-common/audio/dsp_filters/Mono.dsp
Normal file
12
src/minarch/libretro-common/audio/dsp_filters/Mono.dsp
Normal 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"
|
||||||
22
src/minarch/libretro-common/audio/dsp_filters/Panning.dsp
Normal file
22
src/minarch/libretro-common/audio/dsp_filters/Panning.dsp
Normal 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"
|
||||||
10
src/minarch/libretro-common/audio/dsp_filters/Phaser.dsp
Normal file
10
src/minarch/libretro-common/audio/dsp_filters/Phaser.dsp
Normal 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
|
||||||
9
src/minarch/libretro-common/audio/dsp_filters/Reverb.dsp
Normal file
9
src/minarch/libretro-common/audio/dsp_filters/Reverb.dsp
Normal 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
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = tremolo
|
||||||
|
|
||||||
|
# Defaults.
|
||||||
|
#tremolo_frequency = 4.0
|
||||||
|
#tremolo_depth = 0.9
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
filters = 1
|
||||||
|
filter0 = vibrato
|
||||||
|
|
||||||
|
# Defaults.
|
||||||
|
#vibrato_frequency = 5.0
|
||||||
|
#vibrato_depth = 0.5
|
||||||
9
src/minarch/libretro-common/audio/dsp_filters/WahWah.dsp
Normal file
9
src/minarch/libretro-common/audio/dsp_filters/WahWah.dsp
Normal 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
|
||||||
160
src/minarch/libretro-common/audio/dsp_filters/chorus.c
Normal file
160
src/minarch/libretro-common/audio/dsp_filters/chorus.c
Normal 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
|
||||||
3
src/minarch/libretro-common/audio/dsp_filters/configure
vendored
Executable file
3
src/minarch/libretro-common/audio/dsp_filters/configure
vendored
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PACKAGE_NAME=retroarch-filters-audio
|
||||||
90
src/minarch/libretro-common/audio/dsp_filters/crystalizer.c
Normal file
90
src/minarch/libretro-common/audio/dsp_filters/crystalizer.c
Normal 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
|
||||||
180
src/minarch/libretro-common/audio/dsp_filters/echo.c
Normal file
180
src/minarch/libretro-common/audio/dsp_filters/echo.c
Normal 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
|
||||||
352
src/minarch/libretro-common/audio/dsp_filters/eq.c
Normal file
352
src/minarch/libretro-common/audio/dsp_filters/eq.c
Normal 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
|
||||||
204
src/minarch/libretro-common/audio/dsp_filters/fft/fft.c
Normal file
204
src/minarch/libretro-common/audio/dsp_filters/fft/fft.c
Normal 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);
|
||||||
|
}
|
||||||
44
src/minarch/libretro-common/audio/dsp_filters/fft/fft.h
Normal file
44
src/minarch/libretro-common/audio/dsp_filters/fft/fft.h
Normal 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
|
||||||
371
src/minarch/libretro-common/audio/dsp_filters/iir.c
Normal file
371
src/minarch/libretro-common/audio/dsp_filters/iir.c
Normal 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
|
||||||
4
src/minarch/libretro-common/audio/dsp_filters/link.T
Normal file
4
src/minarch/libretro-common/audio/dsp_filters/link.T
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
global: dspfilter_get_implementation;
|
||||||
|
local: *;
|
||||||
|
};
|
||||||
111
src/minarch/libretro-common/audio/dsp_filters/panning.c
Normal file
111
src/minarch/libretro-common/audio/dsp_filters/panning.c
Normal 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
|
||||||
145
src/minarch/libretro-common/audio/dsp_filters/phaser.c
Normal file
145
src/minarch/libretro-common/audio/dsp_filters/phaser.c
Normal 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
|
||||||
318
src/minarch/libretro-common/audio/dsp_filters/reverb.c
Normal file
318
src/minarch/libretro-common/audio/dsp_filters/reverb.c
Normal 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
|
||||||
132
src/minarch/libretro-common/audio/dsp_filters/tremolo.c
Normal file
132
src/minarch/libretro-common/audio/dsp_filters/tremolo.c
Normal 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
|
||||||
167
src/minarch/libretro-common/audio/dsp_filters/vibrato.c
Normal file
167
src/minarch/libretro-common/audio/dsp_filters/vibrato.c
Normal 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
|
||||||
147
src/minarch/libretro-common/audio/dsp_filters/wahwah.c
Normal file
147
src/minarch/libretro-common/audio/dsp_filters/wahwah.c
Normal 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
|
||||||
189
src/minarch/libretro-common/audio/resampler/audio_resampler.c
Normal file
189
src/minarch/libretro-common/audio/resampler/audio_resampler.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
};
|
||||||
1025
src/minarch/libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
1025
src/minarch/libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||||
1745
src/minarch/libretro-common/cdrom/cdrom.c
Normal file
1745
src/minarch/libretro-common/cdrom/cdrom.c
Normal file
File diff suppressed because it is too large
Load diff
122
src/minarch/libretro-common/compat/compat_fnmatch.c
Normal file
122
src/minarch/libretro-common/compat/compat_fnmatch.c
Normal 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;
|
||||||
|
}
|
||||||
238
src/minarch/libretro-common/compat/compat_getopt.c
Normal file
238
src/minarch/libretro-common/compat/compat_getopt.c
Normal 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 '?';
|
||||||
|
}
|
||||||
654
src/minarch/libretro-common/compat/compat_ifaddrs.c
Normal file
654
src/minarch/libretro-common/compat/compat_ifaddrs.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/minarch/libretro-common/compat/compat_posix_string.c
Normal file
104
src/minarch/libretro-common/compat/compat_posix_string.c
Normal 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
|
||||||
83
src/minarch/libretro-common/compat/compat_snprintf.c
Normal file
83
src/minarch/libretro-common/compat/compat_snprintf.c
Normal 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
|
||||||
58
src/minarch/libretro-common/compat/compat_strcasestr.c
Normal file
58
src/minarch/libretro-common/compat/compat_strcasestr.c
Normal 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;
|
||||||
|
}
|
||||||
62
src/minarch/libretro-common/compat/compat_strl.c
Normal file
62
src/minarch/libretro-common/compat/compat_strl.c
Normal 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
|
||||||
33
src/minarch/libretro-common/compat/compat_strldup.c
Normal file
33
src/minarch/libretro-common/compat/compat_strldup.c
Normal 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;
|
||||||
|
}
|
||||||
44
src/minarch/libretro-common/compat/compat_vscprintf.c
Normal file
44
src/minarch/libretro-common/compat/compat_vscprintf.c
Normal 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
|
||||||
64
src/minarch/libretro-common/compat/fopen_utf8.c
Normal file
64
src/minarch/libretro-common/compat/fopen_utf8.c
Normal 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
|
||||||
10
src/minarch/libretro-common/crt/include/string.h
Normal file
10
src/minarch/libretro-common/crt/include/string.h
Normal 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
|
||||||
34
src/minarch/libretro-common/crt/string.c
Normal file
34
src/minarch/libretro-common/crt/string.c
Normal 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;
|
||||||
|
}
|
||||||
225
src/minarch/libretro-common/dynamic/dylib.c
Normal file
225
src/minarch/libretro-common/dynamic/dylib.c
Normal 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
|
||||||
183
src/minarch/libretro-common/encodings/encoding_base64.c
Normal file
183
src/minarch/libretro-common/encodings/encoding_base64.c
Normal 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
Loading…
Add table
Add a link
Reference in a new issue