@@ -4,8 +4,6 @@ | |||
# Created by falkTX | |||
# | |||
# libx11-dev libxext-dev libxinerama-dev libfreetype6-dev libxcursor-dev | |||
# -------------------------------------------------------------- | |||
# Modify to enable/disable specific features | |||
@@ -107,7 +105,6 @@ endif | |||
# Check for optional libs (required by backend or bridges) | |||
HAVE_FFMPEG = $(shell pkg-config --exists libavcodec libavformat libavutil && pkg-config --max-version=1.9 libavcodec && echo true) | |||
HAVE_OPENGL = $(shell pkg-config --exists gl && echo true) | |||
ifeq ($(LINUX),true) | |||
HAVE_ALSA = $(shell pkg-config --exists alsa && echo true) | |||
@@ -116,6 +113,9 @@ HAVE_GTK3 = $(shell pkg-config --exists gtk+-3.0 && echo true) | |||
HAVE_PULSEAUDIO = $(shell pkg-config --exists libpulse-simple && echo true) | |||
HAVE_QT4 = $(shell pkg-config --exists QtCore QtGui && echo true) | |||
HAVE_QT5 = $(shell pkg-config --exists Qt5Core Qt5Gui Qt5Widgets && echo true) | |||
HAVE_OPENGL = $(shell pkg-config --exists gl && echo true) | |||
else | |||
HAVE_OPENGL = true | |||
endif | |||
ifeq ($(CARLA_SAMPLERS_SUPPORT),true) | |||
@@ -138,8 +138,10 @@ ifeq ($(HAIKU),true) | |||
endif | |||
ifeq ($(LINUX),true) | |||
# JUCE_AUDIO_DEVICES_FLAGS = $(shell pkg-config --cflags alsa) | |||
# JUCE_AUDIO_DEVICES_LIBS = $(shell pkg-config --libs alsa) | |||
ifeq ($(HAVE_OPENGL),true) | |||
DGL_FLAGS = $(shell pkg-config --cflags gl x11) | |||
DGL_LIBS = $(shell pkg-config --libs gl x11) | |||
endif | |||
JUCE_CORE_LIBS = -lrt -ldl -lpthread | |||
JUCE_EVENTS_FLAGS = $(shell pkg-config --cflags x11) | |||
JUCE_EVENTS_LIBS = $(shell pkg-config --libs x11) | |||
@@ -150,6 +152,7 @@ JUCE_GUI_BASICS_LIBS = $(shell pkg-config --libs x11 xinerama xext xcursor) | |||
endif | |||
ifeq ($(MACOS),true) | |||
DGL_LIBS = -framework OpenGL -framework Cocoa | |||
JUCE_AUDIO_BASICS_LIBS = -framework Accelerate | |||
JUCE_AUDIO_DEVICES_LIBS = -framework CoreAudio -framework CoreMIDI -framework DiscRecording | |||
JUCE_AUDIO_FORMATS_LIBS = -framework CoreAudio -framework CoreMIDI -framework QuartzCore -framework AudioToolbox | |||
@@ -159,6 +162,7 @@ JUCE_GUI_BASICS_LIBS = -framework Cocoa -framework Carbon -framework QuartzCo | |||
endif | |||
ifeq ($(WIN32),true) | |||
DGL_LIBS = -lopengl32 -lgdi32 | |||
JUCE_AUDIO_DEVICES_LIBS = -lwinmm -lole32 | |||
JUCE_CORE_LIBS = -luuid -lwsock32 -lwininet -lversion -lole32 -lws2_32 -loleaut32 -limm32 -lcomdlg32 -lshlwapi -lrpcrt4 -lwinmm | |||
JUCE_EVENTS_LIBS = -lole32 | |||
@@ -293,6 +293,13 @@ CARLA_EXPORT const CarlaNativePluginInfo* carla_get_internal_plugin_info(unsigne | |||
*/ | |||
CARLA_EXPORT bool carla_engine_init(const char* driverName, const char* clientName); | |||
#ifdef BUILD_BRIDGE | |||
/*! | |||
* Initialize the engine in bridged mode. | |||
*/ | |||
CARLA_EXPORT bool carla_engine_init_bridge(const char* audioBaseName, const char* controlBaseName, const char* clientName); | |||
#endif | |||
/*! | |||
* Close the running engine.\n | |||
* This function always closes the engine even if it returns false.\n | |||
@@ -724,7 +731,6 @@ CARLA_EXPORT void carla_nsm_reply_save(); | |||
#ifdef BUILD_BRIDGE | |||
using CarlaBackend::CarlaEngine; | |||
CARLA_EXPORT CarlaEngine* carla_get_standalone_engine(); | |||
CARLA_EXPORT bool carla_engine_init_bridge(const char* audioBaseName, const char* controlBaseName, const char* clientName); | |||
#endif | |||
/**@}*/ | |||
@@ -41,7 +41,52 @@ using CB::CallbackFunc; | |||
using CB::EngineOptions; | |||
using CB::EngineTimeInfo; | |||
using juce::MessageManager; | |||
//using juce::MessageManager; | |||
using namespace juce; | |||
// ----------------------------------------------------------------------- | |||
// Juce Message Thread | |||
class JuceMessageThread : public Thread | |||
{ | |||
public: | |||
JuceMessageThread() | |||
: Thread("JuceMessageThread"), | |||
fInitialised(false) | |||
{ | |||
startThread(7); | |||
while (! fInitialised) | |||
sleep(1); | |||
} | |||
~JuceMessageThread() | |||
{ | |||
signalThreadShouldExit(); | |||
JUCEApplication::quit(); | |||
waitForThreadToExit(5000); | |||
clearSingletonInstance(); | |||
} | |||
void run() | |||
{ | |||
initialiseJuce_GUI(); | |||
fInitialised = true; | |||
MessageManager::getInstance()->setCurrentThreadAsMessageThread(); | |||
while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil(250)) | |||
{} | |||
} | |||
juce_DeclareSingleton(JuceMessageThread, false); | |||
private: | |||
bool fInitialised; | |||
}; | |||
juce_ImplementSingleton(JuceMessageThread) | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
// Single, standalone engine | |||
@@ -61,14 +106,33 @@ struct CarlaBackendStandalone { | |||
~CarlaBackendStandalone() | |||
{ | |||
CARLA_ASSERT(engine == nullptr); | |||
CARLA_ASSERT(MessageManager::getInstanceWithoutCreating() == nullptr); | |||
//CARLA_ASSERT(MessageManager::getInstanceWithoutCreating() == nullptr); | |||
} | |||
#if 1 | |||
void init() | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
initialiseJuce_GUI(); | |||
JuceMessageThread::getInstance(); | |||
} | |||
void idle() {} | |||
void close() | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
JuceMessageThread::deleteInstance(); | |||
shutdownJuce_GUI(); | |||
} | |||
#else | |||
void init() | |||
{ | |||
juce::initialiseJuce_GUI(); | |||
if (MessageManager* const mgr = MessageManager::getInstanceWithoutCreating()) | |||
if (MessageManager* const mgr = MessageManager::getInstance()) | |||
mgr->setCurrentThreadAsMessageThread(); | |||
} | |||
@@ -85,6 +149,7 @@ struct CarlaBackendStandalone { | |||
juce::shutdownJuce_GUI(); | |||
} | |||
#endif | |||
CARLA_DECLARE_NON_COPY_STRUCT(CarlaBackendStandalone) | |||
}; | |||
@@ -333,19 +398,6 @@ bool carla_engine_init(const char* driverName, const char* clientName) | |||
return false; | |||
} | |||
#if 0 //def DEBUG | |||
static bool firstInit = true; | |||
if (firstInit) | |||
{ | |||
firstInit = false; | |||
if (gStandalone.callback != nullptr) | |||
gStandalone.callback(gStandalone.callbackPtr, CB::CALLBACK_DEBUG, 0, 0, 0, 0.0f, | |||
"Debug builds don't use this, please check the console instead."); | |||
} | |||
#endif | |||
#ifdef Q_OS_WIN | |||
carla_setenv("WINEASIO_CLIENT_NAME", clientName); | |||
#endif | |||
@@ -422,6 +474,58 @@ bool carla_engine_init(const char* driverName, const char* clientName) | |||
} | |||
} | |||
#ifdef BUILD_BRIDGE | |||
bool carla_engine_init_bridge(const char* audioBaseName, const char* controlBaseName, const char* clientName) | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(audioBaseName != nullptr && audioBaseName[0] != '\0', false); | |||
CARLA_SAFE_ASSERT_RETURN(controlBaseName != nullptr && controlBaseName[0] != '\0', false); | |||
CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false); | |||
carla_debug("carla_engine_init_bridge(\"%s\", %s, %s)", audioBaseName, controlBaseName, clientName); | |||
if (gStandalone.engine != nullptr) | |||
{ | |||
carla_stderr2("Engine is already running"); | |||
gStandalone.lastError = "Engine is already running"; | |||
return false; | |||
} | |||
gStandalone.engine = CarlaEngine::newBridge(audioBaseName, controlBaseName); | |||
if (gStandalone.engine == nullptr) | |||
{ | |||
carla_stderr2("The seleted audio driver is not available!"); | |||
gStandalone.lastError = "The seleted audio driver is not available!"; | |||
return false; | |||
} | |||
if (gStandalone.callback != nullptr) | |||
gStandalone.engine->setCallback(gStandalone.callback, gStandalone.callbackPtr); | |||
gStandalone.engine->setOption(CB::OPTION_PROCESS_MODE, CB::PROCESS_MODE_BRIDGE, nullptr); | |||
gStandalone.engine->setOption(CB::OPTION_TRANSPORT_MODE, CB::TRANSPORT_MODE_BRIDGE, nullptr); | |||
gStandalone.engine->setOption(CB::OPTION_FORCE_STEREO, false, nullptr); | |||
gStandalone.engine->setOption(CB::OPTION_PREFER_PLUGIN_BRIDGES, false, nullptr); | |||
gStandalone.engine->setOption(CB::OPTION_PREFER_UI_BRIDGES, false, nullptr); | |||
//gStandalone.engine->setOption(CB::OPTION_UIS_ALWAYS_ON_TOP, gStandalone.options.uisAlwaysOnTop ? 1 : 0, nullptr); | |||
//gStandalone.engine->setOption(CB::OPTION_MAX_PARAMETERS, static_cast<int>(gStandalone.options.maxParameters), nullptr); | |||
//gStandalone.engine->setOption(CB::OPTION_UI_BRIDGES_TIMEOUT, static_cast<int>(gStandalone.options.uiBridgesTimeout), nullptr); | |||
if (gStandalone.engine->init(clientName)) | |||
{ | |||
gStandalone.lastError = "no error"; | |||
gStandalone.init(); | |||
return true; | |||
} | |||
else | |||
{ | |||
gStandalone.lastError = gStandalone.engine->getLastError(); | |||
delete gStandalone.engine; | |||
gStandalone.engine = nullptr; | |||
return false; | |||
} | |||
} | |||
#endif | |||
bool carla_engine_close() | |||
{ | |||
carla_debug("carla_engine_close()"); | |||
@@ -2120,53 +2224,4 @@ CarlaEngine* carla_get_standalone_engine() | |||
{ | |||
return gStandalone.engine; | |||
} | |||
bool carla_engine_init_bridge(const char* audioBaseName, const char* controlBaseName, const char* clientName) | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine == nullptr, false); | |||
CARLA_SAFE_ASSERT_RETURN(audioBaseName != nullptr && audioBaseName[0] != '\0', false); | |||
CARLA_SAFE_ASSERT_RETURN(controlBaseName != nullptr && controlBaseName[0] != '\0', false); | |||
CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false); | |||
carla_debug("carla_engine_init_bridge(\"%s\", \"%s\", \"%s\")", audioBaseName, controlBaseName, clientName); | |||
gStandalone.engine = CarlaEngine::newBridge(audioBaseName, controlBaseName); | |||
if (gStandalone.engine == nullptr) | |||
{ | |||
carla_stderr2("The seleted audio driver is not available!"); | |||
gStandalone.lastError = "The seleted audio driver is not available!"; | |||
return false; | |||
} | |||
if (gStandalone.callback != nullptr) | |||
gStandalone.engine->setCallback(gStandalone.callback, gStandalone.callbackPtr); | |||
gStandalone.engine->setOption(CB::OPTION_PROCESS_MODE, CB::PROCESS_MODE_BRIDGE, nullptr); | |||
gStandalone.engine->setOption(CB::OPTION_TRANSPORT_MODE, CB::TRANSPORT_MODE_BRIDGE, nullptr); | |||
gStandalone.engine->setOption(CB::OPTION_PREFER_PLUGIN_BRIDGES, false, nullptr); | |||
gStandalone.engine->setOption(CB::OPTION_PREFER_UI_BRIDGES, false, nullptr); | |||
// TODO - read from environment | |||
#if 0 | |||
gStandalone.engine->setOption(CB::OPTION_FORCE_STEREO, gStandalone.options.forceStereo ? 1 : 0, nullptr); | |||
# ifdef WANT_DSSI | |||
gStandalone.engine->setOption(CB::OPTION_USE_DSSI_VST_CHUNKS, gStandalone.options.useDssiVstChunks ? 1 : 0, nullptr); | |||
# endif | |||
gStandalone.engine->setOption(CB::OPTION_MAX_PARAMETERS, static_cast<int>(gStandalone.options.maxParameters), nullptr); | |||
#endif | |||
if (gStandalone.engine->init(clientName)) | |||
{ | |||
gStandalone.lastError = "no error"; | |||
gStandalone.init(); | |||
return true; | |||
} | |||
else | |||
{ | |||
gStandalone.lastError = gStandalone.engine->getLastError(); | |||
delete gStandalone.engine; | |||
gStandalone.engine = nullptr; | |||
return false; | |||
} | |||
} | |||
#endif |
@@ -13,6 +13,9 @@ BUILD_CXX_FLAGS += $(shell pkg-config --cflags liblo) | |||
# -------------------------------------------------------------- | |||
# Common | |||
LIBS = ../libcarla_engine.a | |||
LIBS += ../libcarla_plugin.a | |||
LINK_FLAGS += $(shell pkg-config --libs liblo) | |||
LINK_FLAGS += -lpthread | |||
@@ -65,10 +68,6 @@ ifeq ($(HAVE_MF_DEPS),true) | |||
LINK_FLAGS += $(shell pkg-config --libs smf) | |||
endif | |||
ifeq ($(HAVE_OPENGL),true) | |||
LINK_FLAGS += $(shell pkg-config --libs gl) | |||
endif | |||
ifeq ($(HAVE_ZYN_DEPS),true) | |||
LINK_FLAGS += $(shell pkg-config --libs fftw3 mxml zlib) | |||
ifeq ($(HAVE_ZYN_UI_DEPS),true) | |||
@@ -76,10 +75,12 @@ LINK_FLAGS += $(shell pkg-config --libs ntk_images ntk) | |||
endif | |||
endif | |||
ifeq ($(HAVE_OPENGL),true) | |||
LINK_FLAGS += $(DGL_LIBS) | |||
endif | |||
# -------------------------------------------------------------- | |||
LIBS = ../libcarla_engine.a | |||
LIBS += ../libcarla_plugin.a | |||
LIBS += ../../modules/carla_native.a | |||
LIBS += ../../modules/juce_audio_basics.a | |||
LIBS += ../../modules/juce_core.a | |||
@@ -38,7 +38,7 @@ static volatile bool gCloseNow = false; | |||
static volatile bool gSaveNow = false; | |||
#ifdef CARLA_OS_WIN | |||
BOOL WINAPI closeSignalHandler(DWORD dwCtrlType) | |||
BOOL WINAPI winSignalHandler(DWORD dwCtrlType) | |||
{ | |||
if (dwCtrlType == CTRL_C_EVENT) | |||
{ | |||
@@ -62,7 +62,7 @@ static void saveSignalHandler(int) | |||
void initSignalHandler() | |||
{ | |||
#ifdef CARLA_OS_WIN | |||
SetConsoleCtrlHandler(closeSignalHandler, TRUE); | |||
SetConsoleCtrlHandler(winSignalHandler, TRUE); | |||
#elif defined(CARLA_OS_LINUX) || defined(CARLA_OS_HAIKU) | |||
struct sigaction sint; | |||
struct sigaction sterm; | |||
@@ -106,7 +106,7 @@ public: | |||
fEngine(nullptr), | |||
fPlugin(nullptr) | |||
{ | |||
CARLA_ASSERT(driverName != nullptr); | |||
CARLA_ASSERT(driverName != nullptr && driverName[0] != '\0'); | |||
carla_debug("CarlaPluginClient::CarlaPluginClient(%s, \"%s\", %s, %s)", bool2str(useBridge), driverName, audioBaseName, controlBaseName); | |||
carla_set_engine_callback(callback, this); | |||
@@ -124,6 +124,8 @@ public: | |||
carla_engine_init_bridge(audioBaseName, controlBaseName, driverName); | |||
else | |||
carla_engine_init("JACK", driverName); | |||
fEngine = carla_get_standalone_engine(); | |||
} | |||
~CarlaPluginClient() override | |||
@@ -133,17 +135,20 @@ public: | |||
carla_engine_close(); | |||
} | |||
bool isOk() const noexcept | |||
{ | |||
return (fEngine != nullptr); | |||
} | |||
void oscInit(const char* const url) | |||
{ | |||
CarlaBridgeClient::oscInit(url); | |||
fEngine = carla_get_standalone_engine(); | |||
fEngine->setOscBridgeData(&fOscData); | |||
} | |||
void ready(const bool doSaveLoad) | |||
{ | |||
fEngine = carla_get_standalone_engine(); | |||
fPlugin = fEngine->getPlugin(0); | |||
if (doSaveLoad) | |||
@@ -164,7 +169,7 @@ public: | |||
CARLA_SAFE_ASSERT_RETURN(fEngine != nullptr,); | |||
CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||
fEngine->idle(); | |||
carla_engine_idle(); | |||
CarlaBridgeClient::oscIdle(); | |||
if (gSaveNow) | |||
@@ -483,7 +488,7 @@ int main(int argc, char* argv[]) | |||
// Setup args | |||
const bool useBridge = (argc == 7); | |||
const bool useOsc = std::strcmp(oscUrl, "null"); | |||
const bool useOsc = (std::strcmp(oscUrl, "null") != 0 && std::strcmp(oscUrl, "(null)") != 0 && std::strcmp(oscUrl, "NULL") != 0); | |||
if (std::strcmp(name, "(none)") == 0) | |||
name = nullptr; | |||
@@ -496,6 +501,7 @@ int main(int argc, char* argv[]) | |||
if (useBridge) | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(std::strlen(argv[6]) == 6*2, 1); | |||
std::strncpy(bridgeBaseAudioName, argv[6], 6); | |||
std::strncpy(bridgeBaseControlName, argv[6]+6, 6); | |||
bridgeBaseAudioName[6] = '\0'; | |||
@@ -524,10 +530,7 @@ int main(int argc, char* argv[]) | |||
CarlaString clientName((name != nullptr) ? name : label); | |||
if (clientName.isEmpty()) | |||
{ | |||
File file(filename); | |||
clientName = file.getFileNameWithoutExtension().toRawUTF8(); | |||
} | |||
clientName = File(filename).getFileNameWithoutExtension().toRawUTF8(); | |||
// --------------------------------------------------------------------- | |||
// Set extraStuff | |||
@@ -548,6 +551,12 @@ int main(int argc, char* argv[]) | |||
CarlaPluginClient client(useBridge, (const char*)clientName, bridgeBaseAudioName, bridgeBaseControlName); | |||
if (! client.isOk()) | |||
{ | |||
carla_stderr("Failed to init engine, error was:\n%s", carla_get_last_error()); | |||
return 1; | |||
} | |||
// --------------------------------------------------------------------- | |||
// Init OSC | |||
@@ -585,6 +594,7 @@ int main(int argc, char* argv[]) | |||
client.ready(!useOsc); | |||
client.exec(); | |||
carla_set_engine_about_to_close(); | |||
carla_remove_plugin(0); | |||
ret = 0; | |||
@@ -51,21 +51,20 @@ endif | |||
WIN_BUILD_FLAGS = $(BUILD_PLUGIN_FLAGS) -DJACKBRIDGE_EXPORT | |||
WIN_32BIT_FLAGS = $(32BIT_FLAGS) | |||
WIN_64BIT_FLAGS = $(64BIT_FLAGS) | |||
WIN_LINK_FLAGS = $(LINK_PLUGIN_FLAGS) $(EXTRA_LIBS) -mwindows -lwinspool -lole32 -luuid -limm32 -lshell32 -lws2_32 -L../modules | |||
WIN_LINK_FLAGS = $(LINK_PLUGIN_FLAGS) -L../modules | |||
# -------------------------------------------------------------- | |||
# Plugin bridges (Native) | |||
NATIVE_BUILD_FLAGS = $(POSIX_BUILD_FLAGS) | |||
NATIVE_BUILD_FLAGS += -DWANT_NATIVE | |||
NATIVE_BUILD_FLAGS = $(POSIX_BUILD_FLAGS) -DWANT_NATIVE | |||
NATIVE_LINK_FLAGS = $(POSIX_LINK_FLAGS) | |||
NATIVE_LINK_FLAGS += $(JUCE_AUDIO_BASICS_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_CORE_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_DATA_STRUCTURES_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_EVENTS_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_GRAPHICS_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_GUI_BASICS_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_AUDIO_BASICS_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_CORE_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_DATA_STRUCTURES_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_EVENTS_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_GRAPHICS_LIBS) | |||
NATIVE_LINK_FLAGS += $(JUCE_GUI_BASICS_LIBS) | |||
ifeq ($(HAVE_FLUIDSYNTH),true) | |||
NATIVE_BUILD_FLAGS += -DWANT_FLUIDSYNTH | |||
@@ -95,7 +94,7 @@ endif | |||
ifeq ($(HAVE_OPENGL),true) | |||
NATIVE_BUILD_FLAGS += -DWANT_OPENGL | |||
NATIVE_LINK_FLAGS += $(shell pkg-config --libs gl) | |||
NATIVE_LINK_FLAGS += $(DGL_LIBS) | |||
endif | |||
ifeq ($(HAVE_ZYN_DEPS),true) | |||
@@ -400,7 +400,7 @@ class SearchPluginsThread(QThread): | |||
self.fContinueChecking = True | |||
self.fCurCount = 0 | |||
pluginCount = 0 | |||
settingsDB = QSettings("falkTX", "CarlaPlugins") | |||
settingsDB = QSettings("falkTX", "CarlaPlugins2") | |||
if self.fCheckLADSPA: pluginCount += 1 | |||
if self.fCheckDSSI: pluginCount += 1 | |||
@@ -1417,7 +1417,7 @@ class PluginDatabaseW(QDialog): | |||
self.fLastTableIndex += 1 | |||
def _reAddPlugins(self): | |||
settingsDB = QSettings("falkTX", "CarlaPlugins") | |||
settingsDB = QSettings("falkTX", "CarlaPlugins2") | |||
for x in range(self.ui.tableWidget.rowCount()): | |||
self.ui.tableWidget.removeRow(0) | |||
@@ -86,7 +86,7 @@ class CarlaPatchbayW(QGraphicsView): | |||
patchcanvas.setOptions(pOptions) | |||
patchcanvas.setFeatures(pFeatures) | |||
patchcanvas.init("Carla", self.scene, CanvasCallback, False) | |||
patchcanvas.init("Carla2", self.scene, CanvasCallback, False) | |||
tryCanvasSize = parent.fSavedSettings[CARLA_KEY_CANVAS_SIZE].split("x") | |||
@@ -368,7 +368,7 @@ class CarlaPatchbayW(QGraphicsView): | |||
patchcanvas.setOptions(pOptions) | |||
patchcanvas.setFeatures(pFeatures) | |||
patchcanvas.init("Carla", self.scene, CanvasCallback, False) | |||
patchcanvas.init("Carla2", self.scene, CanvasCallback, False) | |||
if Carla.host.is_engine_running(): | |||
Carla.host.patchbay_refresh() | |||
@@ -35,7 +35,7 @@ from carla_shared import * | |||
# ------------------------------------------------------------------------------------------------------------ | |||
class CarlaApplication(object): | |||
def __init__(self, appName = "Carla"): | |||
def __init__(self, appName = "Carla2"): | |||
object.__init__(self) | |||
if os.path.exists(os.path.join(CWD, "modules", "theme")): | |||
@@ -47,7 +47,7 @@ | |||
// Check for C++11 support | |||
#if defined(HAVE_CPP11_SUPPORT) | |||
# define CARLA_PROPER_CPP11_SUPPORT | |||
#elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) | |||
#elif defined(__GNUC__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) | |||
# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 | |||
# define CARLA_PROPER_CPP11_SUPPORT | |||
# if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 | |||
@@ -39,22 +39,17 @@ public: | |||
{ | |||
setVisible(false); | |||
setAlwaysOnTop(true); | |||
setDropShadowEnabled(false); | |||
setOpaque(true); | |||
//setResizable(false, false); | |||
//setUsingNativeTitleBar(false); | |||
setResizable(false, false); | |||
setUsingNativeTitleBar(true); | |||
} | |||
void show(Component* const comp) | |||
{ | |||
fClosed = false; | |||
const int width = comp->getWidth(); | |||
const int height = comp->getHeight()+getTitleBarHeight(); | |||
centreWithSize(width, height); | |||
setContentNonOwned(comp, false); | |||
setSize(width, height); | |||
centreWithSize(comp->getWidth(), comp->getHeight()); | |||
setContentNonOwned(comp, true); | |||
if (! isOnDesktop()) | |||
addToDesktop(); | |||
@@ -389,6 +384,8 @@ protected: | |||
void uiShow(const bool show) override | |||
{ | |||
MessageManagerLock mmLock; | |||
if (show) | |||
{ | |||
if (fWindow == nullptr) | |||
@@ -399,8 +396,8 @@ protected: | |||
if (fView == nullptr) | |||
{ | |||
fView = new PeggyViewComponent(1, fSettings, this); | |||
fView->setSize(207, 320); | |||
fView = new PeggyViewComponent(fSettings, this, true); | |||
fView->setSize(202, 275); | |||
} | |||
fWindow->show(fView); | |||
@@ -431,6 +428,7 @@ protected: | |||
if (fView == nullptr) | |||
return; | |||
MessageManagerLock mmLock; | |||
fView->update(); | |||
} | |||
@@ -447,6 +445,7 @@ protected: | |||
if (fWindow == nullptr) | |||
return; | |||
MessageManagerLock mmLock; | |||
fWindow->setName(uiName); | |||
} | |||
@@ -76,22 +76,17 @@ public: | |||
{ | |||
setVisible(false); | |||
setAlwaysOnTop(true); | |||
setDropShadowEnabled(false); | |||
setOpaque(true); | |||
//setResizable(false, false); | |||
//setUsingNativeTitleBar(false); | |||
setResizable(false, false); | |||
setUsingNativeTitleBar(true); | |||
} | |||
void show(Component* const comp) | |||
{ | |||
fClosed = false; | |||
const int width = comp->getWidth(); | |||
const int height = comp->getHeight()+getTitleBarHeight(); | |||
centreWithSize(width, height); | |||
setContentNonOwned(comp, false); | |||
setSize(width, height); | |||
centreWithSize(comp->getWidth(), comp->getHeight()); | |||
setContentNonOwned(comp, true); | |||
if (! isOnDesktop()) | |||
addToDesktop(); | |||
@@ -121,7 +116,7 @@ protected: | |||
} | |||
private: | |||
bool fClosed; | |||
volatile bool fClosed; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(HelperWindow2) | |||
}; | |||
@@ -301,11 +296,11 @@ public: | |||
TB6->setClickingTogglesState(true); | |||
TB6->setToggleState(false, dontSendNotification); | |||
addChildComponent(p1 = new PeggyViewComponent(1, arpSet1, this)); | |||
addChildComponent(p1 = new PeggyViewComponent(arpSet1, this)); | |||
p1->setLookAndFeel(&mlaf); | |||
addChildComponent(p2 = new PeggyViewComponent(2, arpSet2, this)); | |||
addChildComponent(p2 = new PeggyViewComponent(arpSet2, this)); | |||
p2->setLookAndFeel(&mlaf); | |||
addChildComponent(p3 = new PeggyViewComponent(3, arpSet3, this)); | |||
addChildComponent(p3 = new PeggyViewComponent(arpSet3, this)); | |||
p3->setLookAndFeel(&mlaf); | |||
setSize(800,500); | |||
@@ -1081,6 +1076,8 @@ protected: | |||
void uiShow(const bool show) override | |||
{ | |||
MessageManagerLock mmLock; | |||
if (show) | |||
{ | |||
if (fWindow == nullptr) | |||
@@ -1208,6 +1205,7 @@ protected: | |||
if (fWindow == nullptr) | |||
return; | |||
MessageManagerLock mmLock; | |||
fWindow->setName(uiName); | |||
} | |||
@@ -39,6 +39,7 @@ | |||
#include "gui/BoolGridComponent.h" | |||
#include "gui/SliderFieldComponent.h" | |||
#include "lookandfeel/MyLookAndFeel.h" | |||
class PeggyViewComponent : public Component, | |||
public ChangeListener, | |||
@@ -54,10 +55,10 @@ public: | |||
virtual void arpParameterChanged(const uint32_t id) = 0; | |||
}; | |||
PeggyViewComponent(const int partID, VexArpSettings& arpSet, Callback* const callback) | |||
: fPart(partID), | |||
fArpSettings(arpSet), | |||
fCallback(callback) | |||
PeggyViewComponent(VexArpSettings& arpSet, Callback* const callback, const bool isStandalone = false) | |||
: fArpSettings(arpSet), | |||
fCallback(callback), | |||
fIsStandalone(isStandalone) | |||
{ | |||
addAndMakeVisible(boolGrid = new BoolGridComponent()); | |||
boolGrid->addChangeListener(this); | |||
@@ -134,6 +135,12 @@ public: | |||
onOffBtn->addListener(this); | |||
onOffBtn->setClickingTogglesState(true); | |||
if (isStandalone) | |||
{ | |||
static MyLookAndFeel mlaf; | |||
setLookAndFeel(&mlaf); | |||
} | |||
update(); | |||
} | |||
@@ -163,10 +170,17 @@ public: | |||
g.setGradientFill(ColourGradient(Colour(0xffffffff), 0.0f, 0.0f, | |||
Colour(0xff888899), (float)getWidth(), (float)getHeight(), false)); | |||
g.fillRect(0,0,getWidth() - 5, getHeight() - 5); | |||
if (fIsStandalone) | |||
{ | |||
g.fillRect(0, 0, getWidth(), getHeight()); | |||
} | |||
else | |||
{ | |||
g.fillRect(0, 0, getWidth() - 5, getHeight() - 5); | |||
g.setColour(Colours::black); | |||
g.drawRect(0,0,getWidth() - 5, getHeight() - 5); | |||
g.setColour(Colours::black); | |||
g.drawRect(0, 0, getWidth() - 5, getHeight() - 5); | |||
} | |||
} | |||
void changeListenerCallback(ChangeBroadcaster* caller) override | |||
@@ -244,9 +258,9 @@ public: | |||
} | |||
private: | |||
const int fPart; | |||
VexArpSettings& fArpSettings; | |||
Callback* const fCallback; | |||
const bool fIsStandalone; | |||
ScopedPointer<BoolGridComponent> boolGrid; | |||
ScopedPointer<SliderFieldComponent> sliderField; | |||
@@ -29,7 +29,7 @@ | |||
#if defined(HAVE_CPP11_SUPPORT) | |||
# define PROPER_CPP11_SUPPORT | |||
#elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) | |||
#elif defined(__GNUC__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) | |||
# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 | |||
# define PROPER_CPP11_SUPPORT | |||
# if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 | |||
@@ -40,7 +40,7 @@ | |||
#ifndef PROPER_CPP11_SUPPORT | |||
# define override | |||
# define noexcept | |||
# define noexcept throw() | |||
# define nullptr (0) | |||
#endif | |||
@@ -8,8 +8,9 @@ include ../../../Makefile.mk | |||
# -------------------------------------------------------------- | |||
BUILD_C_FLAGS += -I. | |||
BUILD_CXX_FLAGS += -I. | |||
BUILD_C_FLAGS += $(DGL_FLAGS) -I. | |||
BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. | |||
LINK_FLAGS += $(DGL_LIBS) | |||
OBJS = \ | |||
src/App.cpp.o \ | |||
@@ -71,22 +72,12 @@ OBJS_win64 = \ | |||
src/Widget.cpp.win64.o \ | |||
src/Window.cpp.win64.o | |||
ifeq ($(LINUX),true) | |||
BASE_FLAGS += $(shell pkg-config --cflags gl x11) | |||
LINK_FLAGS += $(shell pkg-config --libs gl x11) | |||
endif | |||
ifeq ($(MACOS),true) | |||
LINK_FLAGS += -framework OpenGL -framework Cocoa | |||
OBJS += src/pugl/pugl_osx_extended.m.o | |||
OBJS_posix32 += src/pugl/pugl_osx_extended.m.posix32.o | |||
OBJS_posix64 += src/pugl/pugl_osx_extended.m.posix64.o | |||
endif | |||
ifeq ($(WIN32),true) | |||
LINK_FLAGS += -lopengl32 -lgdi32 | |||
endif | |||
# -------------------------------------------------------------- | |||
all: ../../dgl.a | |||
@@ -85,7 +85,7 @@ | |||
#if defined(HAVE_CPP11_SUPPORT) | |||
# define PROPER_CPP11_SUPPORT | |||
#elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) | |||
#elif defined(__GNUC__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) | |||
# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 | |||
# define PROPER_CPP11_SUPPORT | |||
# if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 | |||
@@ -457,7 +457,10 @@ struct JackBridge { | |||
~JackBridge() | |||
{ | |||
if (lib != nullptr) | |||
{ | |||
lib_close(lib); | |||
lib = nullptr; | |||
} | |||
} | |||
}; | |||
@@ -251,6 +251,15 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplie | |||
#endif | |||
} | |||
void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept | |||
{ | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); | |||
#else | |||
copyWithMultiply (dest, src, -1.0f, num); | |||
#endif | |||
} | |||
void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept | |||
{ | |||
#if JUCE_USE_SSE_INTRINSICS | |||
@@ -332,3 +341,12 @@ float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int nu | |||
return juce::findMaximum (src, num); | |||
#endif | |||
} | |||
void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept | |||
{ | |||
#if JUCE_USE_SSE_INTRINSICS | |||
if (FloatVectorHelpers::isSSE2Available()) | |||
_MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); | |||
#endif | |||
(void) shouldEnable; | |||
} |
@@ -62,6 +62,9 @@ public: | |||
/** Multiplies each of the destination values by a fixed multiplier. */ | |||
static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; | |||
/** Copies a source vector to a destination, negating each value. */ | |||
static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; | |||
/** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ | |||
static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; | |||
@@ -73,6 +76,11 @@ public: | |||
/** Finds the maximum value in the given array. */ | |||
static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept; | |||
/** On Intel CPUs, this method enables or disables the SSE flush-to-zero mode. | |||
Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE | |||
*/ | |||
static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; | |||
}; | |||
@@ -43,7 +43,7 @@ public: | |||
static Type decibelsToGain (const Type decibels, | |||
const Type minusInfinityDb = (Type) defaultMinusInfinitydB) | |||
{ | |||
return decibels > minusInfinityDb ? powf ((Type) 10.0, decibels * (Type) 0.05) | |||
return decibels > minusInfinityDb ? std::pow ((Type) 10.0, decibels * (Type) 0.05) | |||
: Type(); | |||
} | |||
@@ -1,7 +1,7 @@ | |||
{ | |||
"id": "juce_audio_basics", | |||
"name": "JUCE audio and midi data classes", | |||
"version": "2.1.3", | |||
"version": "2.1.6", | |||
"description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
@@ -26,17 +26,17 @@ namespace MidiBufferHelpers | |||
{ | |||
inline int getEventTime (const void* const d) noexcept | |||
{ | |||
return *static_cast <const int*> (d); | |||
return *static_cast<const int32*> (d); | |||
} | |||
inline uint16 getEventDataSize (const void* const d) noexcept | |||
{ | |||
return *reinterpret_cast <const uint16*> (static_cast <const char*> (d) + sizeof (int)); | |||
return *reinterpret_cast<const uint16*> (static_cast<const char*> (d) + sizeof (int32)); | |||
} | |||
inline uint16 getEventTotalSize (const void* const d) noexcept | |||
{ | |||
return getEventDataSize (d) + sizeof (int) + sizeof (uint16); | |||
return getEventDataSize (d) + sizeof (int32) + sizeof (uint16); | |||
} | |||
static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept | |||
@@ -67,68 +67,44 @@ namespace MidiBufferHelpers | |||
return size; | |||
} | |||
} | |||
//============================================================================== | |||
MidiBuffer::MidiBuffer() noexcept | |||
: bytesUsed (0) | |||
{ | |||
} | |||
static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept | |||
{ | |||
while (d < endData && getEventTime (d) <= samplePosition) | |||
d += getEventTotalSize (d); | |||
MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept | |||
: bytesUsed (0) | |||
{ | |||
addEvent (message, 0); | |||
return d; | |||
} | |||
} | |||
MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept | |||
: data (other.data), | |||
bytesUsed (other.bytesUsed) | |||
{ | |||
} | |||
//============================================================================== | |||
MidiBuffer::MidiBuffer() noexcept {} | |||
MidiBuffer::~MidiBuffer() {} | |||
MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {} | |||
MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept | |||
{ | |||
bytesUsed = other.bytesUsed; | |||
data = other.data; | |||
return *this; | |||
} | |||
void MidiBuffer::swapWith (MidiBuffer& other) noexcept | |||
{ | |||
data.swapWith (other.data); | |||
std::swap (bytesUsed, other.bytesUsed); | |||
} | |||
MidiBuffer::~MidiBuffer() | |||
{ | |||
} | |||
inline uint8* MidiBuffer::getData() const noexcept | |||
MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept | |||
{ | |||
return static_cast <uint8*> (data.getData()); | |||
addEvent (message, 0); | |||
} | |||
void MidiBuffer::clear() noexcept | |||
{ | |||
bytesUsed = 0; | |||
} | |||
void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); } | |||
void MidiBuffer::clear() noexcept { data.clearQuick(); } | |||
void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated (minimumNumBytes); } | |||
bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; } | |||
void MidiBuffer::clear (const int startSample, const int numSamples) | |||
{ | |||
uint8* const start = findEventAfter (getData(), startSample - 1); | |||
uint8* const end = findEventAfter (start, startSample + numSamples - 1); | |||
if (end > start) | |||
{ | |||
const int bytesToMove = bytesUsed - (int) (end - getData()); | |||
uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); | |||
uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); | |||
if (bytesToMove > 0) | |||
memmove (start, end, (size_t) bytesToMove); | |||
bytesUsed -= (int) (end - start); | |||
} | |||
data.removeRange (start - data.begin(), end - data.begin()); | |||
} | |||
void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) | |||
@@ -138,27 +114,19 @@ void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) | |||
void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber) | |||
{ | |||
const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast <const uint8*> (newData), maxBytes); | |||
const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast<const uint8*> (newData), maxBytes); | |||
if (numBytes > 0) | |||
{ | |||
size_t spaceNeeded = (size_t) bytesUsed + (size_t) numBytes + sizeof (int) + sizeof (uint16); | |||
data.ensureSize ((spaceNeeded + spaceNeeded / 2 + 8) & ~(size_t) 7); | |||
uint8* d = findEventAfter (getData(), sampleNumber); | |||
const int bytesToMove = bytesUsed - (int) (d - getData()); | |||
const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); | |||
const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); | |||
if (bytesToMove > 0) | |||
memmove (d + numBytes + sizeof (int) + sizeof (uint16), d, (size_t) bytesToMove); | |||
data.insertMultiple (offset, 0, newItemSize); | |||
*reinterpret_cast <int*> (d) = sampleNumber; | |||
d += sizeof (int); | |||
*reinterpret_cast <uint16*> (d) = (uint16) numBytes; | |||
d += sizeof (uint16); | |||
memcpy (d, newData, (size_t) numBytes); | |||
bytesUsed += sizeof (int) + sizeof (uint16) + (size_t) numBytes; | |||
uint8* const d = data.begin() + offset; | |||
*reinterpret_cast<int32*> (d) = sampleNumber; | |||
*reinterpret_cast<uint16*> (d + 4) = (uint16) numBytes; | |||
memcpy (d + 6, newData, (size_t) numBytes); | |||
} | |||
} | |||
@@ -180,45 +148,30 @@ void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, | |||
} | |||
} | |||
void MidiBuffer::ensureSize (size_t minimumNumBytes) | |||
{ | |||
data.ensureSize (minimumNumBytes); | |||
} | |||
bool MidiBuffer::isEmpty() const noexcept | |||
{ | |||
return bytesUsed == 0; | |||
} | |||
int MidiBuffer::getNumEvents() const noexcept | |||
{ | |||
int n = 0; | |||
const uint8* d = getData(); | |||
const uint8* const end = d + bytesUsed; | |||
const uint8* const end = data.end(); | |||
while (d < end) | |||
{ | |||
for (const uint8* d = data.begin(); d < end; ++n) | |||
d += MidiBufferHelpers::getEventTotalSize (d); | |||
++n; | |||
} | |||
return n; | |||
} | |||
int MidiBuffer::getFirstEventTime() const noexcept | |||
{ | |||
return bytesUsed > 0 ? MidiBufferHelpers::getEventTime (data.getData()) : 0; | |||
return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0; | |||
} | |||
int MidiBuffer::getLastEventTime() const noexcept | |||
{ | |||
if (bytesUsed == 0) | |||
if (data.size() == 0) | |||
return 0; | |||
const uint8* d = getData(); | |||
const uint8* const endData = d + bytesUsed; | |||
const uint8* const endData = data.end(); | |||
for (;;) | |||
for (const uint8* d = data.begin();;) | |||
{ | |||
const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d); | |||
@@ -229,19 +182,9 @@ int MidiBuffer::getLastEventTime() const noexcept | |||
} | |||
} | |||
uint8* MidiBuffer::findEventAfter (uint8* d, const int samplePosition) const noexcept | |||
{ | |||
const uint8* const endData = getData() + bytesUsed; | |||
while (d < endData && MidiBufferHelpers::getEventTime (d) <= samplePosition) | |||
d += MidiBufferHelpers::getEventTotalSize (d); | |||
return d; | |||
} | |||
//============================================================================== | |||
MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept | |||
: buffer (b), data (b.getData()) | |||
: buffer (b), data (b.data.begin()) | |||
{ | |||
} | |||
@@ -251,8 +194,8 @@ MidiBuffer::Iterator::~Iterator() noexcept | |||
void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept | |||
{ | |||
data = buffer.getData(); | |||
const uint8* dataEnd = data + buffer.bytesUsed; | |||
data = buffer.data.begin(); | |||
const uint8* const dataEnd = buffer.data.end(); | |||
while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition) | |||
data += MidiBufferHelpers::getEventTotalSize (data); | |||
@@ -260,27 +203,26 @@ void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noex | |||
bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept | |||
{ | |||
if (data >= buffer.getData() + buffer.bytesUsed) | |||
if (data >= buffer.data.end()) | |||
return false; | |||
samplePosition = MidiBufferHelpers::getEventTime (data); | |||
numBytes = MidiBufferHelpers::getEventDataSize (data); | |||
data += sizeof (int) + sizeof (uint16); | |||
midiData = data; | |||
data += numBytes; | |||
const int itemSize = MidiBufferHelpers::getEventDataSize (data); | |||
numBytes = itemSize; | |||
midiData = data + sizeof (int32) + sizeof (uint16); | |||
data += sizeof (int32) + sizeof (uint16) + itemSize; | |||
return true; | |||
} | |||
bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept | |||
{ | |||
if (data >= buffer.getData() + buffer.bytesUsed) | |||
if (data >= buffer.data.end()) | |||
return false; | |||
samplePosition = MidiBufferHelpers::getEventTime (data); | |||
const int numBytes = MidiBufferHelpers::getEventDataSize (data); | |||
data += sizeof (int) + sizeof (uint16); | |||
result = MidiMessage (data, numBytes, samplePosition); | |||
result = MidiMessage (data + sizeof (int32) + sizeof (uint16), numBytes, samplePosition); | |||
data += numBytes; | |||
return true; | |||
@@ -71,7 +71,6 @@ public: | |||
void clear (int start, int numSamples); | |||
/** Returns true if the buffer is empty. | |||
To actually retrieve the events, use a MidiBuffer::Iterator object | |||
*/ | |||
bool isEmpty() const noexcept; | |||
@@ -137,13 +136,11 @@ public: | |||
int sampleDeltaToAdd); | |||
/** Returns the sample number of the first event in the buffer. | |||
If the buffer's empty, this will just return 0. | |||
*/ | |||
int getFirstEventTime() const noexcept; | |||
/** Returns the sample number of the last event in the buffer. | |||
If the buffer's empty, this will just return 0. | |||
*/ | |||
int getLastEventTime() const noexcept; | |||
@@ -154,7 +151,7 @@ public: | |||
This is a quick operation, because no memory allocating or copying is done, it | |||
just swaps the internal state of the two buffers. | |||
*/ | |||
void swapWith (MidiBuffer& other) noexcept; | |||
void swapWith (MidiBuffer&) noexcept; | |||
/** Preallocates some memory for the buffer to use. | |||
This helps to avoid needing to reallocate space when the buffer has messages | |||
@@ -222,15 +219,13 @@ public: | |||
JUCE_DECLARE_NON_COPYABLE (Iterator) | |||
}; | |||
private: | |||
//============================================================================== | |||
friend class MidiBuffer::Iterator; | |||
MemoryBlock data; | |||
int bytesUsed; | |||
uint8* getData() const noexcept; | |||
uint8* findEventAfter (uint8*, int samplePosition) const noexcept; | |||
/** The raw data holding this buffer. | |||
Obviously access to this data is provided at your own risk. Its internal format could | |||
change in future, so don't write code that relies on it! | |||
*/ | |||
Array<uint8> data; | |||
private: | |||
JUCE_LEAK_DETECTOR (MidiBuffer) | |||
}; | |||
@@ -372,46 +372,48 @@ void MidiFile::writeTrack (OutputStream& mainOut, const int trackNum) | |||
int lastTick = 0; | |||
uint8 lastStatusByte = 0; | |||
bool endOfTrackEventWritten = false; | |||
for (int i = 0; i < ms.getNumEvents(); ++i) | |||
{ | |||
const MidiMessage& mm = ms.getEventPointer(i)->message; | |||
if (! mm.isEndOfTrackMetaEvent()) | |||
{ | |||
const int tick = roundToInt (mm.getTimeStamp()); | |||
const int delta = jmax (0, tick - lastTick); | |||
MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta); | |||
lastTick = tick; | |||
if (mm.isEndOfTrackMetaEvent()) | |||
endOfTrackEventWritten = true; | |||
const uint8* data = mm.getRawData(); | |||
int dataSize = mm.getRawDataSize(); | |||
const int tick = roundToInt (mm.getTimeStamp()); | |||
const int delta = jmax (0, tick - lastTick); | |||
MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta); | |||
lastTick = tick; | |||
const uint8 statusByte = data[0]; | |||
const uint8* data = mm.getRawData(); | |||
int dataSize = mm.getRawDataSize(); | |||
if (statusByte == lastStatusByte | |||
&& (statusByte & 0xf0) != 0xf0 | |||
&& dataSize > 1 | |||
&& i > 0) | |||
{ | |||
++data; | |||
--dataSize; | |||
} | |||
else if (statusByte == 0xf0) // Write sysex message with length bytes. | |||
{ | |||
out.writeByte ((char) statusByte); | |||
const uint8 statusByte = data[0]; | |||
++data; | |||
--dataSize; | |||
if (statusByte == lastStatusByte | |||
&& (statusByte & 0xf0) != 0xf0 | |||
&& dataSize > 1 | |||
&& i > 0) | |||
{ | |||
++data; | |||
--dataSize; | |||
} | |||
else if (statusByte == 0xf0) // Write sysex message with length bytes. | |||
{ | |||
out.writeByte ((char) statusByte); | |||
MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize); | |||
} | |||
++data; | |||
--dataSize; | |||
out.write (data, (size_t) dataSize); | |||
lastStatusByte = statusByte; | |||
MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize); | |||
} | |||
out.write (data, (size_t) dataSize); | |||
lastStatusByte = statusByte; | |||
} | |||
if (! endOfTrackEventWritten) | |||
{ | |||
out.writeByte (0); // (tick delta) | |||
const MidiMessage m (MidiMessage::endOfTrack()); | |||
@@ -777,22 +777,19 @@ MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int | |||
++powerOfTwo; | |||
} | |||
const uint8 d[] = { 0xff, 0x58, 0x04, (uint8) numerator, | |||
(uint8) powerOfTwo, 1, 96 }; | |||
const uint8 d[] = { 0xff, 0x58, 0x04, (uint8) numerator, (uint8) powerOfTwo, 1, 96 }; | |||
return MidiMessage (d, 7, 0.0); | |||
} | |||
MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept | |||
{ | |||
const uint8 d[] = { 0xff, 0x20, 0x01, (uint8) jlimit (0, 0xff, channel - 1) }; | |||
return MidiMessage (d, 4, 0.0); | |||
} | |||
bool MidiMessage::isKeySignatureMetaEvent() const noexcept | |||
{ | |||
return getMetaEventType() == 89; | |||
return getMetaEventType() == 0x59; | |||
} | |||
int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept | |||
@@ -805,6 +802,14 @@ bool MidiMessage::isKeySignatureMajorKey() const noexcept | |||
return getMetaEventData()[1] == 0; | |||
} | |||
MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey) | |||
{ | |||
jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7); | |||
const uint8 d[] = { 0xff, 0x59, 0x02, (uint8) numberOfSharpsOrFlats, isMinorKey ? (uint8) 1 : (uint8) 0 }; | |||
return MidiMessage (d, 5, 0.0); | |||
} | |||
MidiMessage MidiMessage::endOfTrack() noexcept | |||
{ | |||
return MidiMessage (0xff, 0x2f, 0, 0.0); | |||
@@ -958,9 +963,9 @@ double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOf | |||
return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0); | |||
} | |||
String MidiMessage::getGMInstrumentName (const int n) | |||
const char* MidiMessage::getGMInstrumentName (const int n) | |||
{ | |||
const char* names[] = | |||
static const char* names[] = | |||
{ | |||
"Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", | |||
"Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", | |||
@@ -987,12 +992,12 @@ String MidiMessage::getGMInstrumentName (const int n) | |||
"Applause", "Gunshot" | |||
}; | |||
return isPositiveAndBelow (n, (int) 128) ? names[n] : (const char*) 0; | |||
return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; | |||
} | |||
String MidiMessage::getGMInstrumentBankName (const int n) | |||
const char* MidiMessage::getGMInstrumentBankName (const int n) | |||
{ | |||
const char* names[] = | |||
static const char* names[] = | |||
{ | |||
"Piano", "Chromatic Percussion", "Organ", "Guitar", | |||
"Bass", "Strings", "Ensemble", "Brass", | |||
@@ -1000,12 +1005,12 @@ String MidiMessage::getGMInstrumentBankName (const int n) | |||
"Synth Effects", "Ethnic", "Percussive", "Sound Effects" | |||
}; | |||
return isPositiveAndBelow (n, (int) 16) ? names[n] : (const char*) 0; | |||
return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; | |||
} | |||
String MidiMessage::getRhythmInstrumentName (const int n) | |||
const char* MidiMessage::getRhythmInstrumentName (const int n) | |||
{ | |||
const char* names[] = | |||
static const char* names[] = | |||
{ | |||
"Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", | |||
"Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", | |||
@@ -1018,12 +1023,12 @@ String MidiMessage::getRhythmInstrumentName (const int n) | |||
"Mute Triangle", "Open Triangle" | |||
}; | |||
return (n >= 35 && n <= 81) ? names [n - 35] : (const char*) nullptr; | |||
return (n >= 35 && n <= 81) ? names [n - 35] : nullptr; | |||
} | |||
String MidiMessage::getControllerName (const int n) | |||
const char* MidiMessage::getControllerName (const int n) | |||
{ | |||
const char* names[] = | |||
static const char* names[] = | |||
{ | |||
"Bank Select", "Modulation Wheel (coarse)", "Breath controller (coarse)", | |||
0, "Foot Pedal (coarse)", "Portamento Time (coarse)", | |||
@@ -1049,5 +1054,5 @@ String MidiMessage::getControllerName (const int n) | |||
"Poly Operation" | |||
}; | |||
return isPositiveAndBelow (n, (int) 128) ? names[n] : (const char*) nullptr; | |||
return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; | |||
} |
@@ -622,6 +622,14 @@ public: | |||
*/ | |||
bool isKeySignatureMajorKey() const noexcept; | |||
/** Creates a key-signature meta-event. | |||
@param numberOfSharpsOrFlats if positive, this indicates the number of sharps | |||
in the key; if negative, the number of flats | |||
@param isMinorKey if true, the key is minor; if false, it is major | |||
@see isKeySignatureMetaEvent | |||
*/ | |||
static MidiMessage keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey); | |||
//============================================================================== | |||
/** Returns true if this is a 'channel' meta-event. | |||
@@ -881,30 +889,27 @@ public: | |||
*/ | |||
static double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept; | |||
/** Returns the standard name of a GM instrument. | |||
/** Returns the standard name of a GM instrument, or nullptr if unknown for this index. | |||
@param midiInstrumentNumber the program number 0 to 127 | |||
@see getProgramChangeNumber | |||
*/ | |||
static String getGMInstrumentName (int midiInstrumentNumber); | |||
/** Returns the name of a bank of GM instruments. | |||
static const char* getGMInstrumentName (int midiInstrumentNumber); | |||
/** Returns the name of a bank of GM instruments, or nullptr if unknown for this bank number. | |||
@param midiBankNumber the bank, 0 to 15 | |||
*/ | |||
static String getGMInstrumentBankName (int midiBankNumber); | |||
/** Returns the standard name of a channel 10 percussion sound. | |||
static const char* getGMInstrumentBankName (int midiBankNumber); | |||
/** Returns the standard name of a channel 10 percussion sound, or nullptr if unknown for this note number. | |||
@param midiNoteNumber the key number, 35 to 81 | |||
*/ | |||
static String getRhythmInstrumentName (int midiNoteNumber); | |||
/** Returns the name of a controller type number. | |||
static const char* getRhythmInstrumentName (int midiNoteNumber); | |||
/** Returns the name of a controller type number, or nullptr if unknown for this controller number. | |||
@see getControllerNumber | |||
*/ | |||
static String getControllerName (int controllerNumber); | |||
static const char* getControllerName (int controllerNumber); | |||
private: | |||
//============================================================================== | |||
@@ -1,7 +1,7 @@ | |||
{ | |||
"id": "juce_audio_devices", | |||
"name": "JUCE audio and midi I/O device classes", | |||
"version": "2.1.3", | |||
"version": "2.1.6", | |||
"description": "Classes to play and record from audio and midi i/o devices.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
@@ -67,17 +67,16 @@ public: | |||
return s; | |||
} | |||
int getNumSampleRates() { return 1; } | |||
double getSampleRate (int index) { return sampleRate; } | |||
int getNumSampleRates() { return jmax (1, sampleRates.size()); } | |||
double getSampleRate (int index) { return sampleRates.size() > 0 ? sampleRates [index] : sampleRate; } | |||
int getNumBufferSizesAvailable() { return 6; } | |||
int getBufferSizeSamples (int index) { return 1 << (jlimit (0, 5, index) + 6); } | |||
int getDefaultBufferSize() { return 1024; } | |||
String open (const BigInteger& inputChannels, | |||
const BigInteger& outputChannels, | |||
double sampleRate, | |||
int bufferSize) | |||
String open (const BigInteger& inputChannelsWanted, | |||
const BigInteger& outputChannelsWanted, | |||
double targetSampleRate, int bufferSize) | |||
{ | |||
close(); | |||
@@ -86,29 +85,26 @@ public: | |||
// xxx set up channel mapping | |||
activeOutputChans = outputChannels; | |||
activeOutputChans = outputChannelsWanted; | |||
activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); | |||
numOutputChannels = activeOutputChans.countNumberOfSetBits(); | |||
monoOutputChannelNumber = activeOutputChans.findNextSetBit (0); | |||
activeInputChans = inputChannels; | |||
activeInputChans = inputChannelsWanted; | |||
activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); | |||
numInputChannels = activeInputChans.countNumberOfSetBits(); | |||
monoInputChannelNumber = activeInputChans.findNextSetBit (0); | |||
AudioSessionSetActive (true); | |||
UInt32 audioCategory = (numInputChannels > 0 && audioInputIsAvailable) ? kAudioSessionCategory_PlayAndRecord | |||
: kAudioSessionCategory_MediaPlayback; | |||
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory); | |||
if (audioCategory == kAudioSessionCategory_PlayAndRecord) | |||
if (numInputChannels > 0 && audioInputIsAvailable) | |||
{ | |||
setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_PlayAndRecord); | |||
setSessionUInt32Property (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, 1); | |||
} | |||
else | |||
{ | |||
// (note: mustn't set this until after the audio category property has been set) | |||
UInt32 allowBluetoothInput = 1; | |||
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, | |||
sizeof (allowBluetoothInput), &allowBluetoothInput); | |||
setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback); | |||
} | |||
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); | |||
@@ -116,9 +112,11 @@ public: | |||
fixAudioRouteIfSetToReceiver(); | |||
updateDeviceInfo(); | |||
Float32 bufferDuration = preferredBufferSize / sampleRate; | |||
AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof (bufferDuration), &bufferDuration); | |||
actualBufferSize = preferredBufferSize; | |||
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate); | |||
updateSampleRates(); | |||
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate); | |||
updateCurrentBufferSize(); | |||
prepareFloatBuffers (actualBufferSize); | |||
@@ -134,6 +132,9 @@ public: | |||
if (isRunning) | |||
{ | |||
isRunning = false; | |||
setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback); | |||
AudioSessionRemovePropertyListenerWithUserData (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); | |||
AudioSessionSetActive (false); | |||
@@ -193,6 +194,7 @@ private: | |||
//================================================================================================== | |||
CriticalSection callbackLock; | |||
Float64 sampleRate; | |||
Array<Float64> sampleRates; | |||
int numInputChannels, numOutputChannels; | |||
int preferredBufferSize, actualBufferSize; | |||
bool isRunning; | |||
@@ -239,7 +241,7 @@ private: | |||
if (callback != nullptr) | |||
{ | |||
// This shouldn't ever get triggered, but please let me know if it does! | |||
jassert (numFrames <= floatData.getNumSamples()); | |||
jassert ((int) numFrames <= floatData.getNumSamples()); | |||
if (audioInputIsAvailable && numInputChannels > 0) | |||
{ | |||
@@ -309,11 +311,47 @@ private: | |||
void updateDeviceInfo() | |||
{ | |||
UInt32 size = sizeof (sampleRate); | |||
AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate); | |||
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate); | |||
getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable); | |||
} | |||
void updateSampleRates() | |||
{ | |||
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate); | |||
sampleRates.clear(); | |||
sampleRates.add (sampleRate); | |||
const int commonSampleRates[] = { 8000, 16000, 22050, 32000, 44100, 48000 }; | |||
for (int i = 0; i < numElementsInArray (commonSampleRates); ++i) | |||
{ | |||
Float64 rate = (Float64) commonSampleRates[i]; | |||
if (rate != sampleRate) | |||
{ | |||
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, rate); | |||
Float64 actualSampleRate = 0.0; | |||
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, actualSampleRate); | |||
if (actualSampleRate == rate) | |||
sampleRates.add (actualSampleRate); | |||
} | |||
} | |||
DefaultElementComparator<Float64> comparator; | |||
sampleRates.sort (comparator); | |||
size = sizeof (audioInputIsAvailable); | |||
AudioSessionGetProperty (kAudioSessionProperty_AudioInputAvailable, &size, &audioInputIsAvailable); | |||
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, sampleRate); | |||
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate); | |||
} | |||
void updateCurrentBufferSize() | |||
{ | |||
Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f; | |||
getSessionProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, bufferDuration); | |||
actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); | |||
} | |||
void routingChanged (const void* propertyValue) | |||
@@ -349,11 +387,7 @@ private: | |||
UInt32 formatSize = sizeof (format); | |||
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize); | |||
Float32 bufferDuration = preferredBufferSize / sampleRate; | |||
UInt32 bufferDurationSize = sizeof (bufferDuration); | |||
AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, &bufferDurationSize, &bufferDuration); | |||
actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); | |||
updateCurrentBufferSize(); | |||
AudioOutputUnitStart (audioUnit); | |||
} | |||
} | |||
@@ -424,9 +458,9 @@ private: | |||
format.mFormatID = kAudioFormatLinearPCM; | |||
format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kAudioFormatFlagsNativeEndian; | |||
format.mBitsPerChannel = 8 * sizeof (short); | |||
format.mChannelsPerFrame = numChannels; | |||
format.mChannelsPerFrame = (UInt32) numChannels; | |||
format.mFramesPerPacket = 1; | |||
format.mBytesPerFrame = format.mBytesPerPacket = numChannels * sizeof (short); | |||
format.mBytesPerFrame = format.mBytesPerPacket = (UInt32) numChannels * sizeof (short); | |||
} | |||
bool createAudioUnit() | |||
@@ -486,23 +520,29 @@ private: | |||
static void fixAudioRouteIfSetToReceiver() | |||
{ | |||
CFStringRef audioRoute = 0; | |||
UInt32 propertySize = sizeof (audioRoute); | |||
if (AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &audioRoute) == noErr) | |||
if (getSessionProperty (kAudioSessionProperty_AudioRoute, audioRoute) == noErr) | |||
{ | |||
NSString* route = (NSString*) audioRoute; | |||
//DBG ("audio route: " + nsStringToJuce (route)); | |||
if ([route hasPrefix: @"Receiver"]) | |||
{ | |||
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; | |||
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride); | |||
} | |||
setSessionUInt32Property (kAudioSessionProperty_OverrideAudioRoute, kAudioSessionOverrideAudioRoute_Speaker); | |||
CFRelease (audioRoute); | |||
} | |||
} | |||
template <typename Type> | |||
static OSStatus getSessionProperty (AudioSessionPropertyID propID, Type& result) noexcept | |||
{ | |||
UInt32 valueSize = sizeof (result); | |||
return AudioSessionGetProperty (propID, &valueSize, &result); | |||
} | |||
static void setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } | |||
static void setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } | |||
JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) | |||
}; | |||
@@ -511,31 +551,15 @@ private: | |||
class iOSAudioIODeviceType : public AudioIODeviceType | |||
{ | |||
public: | |||
iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") | |||
{ | |||
} | |||
iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") {} | |||
void scanForDevices() {} | |||
StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray ("iOS Audio"); } | |||
int getDefaultDeviceIndex (bool /*forInput*/) const { return 0; } | |||
int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; } | |||
bool hasSeparateInputsAndOutputs() const { return false; } | |||
StringArray getDeviceNames (bool wantInputNames) const | |||
{ | |||
return StringArray ("iOS Audio"); | |||
} | |||
int getDefaultDeviceIndex (bool forInput) const | |||
{ | |||
return 0; | |||
} | |||
int getIndexOfDevice (AudioIODevice* device, bool asInput) const | |||
{ | |||
return device != nullptr ? 0 : -1; | |||
} | |||
bool hasSeparateInputsAndOutputs() const { return false; } | |||
AudioIODevice* createDevice (const String& outputDeviceName, | |||
const String& inputDeviceName) | |||
AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) | |||
{ | |||
if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) | |||
return new iOSAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName | |||
@@ -48,8 +48,8 @@ struct AudioTrackProducerClass : public ObjCClass <NSObject> | |||
struct AudioSourceHolder | |||
{ | |||
AudioSourceHolder (AudioSource* source_, int numFrames) | |||
: source (source_), readPosition (0), lengthInFrames (numFrames) | |||
AudioSourceHolder (AudioSource* s, int numFrames) | |||
: source (s), readPosition (0), lengthInFrames (numFrames) | |||
{ | |||
} | |||
@@ -125,15 +125,9 @@ private: | |||
source->source->getNextAudioBlock (info); | |||
typedef AudioData::Pointer <AudioData::Int16, | |||
AudioData::LittleEndian, | |||
AudioData::Interleaved, | |||
AudioData::NonConst> CDSampleFormat; | |||
typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> CDSampleFormat; | |||
typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat; | |||
typedef AudioData::Pointer <AudioData::Float32, | |||
AudioData::NativeEndian, | |||
AudioData::NonInterleaved, | |||
AudioData::Const> SourceSampleFormat; | |||
CDSampleFormat left (buffer, 2); | |||
left.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (0)), numSamples); | |||
CDSampleFormat right (buffer + 2, 2); | |||
@@ -166,8 +160,8 @@ private: | |||
struct OpenDiskDevice | |||
{ | |||
OpenDiskDevice (DRDevice* device_) | |||
: device (device_), | |||
OpenDiskDevice (DRDevice* d) | |||
: device (d), | |||
tracks ([[NSMutableArray alloc] init]), | |||
underrunProtection (true) | |||
{ | |||
@@ -254,9 +248,8 @@ struct OpenDiskDevice | |||
NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] | |||
objectForKey: DRErrorStatusErrorStringKey]; | |||
if ([err length] > 0) | |||
return CharPointer_UTF8 ([err UTF8String]); | |||
return nsStringToJuce (err); | |||
} | |||
[device releaseMediaReservation]; | |||
@@ -273,12 +266,9 @@ struct OpenDiskDevice | |||
class AudioCDBurner::Pimpl : public Timer | |||
{ | |||
public: | |||
Pimpl (AudioCDBurner& owner_, const int deviceIndex) | |||
: device (0), owner (owner_) | |||
Pimpl (AudioCDBurner& b, int deviceIndex) : owner (b) | |||
{ | |||
DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex]; | |||
if (dev != nil) | |||
if (DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex]) | |||
{ | |||
device = new OpenDiskDevice (dev); | |||
lastState = getDiskState(); | |||
@@ -307,7 +297,6 @@ public: | |||
if ([device->device isValid]) | |||
{ | |||
NSDictionary* status = [device->device status]; | |||
NSString* state = [status objectForKey: DRDeviceMediaStateKey]; | |||
if ([state isEqualTo: DRDeviceMediaStateNone]) | |||
@@ -322,8 +311,8 @@ public: | |||
{ | |||
if ([[[status objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue] > 0) | |||
return writableDiskPresent; | |||
else | |||
return readOnlyDiskPresent; | |||
return readOnlyDiskPresent; | |||
} | |||
} | |||
@@ -337,14 +326,8 @@ public: | |||
Array<int> results; | |||
if ([device->device isValid]) | |||
{ | |||
NSArray* speeds = [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey]; | |||
for (unsigned int i = 0; i < [speeds count]; ++i) | |||
{ | |||
const int kbPerSec = [[speeds objectAtIndex: i] intValue]; | |||
results.add (kbPerSec / kilobytesPerSecond1x); | |||
} | |||
} | |||
for (id kbPerSec in [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey]) | |||
results.add ([kbPerSec intValue] / kilobytesPerSecond1x); | |||
return results; | |||
} | |||
@@ -385,40 +368,21 @@ AudioCDBurner::~AudioCDBurner() | |||
AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) | |||
{ | |||
ScopedPointer <AudioCDBurner> b (new AudioCDBurner (deviceIndex)); | |||
ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex)); | |||
if (b->pimpl->device == nil) | |||
b = 0; | |||
b = nullptr; | |||
return b.release(); | |||
} | |||
namespace | |||
{ | |||
NSArray* findDiskBurnerDevices() | |||
{ | |||
NSMutableArray* results = [NSMutableArray array]; | |||
NSArray* devs = [DRDevice devices]; | |||
for (int i = 0; i < [devs count]; ++i) | |||
{ | |||
NSDictionary* dic = [[devs objectAtIndex: i] info]; | |||
NSString* name = [dic valueForKey: DRDeviceProductNameKey]; | |||
if (name != nil) | |||
[results addObject: name]; | |||
} | |||
return results; | |||
} | |||
} | |||
StringArray AudioCDBurner::findAvailableDevices() | |||
{ | |||
NSArray* names = findDiskBurnerDevices(); | |||
StringArray s; | |||
for (unsigned int i = 0; i < [names count]; ++i) | |||
s.add (CharPointer_UTF8 ([[names objectAtIndex: i] UTF8String])); | |||
for (NSDictionary* dic in [DRDevice devices]) | |||
if (NSString* name = [dic valueForKey: DRDeviceProductNameKey]) | |||
s.add (nsStringToJuce (name)); | |||
return s; | |||
} | |||
@@ -1362,7 +1362,39 @@ private: | |||
static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*) | |||
{ | |||
return ASIOAudioIODevice::asioMessagesCallback (selector, value, deviceIndex); | |||
switch (selector) | |||
{ | |||
case kAsioSelectorSupported: | |||
if (value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest | |||
|| value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor) | |||
return 1; | |||
break; | |||
case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); return sendResetRequest (deviceIndex); | |||
case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); return sendResetRequest (deviceIndex); | |||
case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); return sendResetRequest (deviceIndex); | |||
case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1; | |||
case kAsioEngineVersion: return 2; | |||
case kAsioSupportsTimeInfo: | |||
case kAsioSupportsTimeCode: | |||
return 0; | |||
} | |||
return 0; | |||
} | |||
static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate) | |||
{ | |||
sendResetRequest (deviceIndex); | |||
} | |||
static long sendResetRequest (int deviceIndex) | |||
{ | |||
if (currentASIODev[deviceIndex] != nullptr) | |||
currentASIODev[deviceIndex]->resetRequest(); | |||
return 1; | |||
} | |||
static void setCallbacks (ASIOCallbacks& callbacks) | |||
@@ -1370,56 +1402,18 @@ private: | |||
callbacks.bufferSwitch = &bufferSwitchCallback; | |||
callbacks.asioMessage = &asioMessagesCallback; | |||
callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; | |||
callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
} | |||
}; | |||
void setCallbackFunctions() | |||
{ | |||
callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
if (currentASIODev[0] == this) ASIOCallbackFunctions<0>::setCallbacks (callbacks); | |||
else if (currentASIODev[1] == this) ASIOCallbackFunctions<1>::setCallbacks (callbacks); | |||
else if (currentASIODev[2] == this) ASIOCallbackFunctions<2>::setCallbacks (callbacks); | |||
else jassertfalse; | |||
} | |||
//============================================================================== | |||
static long asioMessagesCallback (long selector, long value, const int deviceIndex) | |||
{ | |||
switch (selector) | |||
{ | |||
case kAsioSelectorSupported: | |||
if (value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest | |||
|| value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor) | |||
return 1; | |||
break; | |||
case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); return sendResetRequest (deviceIndex); | |||
case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); return sendResetRequest (deviceIndex); | |||
case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); return sendResetRequest (deviceIndex); | |||
case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1; | |||
case kAsioEngineVersion: return 2; | |||
case kAsioSupportsTimeInfo: | |||
case kAsioSupportsTimeCode: | |||
return 0; | |||
} | |||
return 0; | |||
} | |||
static long sendResetRequest (int deviceIndex) | |||
{ | |||
if (currentASIODev[deviceIndex] != nullptr) | |||
currentASIODev[deviceIndex]->resetRequest(); | |||
return 1; | |||
} | |||
static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate) | |||
{ | |||
} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) | |||
}; | |||
@@ -22,9 +22,7 @@ | |||
============================================================================== | |||
*/ | |||
//============================================================================== | |||
static const char* const aiffFormatName = "AIFF file"; | |||
static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 }; | |||
//============================================================================== | |||
const char* const AiffAudioFormat::appleOneShot = "apple one shot"; | |||
@@ -101,7 +99,7 @@ namespace AiffFileHelpers | |||
if (values.getAllKeys().contains ("MidiUnityNote", true)) | |||
{ | |||
block.setSize ((sizeof (InstChunk) + 3) & ~(size_t) 3, true); | |||
InstChunk& inst = *static_cast <InstChunk*> (block.getData()); | |||
InstChunk& inst = *static_cast<InstChunk*> (block.getData()); | |||
inst.baseNote = getValue8 (values, "MidiUnityNote", "60"); | |||
inst.detune = getValue8 (values, "Detune", "0"); | |||
@@ -220,7 +218,7 @@ namespace AiffFileHelpers | |||
StringArray tagsArray; | |||
int bytesLeft = (int) mb.getSize(); | |||
const char* data = static_cast <const char*> (mb.getData()); | |||
const char* data = static_cast<const char*> (mb.getData()); | |||
while (bytesLeft > 0) | |||
{ | |||
@@ -365,7 +363,7 @@ class AiffAudioFormatReader : public AudioFormatReader | |||
{ | |||
public: | |||
AiffAudioFormatReader (InputStream* in) | |||
: AudioFormatReader (in, TRANS (aiffFormatName)) | |||
: AudioFormatReader (in, aiffFormatName) | |||
{ | |||
using namespace AiffFileHelpers; | |||
@@ -611,7 +609,7 @@ public: | |||
AiffAudioFormatWriter (OutputStream* out, double rate, | |||
unsigned int numChans, unsigned int bits, | |||
const StringPairArray& metadataValues) | |||
: AudioFormatWriter (out, TRANS (aiffFormatName), rate, numChans, bits), | |||
: AudioFormatWriter (out, aiffFormatName, rate, numChans, bits), | |||
lengthInSamples (0), | |||
bytesWritten (0), | |||
writeFailed (false) | |||
@@ -879,8 +877,7 @@ private: | |||
}; | |||
//============================================================================== | |||
AiffAudioFormat::AiffAudioFormat() | |||
: AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions)) | |||
AiffAudioFormat::AiffAudioFormat() : AudioFormat (aiffFormatName, ".aiff .aif") | |||
{ | |||
} | |||
@@ -327,7 +327,7 @@ class CoreAudioReader : public AudioFormatReader | |||
{ | |||
public: | |||
CoreAudioReader (InputStream* const inp) | |||
: AudioFormatReader (inp, TRANS (coreAudioFormatName)), | |||
: AudioFormatReader (inp, coreAudioFormatName), | |||
ok (false), lastReadPosition (0) | |||
{ | |||
usesFloatingPointData = true; | |||
@@ -487,7 +487,7 @@ private: | |||
//============================================================================== | |||
CoreAudioFormat::CoreAudioFormat() | |||
: AudioFormat (TRANS (coreAudioFormatName), findFileExtensionsForCoreAudioCodecs()) | |||
: AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs()) | |||
{ | |||
} | |||
@@ -83,16 +83,14 @@ namespace FlacNamespace | |||
//============================================================================== | |||
static const char* const flacFormatName = "FLAC file"; | |||
static const char* const flacExtensions[] = { ".flac", 0 }; | |||
//============================================================================== | |||
class FlacReader : public AudioFormatReader | |||
{ | |||
public: | |||
//============================================================================== | |||
FlacReader (InputStream* const in) | |||
: AudioFormatReader (in, TRANS (flacFormatName)), | |||
: AudioFormatReader (in, flacFormatName), | |||
reservoir (2, 0), | |||
reservoirStart (0), | |||
samplesInReservoir (0), | |||
@@ -313,7 +311,7 @@ class FlacWriter : public AudioFormatWriter | |||
{ | |||
public: | |||
FlacWriter (OutputStream* const out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex) | |||
: AudioFormatWriter (out, TRANS (flacFormatName), rate, numChans, bits) | |||
: AudioFormatWriter (out, flacFormatName, rate, numChans, bits) | |||
{ | |||
using namespace FlacNamespace; | |||
encoder = FLAC__stream_encoder_new(); | |||
@@ -478,7 +476,7 @@ private: | |||
//============================================================================== | |||
FlacAudioFormat::FlacAudioFormat() | |||
: AudioFormat (TRANS (flacFormatName), StringArray (flacExtensions)) | |||
: AudioFormat (flacFormatName, ".flac") | |||
{ | |||
} | |||
@@ -538,7 +536,7 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out, | |||
StringArray FlacAudioFormat::getQualityOptions() | |||
{ | |||
const char* options[] = { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)", 0 }; | |||
static const char* options[] = { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)", 0 }; | |||
return StringArray (options); | |||
} | |||
@@ -147,12 +147,9 @@ private: | |||
}; | |||
//============================================================================== | |||
static const char* const lameFormatName = "MP3 file"; | |||
static const char* const lameExtensions[] = { ".mp3", nullptr }; | |||
LAMEEncoderAudioFormat::LAMEEncoderAudioFormat (const File& lameApplication) | |||
: AudioFormat (TRANS (lameFormatName), StringArray (lameExtensions)), | |||
lameApp (lameApplication) | |||
: AudioFormat ("MP3 file", ".mp3"), | |||
lameApp (lameApplication) | |||
{ | |||
} | |||
@@ -183,11 +180,9 @@ bool LAMEEncoderAudioFormat::isCompressed() { return true; } | |||
StringArray LAMEEncoderAudioFormat::getQualityOptions() | |||
{ | |||
const char* vbrOptions[] = { "VBR quality 0 (best)", "VBR quality 1", "VBR quality 2", "VBR quality 3", | |||
"VBR quality 4 (normal)", "VBR quality 5", "VBR quality 6", "VBR quality 7", "VBR quality 8", | |||
"VBR quality 9 (smallest)", | |||
nullptr }; | |||
static const char* vbrOptions[] = { "VBR quality 0 (best)", "VBR quality 1", "VBR quality 2", "VBR quality 3", | |||
"VBR quality 4 (normal)", "VBR quality 5", "VBR quality 6", "VBR quality 7", | |||
"VBR quality 8", "VBR quality 9 (smallest)", nullptr }; | |||
StringArray opts (vbrOptions); | |||
const int cbrRates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }; | |||
@@ -2929,14 +2929,13 @@ private: | |||
//============================================================================== | |||
static const char* const mp3FormatName = "MP3 file"; | |||
static const char* const mp3Extensions[] = { ".mp3", nullptr }; | |||
//============================================================================== | |||
class MP3Reader : public AudioFormatReader | |||
{ | |||
public: | |||
MP3Reader (InputStream* const in) | |||
: AudioFormatReader (in, TRANS (mp3FormatName)), | |||
: AudioFormatReader (in, mp3FormatName), | |||
stream (*in), currentPosition (0), | |||
decodedStart (0), decodedEnd (0) | |||
{ | |||
@@ -2953,7 +2952,6 @@ public: | |||
} | |||
} | |||
//============================================================================== | |||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
int64 startSampleInFile, int numSamples) override | |||
{ | |||
@@ -3109,10 +3107,7 @@ private: | |||
} | |||
//============================================================================== | |||
MP3AudioFormat::MP3AudioFormat() | |||
: AudioFormat (MP3Decoder::mp3FormatName, StringArray (MP3Decoder::mp3Extensions)) | |||
{} | |||
MP3AudioFormat::MP3AudioFormat() : AudioFormat (MP3Decoder::mp3FormatName, ".mp3") {} | |||
MP3AudioFormat::~MP3AudioFormat() {} | |||
Array<int> MP3AudioFormat::getPossibleSampleRates() { return Array<int>(); } | |||
@@ -89,7 +89,6 @@ namespace OggVorbisNamespace | |||
//============================================================================== | |||
static const char* const oggFormatName = "Ogg-Vorbis file"; | |||
static const char* const oggExtensions[] = { ".ogg", 0 }; | |||
const char* const OggVorbisAudioFormat::encoderName = "encoder"; | |||
const char* const OggVorbisAudioFormat::id3title = "id3title"; | |||
@@ -106,7 +105,7 @@ class OggReader : public AudioFormatReader | |||
{ | |||
public: | |||
OggReader (InputStream* const inp) | |||
: AudioFormatReader (inp, TRANS (oggFormatName)), | |||
: AudioFormatReader (inp, oggFormatName), | |||
reservoir (2, 4096), | |||
reservoirStart (0), | |||
samplesInReservoir (0) | |||
@@ -115,10 +114,10 @@ public: | |||
sampleRate = 0; | |||
usesFloatingPointData = true; | |||
callbacks.read_func = &oggReadCallback; | |||
callbacks.seek_func = &oggSeekCallback; | |||
callbacks.read_func = &oggReadCallback; | |||
callbacks.seek_func = &oggSeekCallback; | |||
callbacks.close_func = &oggCloseCallback; | |||
callbacks.tell_func = &oggTellCallback; | |||
callbacks.tell_func = &oggTellCallback; | |||
const int err = ov_open_callbacks (input, &ovFile, 0, 0, callbacks); | |||
@@ -222,12 +221,12 @@ public: | |||
//============================================================================== | |||
static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource) | |||
{ | |||
return (size_t) (static_cast <InputStream*> (datasource)->read (ptr, (int) (size * nmemb))) / size; | |||
return (size_t) (static_cast<InputStream*> (datasource)->read (ptr, (int) (size * nmemb))) / size; | |||
} | |||
static int oggSeekCallback (void* datasource, OggVorbisNamespace::ogg_int64_t offset, int whence) | |||
{ | |||
InputStream* const in = static_cast <InputStream*> (datasource); | |||
InputStream* const in = static_cast<InputStream*> (datasource); | |||
if (whence == SEEK_CUR) | |||
offset += in->getPosition(); | |||
@@ -245,7 +244,7 @@ public: | |||
static long oggTellCallback (void* datasource) | |||
{ | |||
return (long) static_cast <InputStream*> (datasource)->getPosition(); | |||
return (long) static_cast<InputStream*> (datasource)->getPosition(); | |||
} | |||
private: | |||
@@ -267,7 +266,7 @@ public: | |||
const unsigned int bitsPerSample_, | |||
const int qualityIndex, | |||
const StringPairArray& metadata) | |||
: AudioFormatWriter (out, TRANS (oggFormatName), sampleRate_, numChannels_, bitsPerSample_), | |||
: AudioFormatWriter (out, oggFormatName, sampleRate_, numChannels_, bitsPerSample_), | |||
ok (false) | |||
{ | |||
using namespace OggVorbisNamespace; | |||
@@ -279,13 +278,13 @@ public: | |||
{ | |||
vorbis_comment_init (&vc); | |||
addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE"); | |||
addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE"); | |||
addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber, "TRACKNUMBER"); | |||
vorbis_analysis_init (&vd, &vi); | |||
@@ -425,8 +424,7 @@ private: | |||
//============================================================================== | |||
OggVorbisAudioFormat::OggVorbisAudioFormat() | |||
: AudioFormat (TRANS (oggFormatName), StringArray (oggExtensions)) | |||
OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg") | |||
{ | |||
} | |||
@@ -481,8 +479,8 @@ AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out, | |||
StringArray OggVorbisAudioFormat::getQualityOptions() | |||
{ | |||
const char* options[] = { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps", | |||
"192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps", 0 }; | |||
static const char* options[] = { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps", | |||
"192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps", 0 }; | |||
return StringArray (options); | |||
} | |||
@@ -66,14 +66,13 @@ namespace juce | |||
bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); | |||
static const char* const quickTimeFormatName = "QuickTime file"; | |||
static const char* const quickTimeExtensions[] = { ".mov", ".mp3", ".mp4", ".m4a", 0 }; | |||
//============================================================================== | |||
class QTAudioReader : public AudioFormatReader | |||
{ | |||
public: | |||
QTAudioReader (InputStream* const input_, const int trackNum_) | |||
: AudioFormatReader (input_, TRANS (quickTimeFormatName)), | |||
: AudioFormatReader (input_, quickTimeFormatName), | |||
ok (false), | |||
movie (0), | |||
trackNum (trackNum_), | |||
@@ -133,31 +132,31 @@ public: | |||
trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame | |||
/ GetMediaTimeScale (media); | |||
OSStatus err = MovieAudioExtractionBegin (movie, 0, &extractor); | |||
MovieAudioExtractionBegin (movie, 0, &extractor); | |||
unsigned long output_layout_size; | |||
err = MovieAudioExtractionGetPropertyInfo (extractor, | |||
kQTPropertyClass_MovieAudioExtraction_Audio, | |||
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, | |||
0, &output_layout_size, 0); | |||
OSStatus err = MovieAudioExtractionGetPropertyInfo (extractor, | |||
kQTPropertyClass_MovieAudioExtraction_Audio, | |||
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, | |||
0, &output_layout_size, 0); | |||
if (err != noErr) | |||
return; | |||
HeapBlock <AudioChannelLayout> qt_audio_channel_layout; | |||
qt_audio_channel_layout.calloc (output_layout_size, 1); | |||
err = MovieAudioExtractionGetProperty (extractor, | |||
kQTPropertyClass_MovieAudioExtraction_Audio, | |||
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, | |||
output_layout_size, qt_audio_channel_layout, 0); | |||
MovieAudioExtractionGetProperty (extractor, | |||
kQTPropertyClass_MovieAudioExtraction_Audio, | |||
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, | |||
output_layout_size, qt_audio_channel_layout, 0); | |||
qt_audio_channel_layout[0].mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; | |||
err = MovieAudioExtractionSetProperty (extractor, | |||
kQTPropertyClass_MovieAudioExtraction_Audio, | |||
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, | |||
output_layout_size, | |||
qt_audio_channel_layout); | |||
MovieAudioExtractionSetProperty (extractor, | |||
kQTPropertyClass_MovieAudioExtraction_Audio, | |||
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, | |||
output_layout_size, | |||
qt_audio_channel_layout); | |||
err = MovieAudioExtractionGetProperty (extractor, | |||
kQTPropertyClass_MovieAudioExtraction_Audio, | |||
@@ -349,8 +348,7 @@ private: | |||
//============================================================================== | |||
QuickTimeAudioFormat::QuickTimeAudioFormat() | |||
: AudioFormat (TRANS (quickTimeFormatName), StringArray (quickTimeExtensions)) | |||
QuickTimeAudioFormat::QuickTimeAudioFormat() : AudioFormat (quickTimeFormatName, ".mov .mp3 .mp4 .m4a") | |||
{ | |||
} | |||
@@ -368,7 +366,7 @@ bool QuickTimeAudioFormat::canDoMono() { return true; } | |||
AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream, | |||
const bool deleteStreamIfOpeningFails) | |||
{ | |||
ScopedPointer <QTAudioReader> r (new QTAudioReader (sourceStream, 0)); | |||
ScopedPointer<QTAudioReader> r (new QTAudioReader (sourceStream, 0)); | |||
if (r->ok) | |||
return r.release(); | |||
@@ -23,7 +23,6 @@ | |||
*/ | |||
static const char* const wavFormatName = "WAV file"; | |||
static const char* const wavExtensions[] = { ".wav", ".bwf", 0 }; | |||
//============================================================================== | |||
const char* const WavAudioFormat::bwavDescription = "bwav description"; | |||
@@ -225,7 +224,7 @@ namespace WavFileHelpers | |||
{ | |||
data.setSize (roundUpSize (sizeof (SMPLChunk) + (size_t) (numLoops - 1) * sizeof (SampleLoop)), true); | |||
SMPLChunk* const s = static_cast <SMPLChunk*> (data.getData()); | |||
SMPLChunk* const s = static_cast<SMPLChunk*> (data.getData()); | |||
s->manufacturer = getValue (values, "Manufacturer", "0"); | |||
s->product = getValue (values, "Product", "0"); | |||
@@ -293,7 +292,7 @@ namespace WavFileHelpers | |||
if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true)) | |||
{ | |||
data.setSize (8, true); | |||
InstChunk* const inst = static_cast <InstChunk*> (data.getData()); | |||
InstChunk* const inst = static_cast<InstChunk*> (data.getData()); | |||
inst->baseNote = getValue (values, "MidiUnityNote", "60"); | |||
inst->detune = getValue (values, "Detune", "0"); | |||
@@ -356,7 +355,7 @@ namespace WavFileHelpers | |||
{ | |||
data.setSize (roundUpSize (sizeof (CueChunk) + (size_t) (numCues - 1) * sizeof (Cue)), true); | |||
CueChunk* const c = static_cast <CueChunk*> (data.getData()); | |||
CueChunk* const c = static_cast<CueChunk*> (data.getData()); | |||
c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues); | |||
@@ -605,7 +604,7 @@ class WavAudioFormatReader : public AudioFormatReader | |||
{ | |||
public: | |||
WavAudioFormatReader (InputStream* const in) | |||
: AudioFormatReader (in, TRANS (wavFormatName)), | |||
: AudioFormatReader (in, wavFormatName), | |||
bwavChunkStart (0), | |||
bwavSize (0), | |||
dataLength (0), | |||
@@ -900,7 +899,7 @@ public: | |||
WavAudioFormatWriter (OutputStream* const out, const double rate, | |||
const unsigned int numChans, const unsigned int bits, | |||
const StringPairArray& metadataValues) | |||
: AudioFormatWriter (out, TRANS (wavFormatName), rate, numChans, bits), | |||
: AudioFormatWriter (out, wavFormatName, rate, numChans, bits), | |||
lengthInSamples (0), | |||
bytesWritten (0), | |||
writeFailed (false) | |||
@@ -1197,14 +1196,8 @@ private: | |||
}; | |||
//============================================================================== | |||
WavAudioFormat::WavAudioFormat() | |||
: AudioFormat (TRANS (wavFormatName), StringArray (wavExtensions)) | |||
{ | |||
} | |||
WavAudioFormat::~WavAudioFormat() | |||
{ | |||
} | |||
WavAudioFormat::WavAudioFormat() : AudioFormat (wavFormatName, ".wav .bwf") {} | |||
WavAudioFormat::~WavAudioFormat() {} | |||
Array<int> WavAudioFormat::getPossibleSampleRates() | |||
{ | |||
@@ -22,9 +22,13 @@ | |||
============================================================================== | |||
*/ | |||
AudioFormat::AudioFormat (const String& name, const StringArray& extensions) | |||
: formatName (name), | |||
fileExtensions (extensions) | |||
AudioFormat::AudioFormat (String name, StringArray extensions) | |||
: formatName (name), fileExtensions (extensions) | |||
{ | |||
} | |||
AudioFormat::AudioFormat (StringRef name, StringRef extensions) | |||
: formatName (name.text), fileExtensions (StringArray::fromTokens (extensions, false)) | |||
{ | |||
} | |||
@@ -32,7 +36,6 @@ AudioFormat::~AudioFormat() | |||
{ | |||
} | |||
//============================================================================== | |||
bool AudioFormat::canHandleFile (const File& f) | |||
{ | |||
for (int i = 0; i < fileExtensions.size(); ++i) | |||
@@ -42,7 +42,6 @@ public: | |||
//============================================================================== | |||
/** Returns the name of this format. | |||
e.g. "WAV file" or "AIFF file" | |||
*/ | |||
const String& getFormatName() const; | |||
@@ -157,11 +156,17 @@ protected: | |||
/** Creates an AudioFormat object. | |||
@param formatName this sets the value that will be returned by getFormatName() | |||
@param fileExtensions a zero-terminated list of file extensions - this is what will | |||
be returned by getFileExtension() | |||
@param fileExtensions an array of file extensions - these will be returned by getFileExtensions() | |||
*/ | |||
AudioFormat (String formatName, StringArray fileExtensions); | |||
/** Creates an AudioFormat object. | |||
@param formatName this sets the value that will be returned by getFormatName() | |||
@param fileExtensions a whitespace-separated list of file extensions - these will | |||
be returned by getFileExtensions() | |||
*/ | |||
AudioFormat (const String& formatName, | |||
const StringArray& fileExtensions); | |||
AudioFormat (StringRef formatName, StringRef fileExtensions); | |||
private: | |||
//============================================================================== | |||
@@ -127,7 +127,7 @@ bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSample | |||
return true; | |||
} | |||
bool AudioFormatWriter::writeFromFloatArrays (const float** channels, int numSourceChannels, int numSamples) | |||
bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples) | |||
{ | |||
if (numSamples <= 0) | |||
return true; | |||
@@ -170,7 +170,7 @@ bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& sou | |||
jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0); | |||
if (startSample == 0) | |||
return writeFromFloatArrays ((const float**) source.getArrayOfChannels(), numSourceChannels, numSamples); | |||
return writeFromFloatArrays (source.getArrayOfChannels(), numSourceChannels, numSamples); | |||
const float* chans [256]; | |||
jassert ((int) numChannels < numElementsInArray (chans)); | |||
@@ -184,12 +184,11 @@ bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& sou | |||
} | |||
//============================================================================== | |||
class AudioFormatWriter::ThreadedWriter::Buffer : public AbstractFifo, | |||
private TimeSliceClient | |||
class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient | |||
{ | |||
public: | |||
Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples) | |||
: AbstractFifo (numSamples), | |||
: fifo (numSamples), | |||
buffer (channels, numSamples), | |||
timeSliceThread (tst), | |||
writer (w), | |||
@@ -209,7 +208,7 @@ public: | |||
{} | |||
} | |||
bool write (const float** data, int numSamples) | |||
bool write (const float* const* data, int numSamples) | |||
{ | |||
if (numSamples <= 0 || ! isRunning) | |||
return true; | |||
@@ -217,7 +216,7 @@ public: | |||
jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this! | |||
int start1, size1, start2, size2; | |||
prepareToWrite (numSamples, start1, size1, start2, size2); | |||
fifo.prepareToWrite (numSamples, start1, size1, start2, size2); | |||
if (size1 + size2 < numSamples) | |||
return false; | |||
@@ -228,7 +227,7 @@ public: | |||
buffer.copyFrom (i, start2, data[i] + size1, size2); | |||
} | |||
finishedWrite (size1 + size2); | |||
fifo.finishedWrite (size1 + size2); | |||
timeSliceThread.notify(); | |||
return true; | |||
} | |||
@@ -240,10 +239,10 @@ public: | |||
int writePendingData() | |||
{ | |||
const int numToDo = getTotalSize() / 4; | |||
const int numToDo = fifo.getTotalSize() / 4; | |||
int start1, size1, start2, size2; | |||
prepareToRead (numToDo, start1, size1, start2, size2); | |||
fifo.prepareToRead (numToDo, start1, size1, start2, size2); | |||
if (size1 <= 0) | |||
return 10; | |||
@@ -266,7 +265,7 @@ public: | |||
samplesWritten += size2; | |||
} | |||
finishedRead (size1 + size2); | |||
fifo.finishedRead (size1 + size2); | |||
return 0; | |||
} | |||
@@ -281,6 +280,7 @@ public: | |||
} | |||
private: | |||
AbstractFifo fifo; | |||
AudioSampleBuffer buffer; | |||
TimeSliceThread& timeSliceThread; | |||
ScopedPointer<AudioFormatWriter> writer; | |||
@@ -301,7 +301,7 @@ AudioFormatWriter::ThreadedWriter::~ThreadedWriter() | |||
{ | |||
} | |||
bool AudioFormatWriter::ThreadedWriter::write (const float** data, int numSamples) | |||
bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples) | |||
{ | |||
return buffer->write (data, numSamples); | |||
} | |||
@@ -128,7 +128,7 @@ public: | |||
int startSample, int numSamples); | |||
/** Writes some samples from a set of float data channels. */ | |||
bool writeFromFloatArrays (const float** channels, int numChannels, int numSamples); | |||
bool writeFromFloatArrays (const float* const* channels, int numChannels, int numSamples); | |||
//============================================================================== | |||
/** Returns the sample rate being used. */ | |||
@@ -177,7 +177,7 @@ public: | |||
The data must be an array containing the same number of channels as the | |||
AudioFormatWriter object is using. None of these channels can be null. | |||
*/ | |||
bool write (const float** data, int numSamples); | |||
bool write (const float* const* data, int numSamples); | |||
class JUCE_API IncomingDataReceiver | |||
{ | |||
@@ -229,7 +229,7 @@ protected: | |||
typedef AudioData::Pointer <DestSampleType, DestEndianness, AudioData::Interleaved, AudioData::NonConst> DestType; | |||
typedef AudioData::Pointer <SourceSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SourceType; | |||
static void write (void* destData, int numDestChannels, const int** source, | |||
static void write (void* destData, int numDestChannels, const int* const* source, | |||
int numSamples, const int sourceOffset = 0) noexcept | |||
{ | |||
for (int i = 0; i < numDestChannels; ++i) | |||
@@ -1,7 +1,7 @@ | |||
{ | |||
"id": "juce_audio_formats", | |||
"name": "JUCE audio file format codecs", | |||
"version": "2.1.3", | |||
"version": "2.1.6", | |||
"description": "Classes for reading and writing various audio file formats.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
@@ -60,7 +60,9 @@ public: | |||
/** Tries to recreate a type from a previously generated PluginDescription. | |||
@see PluginDescription::createInstance | |||
*/ | |||
virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc) = 0; | |||
virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, | |||
double initialSampleRate, | |||
int initialBufferSize) = 0; | |||
/** Should do a quick check to see if this file or directory might be a plugin of | |||
this format. | |||
@@ -74,28 +74,16 @@ void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format) | |||
formats.add (format); | |||
} | |||
AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, | |||
String& errorMessage) const | |||
AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, double rate, | |||
int blockSize, String& errorMessage) const | |||
{ | |||
AudioPluginInstance* result = nullptr; | |||
for (int i = 0; i < formats.size(); ++i) | |||
{ | |||
result = formats.getUnchecked(i)->createInstanceFromDescription (description); | |||
if (result != nullptr) | |||
break; | |||
} | |||
if (result == nullptr) | |||
{ | |||
if (! doesPluginStillExist (description)) | |||
errorMessage = TRANS ("This plug-in file no longer exists"); | |||
else | |||
errorMessage = TRANS ("This plug-in failed to load correctly"); | |||
} | |||
if (AudioPluginInstance* result = formats.getUnchecked(i)->createInstanceFromDescription (description, rate, blockSize)) | |||
return result; | |||
return result; | |||
errorMessage = doesPluginStillExist (description) ? TRANS ("This plug-in failed to load correctly") | |||
: TRANS ("This plug-in file no longer exists"); | |||
return nullptr; | |||
} | |||
bool AudioPluginFormatManager::doesPluginStillExist (const PluginDescription& description) const | |||
@@ -77,6 +77,8 @@ public: | |||
errorMessage string. | |||
*/ | |||
AudioPluginInstance* createPluginInstance (const PluginDescription& description, | |||
double initialSampleRate, | |||
int initialBufferSize, | |||
String& errorMessage) const; | |||
/** Checks that the file or component for this plugin actually still exists. | |||
@@ -38,7 +38,7 @@ public: | |||
//============================================================================== | |||
String getName() const override { return "AudioUnit"; } | |||
void findAllTypesForFile (OwnedArray <PluginDescription>&, const String& fileOrIdentifier) override; | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc) override; | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override; | |||
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; | |||
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; | |||
bool pluginNeedsRescanning (const PluginDescription&) override; | |||
@@ -331,14 +331,15 @@ public: | |||
} | |||
} | |||
void initialise() | |||
void initialise (double rate, int blockSize) | |||
{ | |||
refreshParameterList(); | |||
updateNumChannels(); | |||
producesMidiMessages = canProduceMidiOutput(); | |||
setPluginCallbacks(); | |||
setPlayConfigDetails (numInputBusChannels * numInputBusses, | |||
numOutputBusChannels * numOutputBusses, 0, 0); | |||
numOutputBusChannels * numOutputBusses, | |||
rate, blockSize); | |||
setLatencySamples (0); | |||
} | |||
@@ -777,26 +778,18 @@ public: | |||
void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override | |||
{ | |||
CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, | |||
(const UInt8*) data, | |||
sizeInBytes, | |||
kCFAllocatorNull); | |||
CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) data, | |||
sizeInBytes, kCFAllocatorNull); | |||
CFReadStreamOpen (stream); | |||
CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; | |||
CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, | |||
stream, | |||
0, | |||
kCFPropertyListImmutable, | |||
&format, | |||
0); | |||
CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, stream, 0, | |||
kCFPropertyListImmutable, &format, 0); | |||
CFRelease (stream); | |||
if (propertyList != 0) | |||
{ | |||
AudioUnitSetProperty (audioUnit, | |||
kAudioUnitProperty_ClassInfo, | |||
kAudioUnitScope_Global, | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, | |||
0, &propertyList, sizeof (propertyList)); | |||
sendAllParametersChangedEvents(); | |||
@@ -1004,11 +997,11 @@ private: | |||
} | |||
} | |||
static void eventListenerCallback (void* userData, void*, const AudioUnitEvent* event, | |||
static void eventListenerCallback (void* userRef, void*, const AudioUnitEvent* event, | |||
UInt64, AudioUnitParameterValue value) | |||
{ | |||
jassert (event != nullptr); | |||
static_cast <AudioUnitPluginInstance*> (userData)->eventCallback (*event, value); | |||
static_cast<AudioUnitPluginInstance*> (userRef)->eventCallback (*event, value); | |||
} | |||
//============================================================================== | |||
@@ -1053,9 +1046,7 @@ private: | |||
for (UInt32 i = 0; i < pktlist->numPackets; ++i) | |||
{ | |||
midiConcatenator.pushMidiData (packet->data, (int) packet->length, | |||
time, (void*) nullptr, *this); | |||
midiConcatenator.pushMidiData (packet->data, (int) packet->length, time, (void*) nullptr, *this); | |||
packet = MIDIPacketNext (packet); | |||
} | |||
} | |||
@@ -1148,40 +1139,39 @@ private: | |||
} | |||
//============================================================================== | |||
static OSStatus renderGetInputCallback (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, | |||
static OSStatus renderGetInputCallback (void* hostRef, AudioUnitRenderActionFlags* ioActionFlags, | |||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, | |||
UInt32 inNumberFrames, AudioBufferList* ioData) | |||
{ | |||
return static_cast <AudioUnitPluginInstance*> (inRefCon) | |||
return static_cast<AudioUnitPluginInstance*> (hostRef) | |||
->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); | |||
} | |||
static OSStatus renderMidiOutputCallback (void* userData, const AudioTimeStamp* timeStamp, UInt32 midiOutNum, | |||
static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp* timeStamp, UInt32 midiOutNum, | |||
const struct MIDIPacketList* pktlist) | |||
{ | |||
return static_cast <AudioUnitPluginInstance*> (userData)->renderMidiOutput (pktlist); | |||
return static_cast<AudioUnitPluginInstance*> (hostRef)->renderMidiOutput (pktlist); | |||
} | |||
static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) | |||
static OSStatus getBeatAndTempoCallback (void* hostRef, Float64* outCurrentBeat, Float64* outCurrentTempo) | |||
{ | |||
return static_cast <AudioUnitPluginInstance*> (inHostUserData) | |||
->getBeatAndTempo (outCurrentBeat, outCurrentTempo); | |||
return static_cast<AudioUnitPluginInstance*> (hostRef)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); | |||
} | |||
static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat, | |||
static OSStatus getMusicalTimeLocationCallback (void* hostRef, UInt32* outDeltaSampleOffsetToNextBeat, | |||
Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, | |||
Float64* outCurrentMeasureDownBeat) | |||
{ | |||
return static_cast <AudioUnitPluginInstance*> (inHostUserData) | |||
return static_cast<AudioUnitPluginInstance*> (hostRef) | |||
->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, | |||
outTimeSig_Denominator, outCurrentMeasureDownBeat); | |||
} | |||
static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged, | |||
static OSStatus getTransportStateCallback (void* hostRef, Boolean* outIsPlaying, Boolean* outTransportStateChanged, | |||
Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, | |||
Float64* outCycleStartBeat, Float64* outCycleEndBeat) | |||
{ | |||
return static_cast <AudioUnitPluginInstance*> (inHostUserData) | |||
return static_cast<AudioUnitPluginInstance*> (hostRef) | |||
->getTransportState (outIsPlaying, outTransportStateChanged, outCurrentSampleInTimeLine, | |||
outIsCycling, outCycleStartBeat, outCycleEndBeat); | |||
} | |||
@@ -1211,7 +1201,7 @@ private: | |||
void updateNumChannels() | |||
{ | |||
numInputBusses = getElementCount (kAudioUnitScope_Input); | |||
numInputBusses = getElementCount (kAudioUnitScope_Input); | |||
numOutputBusses = getElementCount (kAudioUnitScope_Output); | |||
AUChannelInfo supportedChannels [128]; | |||
@@ -1299,7 +1289,7 @@ class AudioUnitPluginWindowCocoa : public AudioProcessorEditor, | |||
public Timer | |||
{ | |||
public: | |||
AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& p, const bool createGenericViewIfNeeded) | |||
AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& p, bool createGenericViewIfNeeded) | |||
: AudioProcessorEditor (&p), | |||
plugin (p) | |||
{ | |||
@@ -1510,17 +1500,14 @@ private: | |||
class InnerWrapperComponent : public CarbonViewWrapperComponent | |||
{ | |||
public: | |||
InnerWrapperComponent (AudioUnitPluginWindowCarbon& owner_) | |||
: owner (owner_) | |||
{ | |||
} | |||
InnerWrapperComponent (AudioUnitPluginWindowCarbon& w) : owner (w) {} | |||
~InnerWrapperComponent() | |||
{ | |||
deleteWindow(); | |||
} | |||
HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) | |||
HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) override | |||
{ | |||
JUCE_AU_LOG ("Opening AU GUI: " + owner.plugin.getName()); | |||
@@ -1545,7 +1532,7 @@ private: | |||
return pluginView; | |||
} | |||
void removeView (HIViewRef) | |||
void removeView (HIViewRef) override | |||
{ | |||
owner.closeViewComponent(); | |||
} | |||
@@ -1569,7 +1556,7 @@ AudioProcessorEditor* AudioUnitPluginInstance::createEditor() | |||
{ | |||
ScopedPointer<AudioProcessorEditor> w (new AudioUnitPluginWindowCocoa (*this, false)); | |||
if (! static_cast <AudioUnitPluginWindowCocoa*> (w.get())->isValid()) | |||
if (! static_cast<AudioUnitPluginWindowCocoa*> (w.get())->isValid()) | |||
w = nullptr; | |||
#if JUCE_SUPPORT_CARBON | |||
@@ -1577,7 +1564,7 @@ AudioProcessorEditor* AudioUnitPluginInstance::createEditor() | |||
{ | |||
w = new AudioUnitPluginWindowCarbon (*this); | |||
if (! static_cast <AudioUnitPluginWindowCarbon*> (w.get())->isValid()) | |||
if (! static_cast<AudioUnitPluginWindowCarbon*> (w.get())->isValid()) | |||
w = nullptr; | |||
} | |||
#endif | |||
@@ -1611,9 +1598,9 @@ void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& | |||
try | |||
{ | |||
ScopedPointer <AudioPluginInstance> createdInstance (createInstanceFromDescription (desc)); | |||
ScopedPointer<AudioPluginInstance> createdInstance (createInstanceFromDescription (desc, 44100.0, 512)); | |||
if (AudioUnitPluginInstance* const auInstance = dynamic_cast <AudioUnitPluginInstance*> (createdInstance.get())) | |||
if (AudioUnitPluginInstance* auInstance = dynamic_cast<AudioUnitPluginInstance*> (createdInstance.get())) | |||
results.add (new PluginDescription (auInstance->getPluginDescription())); | |||
} | |||
catch (...) | |||
@@ -1622,15 +1609,15 @@ void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& | |||
} | |||
} | |||
AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc) | |||
AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc, double rate, int blockSize) | |||
{ | |||
if (fileMightContainThisPluginType (desc.fileOrIdentifier)) | |||
{ | |||
ScopedPointer <AudioUnitPluginInstance> result (new AudioUnitPluginInstance (desc.fileOrIdentifier)); | |||
ScopedPointer<AudioUnitPluginInstance> result (new AudioUnitPluginInstance (desc.fileOrIdentifier)); | |||
if (result->audioUnit != nullptr) | |||
{ | |||
result->initialise(); | |||
result->initialise (rate, blockSize); | |||
return result.release(); | |||
} | |||
} | |||
@@ -1638,8 +1625,7 @@ AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const | |||
return nullptr; | |||
} | |||
StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/, | |||
const bool /*recursive*/) | |||
StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath&, bool /*recursive*/) | |||
{ | |||
StringArray result; | |||
AudioComponent comp = nullptr; | |||
@@ -1662,9 +1648,7 @@ StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& | |||
|| desc.componentType == kAudioUnitType_Generator | |||
|| desc.componentType == kAudioUnitType_Panner) | |||
{ | |||
const String s (AudioUnitFormatHelpers::createPluginIdentifier (desc)); | |||
DBG (s); | |||
result.add (s); | |||
result.add (AudioUnitFormatHelpers::createPluginIdentifier (desc)); | |||
} | |||
} | |||
@@ -167,8 +167,10 @@ public: | |||
handle = nullptr; | |||
} | |||
void initialise() | |||
void initialise (double initialSampleRate, int initialBlockSize) | |||
{ | |||
setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize); | |||
if (initialised || plugin == nullptr || handle == nullptr) | |||
return; | |||
@@ -199,9 +201,7 @@ public: | |||
for (int i = 0; i < parameters.size(); ++i) | |||
plugin->connect_port (handle, parameters[i], &(parameterValues[i].scaled)); | |||
setPlayConfigDetails (inputs.size(), outputs.size(), | |||
getSampleRate() > 0 ? getSampleRate() : 44100.0f, | |||
getBlockSize() > 0 ? getBlockSize() : 512); | |||
setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize); | |||
setCurrentProgram (0); | |||
setLatencySamples (0); | |||
@@ -257,12 +257,9 @@ public: | |||
//============================================================================== | |||
void prepareToPlay (double newSampleRate, int samplesPerBlockExpected) | |||
{ | |||
setPlayConfigDetails (inputs.size(), outputs.size(), | |||
newSampleRate, samplesPerBlockExpected); | |||
setLatencySamples (0); | |||
initialise(); | |||
initialise (newSampleRate, samplesPerBlockExpected); | |||
if (initialised) | |||
{ | |||
@@ -584,12 +581,12 @@ void LADSPAPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& re | |||
desc.fileOrIdentifier = fileOrIdentifier; | |||
desc.uid = 0; | |||
ScopedPointer<LADSPAPluginInstance> instance (dynamic_cast <LADSPAPluginInstance*> (createInstanceFromDescription (desc))); | |||
ScopedPointer<LADSPAPluginInstance> instance (dynamic_cast<LADSPAPluginInstance*> (createInstanceFromDescription (desc, 44100.0, 512))); | |||
if (instance == nullptr || ! instance->isValid()) | |||
return; | |||
instance->initialise(); | |||
instance->initialise (44100.0, 512); | |||
instance->fillInPluginDescription (desc); | |||
@@ -613,7 +610,8 @@ void LADSPAPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& re | |||
} | |||
} | |||
AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const PluginDescription& desc) | |||
AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const PluginDescription& desc, | |||
double sampleRate, int blockSize) | |||
{ | |||
ScopedPointer<LADSPAPluginInstance> result; | |||
@@ -633,7 +631,7 @@ AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const Pl | |||
result = new LADSPAPluginInstance (module); | |||
if (result->plugin != nullptr && result->isValid()) | |||
result->initialise(); | |||
result->initialise (sampleRate, blockSize); | |||
else | |||
result = nullptr; | |||
} | |||
@@ -36,16 +36,16 @@ public: | |||
~LADSPAPluginFormat(); | |||
//============================================================================== | |||
String getName() const { return "LADSPA"; } | |||
void findAllTypesForFile (OwnedArray <PluginDescription>&, const String& fileOrIdentifier); | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); | |||
bool fileMightContainThisPluginType (const String& fileOrIdentifier); | |||
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier); | |||
bool pluginNeedsRescanning (const PluginDescription&); | |||
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive); | |||
bool doesPluginStillExist (const PluginDescription&); | |||
FileSearchPath getDefaultLocationsToSearch(); | |||
bool canScanForPlugins() const { return true; } | |||
String getName() const override { return "LADSPA"; } | |||
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override; | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override; | |||
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; | |||
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; | |||
bool pluginNeedsRescanning (const PluginDescription&) override; | |||
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override; | |||
bool doesPluginStillExist (const PluginDescription&) override; | |||
FileSearchPath getDefaultLocationsToSearch() override; | |||
bool canScanForPlugins() const override { return true; } | |||
private: | |||
void recursiveFileSearch (StringArray&, const File&, bool recursive); | |||
@@ -32,7 +32,7 @@ | |||
#if JUCE_MAC | |||
static bool makeFSRefFromPath (FSRef* destFSRef, const String& path) | |||
{ | |||
return FSPathMakeRef (reinterpret_cast <const UInt8*> (path.toRawUTF8()), destFSRef, 0) == noErr; | |||
return FSPathMakeRef (reinterpret_cast<const UInt8*> (path.toRawUTF8()), destFSRef, 0) == noErr; | |||
} | |||
#endif | |||
@@ -378,7 +378,7 @@ public: | |||
JUCE_VST_LOG ("Attempting to load VST: " + file.getFullPathName()); | |||
ScopedPointer <ModuleHandle> m (new ModuleHandle (file)); | |||
ScopedPointer<ModuleHandle> m (new ModuleHandle (file)); | |||
if (! m->open()) | |||
m = nullptr; | |||
@@ -389,10 +389,8 @@ public: | |||
} | |||
//============================================================================== | |||
ModuleHandle (const File& file_) | |||
: file (file_), | |||
moduleMain (nullptr), | |||
customMain (nullptr) | |||
ModuleHandle (const File& f) | |||
: file (f), moduleMain (nullptr), customMain (nullptr) | |||
#if JUCE_MAC | |||
#if JUCE_PPC | |||
, fragId (0) | |||
@@ -403,10 +401,10 @@ public: | |||
getActiveModules().add (this); | |||
#if JUCE_WINDOWS || JUCE_LINUX | |||
fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName(); | |||
fullParentDirectoryPathName = f.getParentDirectory().getFullPathName(); | |||
#elif JUCE_MAC | |||
FSRef ref; | |||
makeFSRefFromPath (&ref, file_.getParentDirectory().getFullPathName()); | |||
makeFSRefFromPath (&ref, f.getParentDirectory().getFullPathName()); | |||
FSGetCatalogInfo (&ref, kFSCatInfoNone, 0, 0, &parentDirFSSpec, 0); | |||
#endif | |||
} | |||
@@ -828,7 +826,7 @@ public: | |||
desc.isInstrument = (effect != nullptr && (effect->flags & effFlagsIsSynth) != 0); | |||
} | |||
void initialise() | |||
void initialise (double initialSampleRate, int initialBlockSize) | |||
{ | |||
if (initialised || effect == nullptr) | |||
return; | |||
@@ -844,6 +842,9 @@ public: | |||
JUCE_VST_LOG ("Initialising VST: " + module->pluginName); | |||
initialised = true; | |||
setPlayConfigDetails (effect->numInputs, effect->numOutputs, | |||
initialSampleRate, initialBlockSize); | |||
dispatch (effIdentify, 0, 0, 0, 0); | |||
if (getSampleRate() > 0) | |||
@@ -922,7 +923,7 @@ public: | |||
vstHostTime.samplePos = 0; | |||
vstHostTime.flags = kVstNanosValid | kVstAutomationWriting | kVstAutomationReading; | |||
initialise(); | |||
initialise (rate, samplesPerBlockExpected); | |||
if (initialised) | |||
{ | |||
@@ -1185,7 +1186,9 @@ public: | |||
} | |||
//============================================================================== | |||
int getNumPrograms() override { return effect != nullptr ? effect->numPrograms : 0; } | |||
int getNumPrograms() override { return effect != nullptr ? jmax (0, effect->numPrograms) : 0; } | |||
// NB: some plugs return negative numbers from this function. | |||
int getCurrentProgram() override { return (int) dispatch (effGetProgram, 0, 0, 0, 0); } | |||
void setCurrentProgram (int newIndex) override | |||
@@ -1196,18 +1199,17 @@ public: | |||
const String getProgramName (int index) override | |||
{ | |||
if (index == getCurrentProgram()) | |||
return getCurrentProgramName(); | |||
if (effect != nullptr) | |||
if (index >= 0) | |||
{ | |||
char nm [256] = { 0 }; | |||
if (index == getCurrentProgram()) | |||
return getCurrentProgramName(); | |||
if (dispatch (effGetProgramNameIndexed, | |||
jlimit (0, getNumPrograms(), index), | |||
-1, nm, 0) != 0) | |||
if (effect != nullptr) | |||
{ | |||
return String (CharPointer_UTF8 (nm)).trim(); | |||
char nm[264] = { 0 }; | |||
if (dispatch (effGetProgramNameIndexed, jlimit (0, getNumPrograms(), index), -1, nm, 0) != 0) | |||
return String (CharPointer_UTF8 (nm)).trim(); | |||
} | |||
} | |||
@@ -1216,7 +1218,7 @@ public: | |||
void changeProgramName (int index, const String& newName) override | |||
{ | |||
if (index == getCurrentProgram()) | |||
if (index >= 0 && index == getCurrentProgram()) | |||
{ | |||
if (getNumPrograms() > 0 && newName != getCurrentProgramName()) | |||
dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toRawUTF8(), 0.0f); | |||
@@ -1717,7 +1719,7 @@ private: | |||
const int index = getCurrentProgram(); | |||
if (programNames[index].isEmpty()) | |||
if (index >= 0 && programNames[index].isEmpty()) | |||
{ | |||
while (programNames.size() < index) | |||
programNames.add (String::empty); | |||
@@ -2571,10 +2573,10 @@ private: | |||
}; | |||
friend class InnerWrapperComponent; | |||
ScopedPointer <InnerWrapperComponent> innerWrapper; | |||
ScopedPointer<InnerWrapperComponent> innerWrapper; | |||
#else | |||
ScopedPointer <NSViewComponent> innerWrapper; | |||
ScopedPointer<NSViewComponent> innerWrapper; | |||
#endif | |||
void resized() override | |||
@@ -2611,16 +2613,20 @@ VSTPluginFormat::~VSTPluginFormat() {} | |||
static VSTPluginInstance* createAndUpdateDesc (VSTPluginFormat& format, PluginDescription& desc) | |||
{ | |||
if (VSTPluginInstance* instance = dynamic_cast <VSTPluginInstance*> (format.createInstanceFromDescription (desc))) | |||
if (AudioPluginInstance* p = format.createInstanceFromDescription (desc, 44100.0, 512)) | |||
{ | |||
#if JUCE_MAC | |||
if (instance->module->resFileId != 0) | |||
UseResFile (instance->module->resFileId); | |||
#endif | |||
if (VSTPluginInstance* instance = dynamic_cast<VSTPluginInstance*> (p)) | |||
{ | |||
#if JUCE_MAC | |||
if (instance->module->resFileId != 0) | |||
UseResFile (instance->module->resFileId); | |||
#endif | |||
instance->fillInPluginDescription (desc); | |||
instance->fillInPluginDescription (desc); | |||
return instance; | |||
} | |||
return instance; | |||
jassertfalse; | |||
} | |||
return nullptr; | |||
@@ -2667,6 +2673,7 @@ void VSTPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& resul | |||
{ | |||
jassert (desc.uid == uid); | |||
desc.name = shellEffectName; | |||
desc.hasSharedContainer = true; | |||
if (! arrayContainsPlugin (results, desc)) | |||
results.add (new PluginDescription (desc)); | |||
@@ -2675,9 +2682,10 @@ void VSTPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& resul | |||
} | |||
} | |||
AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc) | |||
AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc, | |||
double sampleRate, int blockSize) | |||
{ | |||
ScopedPointer <VSTPluginInstance> result; | |||
ScopedPointer<VSTPluginInstance> result; | |||
if (fileMightContainThisPluginType (desc.fileOrIdentifier)) | |||
{ | |||
@@ -2686,9 +2694,7 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi | |||
const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); | |||
file.getParentDirectory().setAsCurrentWorkingDirectory(); | |||
const ModuleHandle::Ptr module (ModuleHandle::findOrCreateModule (file)); | |||
if (module != nullptr) | |||
if (ModuleHandle::Ptr module = ModuleHandle::findOrCreateModule (file)) | |||
{ | |||
shellUIDToCreate = desc.uid; | |||
@@ -2697,7 +2703,7 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi | |||
if (result->effect != nullptr) | |||
{ | |||
result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) (VSTPluginInstance*) result; | |||
result->initialise(); | |||
result->initialise (sampleRate, blockSize); | |||
} | |||
else | |||
{ | |||
@@ -86,8 +86,8 @@ public: | |||
//============================================================================== | |||
String getName() const override { return "VST"; } | |||
void findAllTypesForFile (OwnedArray <PluginDescription>&, const String& fileOrIdentifier) override; | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&) override; | |||
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override; | |||
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override; | |||
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; | |||
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; | |||
bool pluginNeedsRescanning (const PluginDescription&) override; | |||
@@ -1,7 +1,7 @@ | |||
{ | |||
"id": "juce_audio_processors", | |||
"name": "JUCE audio plugin hosting classes", | |||
"version": "2.1.3", | |||
"version": "2.1.6", | |||
"description": "Classes for loading and playing VST, AU, or internally-generated audio processors.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
@@ -55,7 +55,7 @@ AudioProcessor::~AudioProcessor() | |||
#endif | |||
} | |||
void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead) noexcept | |||
void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead) | |||
{ | |||
playHead = newPlayHead; | |||
} | |||
@@ -139,44 +139,59 @@ AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) cons | |||
void AudioProcessor::sendParamChangeMessageToListeners (const int parameterIndex, const float newValue) | |||
{ | |||
jassert (isPositiveAndBelow (parameterIndex, getNumParameters())); | |||
for (int i = listeners.size(); --i >= 0;) | |||
if (AudioProcessorListener* l = getListenerLocked (i)) | |||
l->audioProcessorParameterChanged (this, parameterIndex, newValue); | |||
if (isPositiveAndBelow (parameterIndex, getNumParameters())) | |||
{ | |||
for (int i = listeners.size(); --i >= 0;) | |||
if (AudioProcessorListener* l = getListenerLocked (i)) | |||
l->audioProcessorParameterChanged (this, parameterIndex, newValue); | |||
} | |||
else | |||
{ | |||
jassertfalse; // called with an out-of-range parameter index! | |||
} | |||
} | |||
void AudioProcessor::beginParameterChangeGesture (int parameterIndex) | |||
{ | |||
jassert (isPositiveAndBelow (parameterIndex, getNumParameters())); | |||
#if JUCE_DEBUG | |||
// This means you've called beginParameterChangeGesture twice in succession without a matching | |||
// call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. | |||
jassert (! changingParams [parameterIndex]); | |||
changingParams.setBit (parameterIndex); | |||
#endif | |||
for (int i = listeners.size(); --i >= 0;) | |||
if (AudioProcessorListener* l = getListenerLocked (i)) | |||
l->audioProcessorParameterChangeGestureBegin (this, parameterIndex); | |||
if (isPositiveAndBelow (parameterIndex, getNumParameters())) | |||
{ | |||
#if JUCE_DEBUG | |||
// This means you've called beginParameterChangeGesture twice in succession without a matching | |||
// call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. | |||
jassert (! changingParams [parameterIndex]); | |||
changingParams.setBit (parameterIndex); | |||
#endif | |||
for (int i = listeners.size(); --i >= 0;) | |||
if (AudioProcessorListener* l = getListenerLocked (i)) | |||
l->audioProcessorParameterChangeGestureBegin (this, parameterIndex); | |||
} | |||
else | |||
{ | |||
jassertfalse; // called with an out-of-range parameter index! | |||
} | |||
} | |||
void AudioProcessor::endParameterChangeGesture (int parameterIndex) | |||
{ | |||
jassert (isPositiveAndBelow (parameterIndex, getNumParameters())); | |||
#if JUCE_DEBUG | |||
// This means you've called endParameterChangeGesture without having previously called | |||
// endParameterChangeGesture. That might be fine in most hosts, but better to keep the | |||
// calls matched correctly. | |||
jassert (changingParams [parameterIndex]); | |||
changingParams.clearBit (parameterIndex); | |||
#endif | |||
for (int i = listeners.size(); --i >= 0;) | |||
if (AudioProcessorListener* l = getListenerLocked (i)) | |||
l->audioProcessorParameterChangeGestureEnd (this, parameterIndex); | |||
if (isPositiveAndBelow (parameterIndex, getNumParameters())) | |||
{ | |||
#if JUCE_DEBUG | |||
// This means you've called endParameterChangeGesture without having previously called | |||
// endParameterChangeGesture. That might be fine in most hosts, but better to keep the | |||
// calls matched correctly. | |||
jassert (changingParams [parameterIndex]); | |||
changingParams.clearBit (parameterIndex); | |||
#endif | |||
for (int i = listeners.size(); --i >= 0;) | |||
if (AudioProcessorListener* l = getListenerLocked (i)) | |||
l->audioProcessorParameterChangeGestureEnd (this, parameterIndex); | |||
} | |||
else | |||
{ | |||
jassertfalse; // called with an out-of-range parameter index! | |||
} | |||
} | |||
void AudioProcessor::updateHostDisplay() | |||
@@ -247,16 +262,17 @@ const uint32 magicXmlNumber = 0x21324356; | |||
void AudioProcessor::copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& destData) | |||
{ | |||
const String xmlString (xml.createDocument (String::empty, true, false)); | |||
const size_t stringLength = xmlString.getNumBytesAsUTF8(); | |||
destData.setSize (stringLength + 9); | |||
uint32* const d = static_cast<uint32*> (destData.getData()); | |||
d[0] = ByteOrder::swapIfBigEndian ((const uint32) magicXmlNumber); | |||
d[1] = ByteOrder::swapIfBigEndian ((const uint32) stringLength); | |||
{ | |||
MemoryOutputStream out (destData, false); | |||
out.writeInt (magicXmlNumber); | |||
out.writeInt (0); | |||
xml.writeToStream (out, String::empty, true, false); | |||
out.writeByte (0); | |||
} | |||
xmlString.copyToUTF8 ((CharPointer_UTF8::CharType*) (d + 2), stringLength + 1); | |||
// go back and write the string length.. | |||
static_cast<uint32*> (destData.getData())[1] | |||
= ByteOrder::swapIfBigEndian ((uint32) destData.getSize() - 9); | |||
} | |||
XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeInBytes) | |||
@@ -276,7 +292,7 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeIn | |||
//============================================================================== | |||
void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {} | |||
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} | |||
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} | |||
//============================================================================== | |||
bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept | |||
@@ -596,7 +596,7 @@ public: | |||
The processor will not take ownership of the object, so the caller must delete it when | |||
it is no longer being used. | |||
*/ | |||
void setPlayHead (AudioPlayHead* newPlayHead) noexcept; | |||
virtual void setPlayHead (AudioPlayHead* newPlayHead); | |||
//============================================================================== | |||
/** This is called by the processor to specify its details before being played. */ | |||
@@ -625,10 +625,6 @@ public: | |||
*/ | |||
WrapperType wrapperType; | |||
/** @internal */ | |||
static void JUCE_CALLTYPE setTypeOfNextNewPlugin (WrapperType); | |||
protected: | |||
//============================================================================== | |||
/** Helper function that just converts an xml element into a binary blob. | |||
@@ -648,6 +644,10 @@ protected: | |||
*/ | |||
static XmlElement* getXmlFromBinary (const void* data, int sizeInBytes); | |||
/** @internal */ | |||
static void JUCE_CALLTYPE setTypeOfNextNewPlugin (WrapperType); | |||
protected: | |||
/** @internal */ | |||
AudioPlayHead* playHead; | |||
@@ -655,7 +655,7 @@ protected: | |||
void sendParamChangeMessageToListeners (int parameterIndex, float newValue); | |||
private: | |||
Array <AudioProcessorListener*> listeners; | |||
Array<AudioProcessorListener*> listeners; | |||
Component::SafePointer<AudioProcessorEditor> activeEditor; | |||
double sampleRate; | |||
int blockSize, numInputChannels, numOutputChannels, latencySamples; | |||
@@ -26,7 +26,8 @@ PluginDescription::PluginDescription() | |||
: uid (0), | |||
isInstrument (false), | |||
numInputChannels (0), | |||
numOutputChannels (0) | |||
numOutputChannels (0), | |||
hasSharedContainer (false) | |||
{ | |||
} | |||
@@ -46,7 +47,8 @@ PluginDescription::PluginDescription (const PluginDescription& other) | |||
uid (other.uid), | |||
isInstrument (other.isInstrument), | |||
numInputChannels (other.numInputChannels), | |||
numOutputChannels (other.numOutputChannels) | |||
numOutputChannels (other.numOutputChannels), | |||
hasSharedContainer (other.hasSharedContainer) | |||
{ | |||
} | |||
@@ -64,6 +66,7 @@ PluginDescription& PluginDescription::operator= (const PluginDescription& other) | |||
lastFileModTime = other.lastFileModTime; | |||
numInputChannels = other.numInputChannels; | |||
numOutputChannels = other.numOutputChannels; | |||
hasSharedContainer = other.hasSharedContainer; | |||
return *this; | |||
} | |||
@@ -99,6 +102,7 @@ XmlElement* PluginDescription::createXml() const | |||
e->setAttribute ("fileTime", String::toHexString (lastFileModTime.toMilliseconds())); | |||
e->setAttribute ("numInputs", numInputChannels); | |||
e->setAttribute ("numOutputs", numOutputChannels); | |||
e->setAttribute ("isShell", hasSharedContainer); | |||
return e; | |||
} | |||
@@ -119,6 +123,7 @@ bool PluginDescription::loadFromXml (const XmlElement& xml) | |||
lastFileModTime = Time (xml.getStringAttribute ("fileTime").getHexValue64()); | |||
numInputChannels = xml.getIntAttribute ("numInputs"); | |||
numOutputChannels = xml.getIntAttribute ("numOutputs"); | |||
hasSharedContainer = xml.getBoolAttribute ("isShell", false); | |||
return true; | |||
} | |||
@@ -28,9 +28,9 @@ | |||
//============================================================================== | |||
/** | |||
A small class to represent some facts about a particular type of plugin. | |||
A small class to represent some facts about a particular type of plug-in. | |||
This class is for storing and managing the details about a plugin without | |||
This class is for storing and managing the details about a plug-in without | |||
actually having to load an instance of it. | |||
A KnownPluginList contains a list of PluginDescription objects. | |||
@@ -47,21 +47,19 @@ public: | |||
~PluginDescription(); | |||
//============================================================================== | |||
/** The name of the plugin. */ | |||
/** The name of the plug-in. */ | |||
String name; | |||
/** A more descriptive name for the plugin. | |||
This may be the same as the 'name' field, but some plugins may provide an | |||
/** A more descriptive name for the plug-in. | |||
This may be the same as the 'name' field, but some plug-ins may provide an | |||
alternative name. | |||
*/ | |||
String descriptiveName; | |||
/** The plugin format, e.g. "VST", "AudioUnit", etc. | |||
*/ | |||
/** The plug-in format, e.g. "VST", "AudioUnit", etc. */ | |||
String pluginFormatName; | |||
/** A category, such as "Dynamics", "Reverbs", etc. | |||
*/ | |||
/** A category, such as "Dynamics", "Reverbs", etc. */ | |||
String category; | |||
/** The manufacturer. */ | |||
@@ -70,20 +68,20 @@ public: | |||
/** The version. This string doesn't have any particular format. */ | |||
String version; | |||
/** Either the file containing the plugin module, or some other unique way | |||
/** Either the file containing the plug-in module, or some other unique way | |||
of identifying it. | |||
E.g. for an AU, this would be an ID string that the component manager | |||
could use to retrieve the plugin. For a VST, it's the file path. | |||
could use to retrieve the plug-in. For a VST, it's the file path. | |||
*/ | |||
String fileOrIdentifier; | |||
/** The last time the plugin file was changed. | |||
This is handy when scanning for new or changed plugins. | |||
/** The last time the plug-in file was changed. | |||
This is handy when scanning for new or changed plug-ins. | |||
*/ | |||
Time lastFileModTime; | |||
/** A unique ID for the plugin. | |||
/** A unique ID for the plug-in. | |||
Note that this might not be unique between formats, e.g. a VST and some | |||
other format might actually have the same id. | |||
@@ -92,7 +90,7 @@ public: | |||
*/ | |||
int uid; | |||
/** True if the plugin identifies itself as a synthesiser. */ | |||
/** True if the plug-in identifies itself as a synthesiser. */ | |||
bool isInstrument; | |||
/** The number of inputs. */ | |||
@@ -101,10 +99,13 @@ public: | |||
/** The number of outputs. */ | |||
int numOutputChannels; | |||
/** Returns true if the two descriptions refer the the same plugin. | |||
/** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */ | |||
bool hasSharedContainer; | |||
/** Returns true if the two descriptions refer the the same plug-in. | |||
This isn't quite as simple as them just having the same file (because of | |||
shell plugins). | |||
shell plug-ins). | |||
*/ | |||
bool isDuplicateOf (const PluginDescription& other) const; | |||
@@ -113,7 +114,7 @@ public: | |||
plugin again. | |||
This contains less info than the XML encoding, and is independent of the | |||
plugin's file location, so can be used to store a plugin ID for use | |||
plug-in's file location, so can be used to store a plug-in ID for use | |||
across different machines. | |||
*/ | |||
String createIdentifierString() const; | |||
@@ -128,7 +129,7 @@ public: | |||
/** Reloads the info in this structure from an XML record that was previously | |||
saved with createXML(). | |||
Returns true if the XML was a valid plugin description. | |||
Returns true if the XML was a valid plug-in description. | |||
*/ | |||
bool loadFromXml (const XmlElement& xml); | |||
@@ -85,7 +85,7 @@ public: | |||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
Array (Array<ElementType, TypeOfCriticalSectionToUse>&& other) noexcept | |||
: data (static_cast <ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data)), | |||
: data (static_cast<ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data)), | |||
numUsed (other.numUsed) | |||
{ | |||
other.numUsed = 0; | |||
@@ -143,7 +143,7 @@ public: | |||
Array& operator= (Array&& other) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data = static_cast <ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data); | |||
data = static_cast<ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data); | |||
numUsed = other.numUsed; | |||
other.numUsed = 0; | |||
return *this; | |||
@@ -279,8 +279,14 @@ public: | |||
inline ElementType getFirst() const | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
return (numUsed > 0) ? data.elements [0] | |||
: ElementType(); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements[0]; | |||
} | |||
return ElementType(); | |||
} | |||
/** Returns the last element in the array, or a default value if the array is empty. | |||
@@ -290,8 +296,14 @@ public: | |||
inline ElementType getLast() const | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
return (numUsed > 0) ? data.elements [numUsed - 1] | |||
: ElementType(); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements[numUsed - 1]; | |||
} | |||
return ElementType(); | |||
} | |||
/** Returns a pointer to the actual array data. | |||
@@ -317,6 +329,11 @@ public: | |||
*/ | |||
inline ElementType* end() const noexcept | |||
{ | |||
#if JUCE_DEBUG | |||
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy) | |||
return data.elements; | |||
#endif | |||
return data.elements + numUsed; | |||
} | |||
@@ -366,13 +383,27 @@ public: | |||
@param newElement the new object to add to the array | |||
@see set, insert, addIfNotAlreadyThere, addSorted, addUsingDefaultSort, addArray | |||
*/ | |||
void add (ParameterType newElement) | |||
void add (const ElementType& newElement) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.ensureAllocatedSize (numUsed + 1); | |||
new (data.elements + numUsed++) ElementType (newElement); | |||
} | |||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
/** Appends a new element at the end of the array. | |||
@param newElement the new object to add to the array | |||
@see set, insert, addIfNotAlreadyThere, addSorted, addUsingDefaultSort, addArray | |||
*/ | |||
void add (ElementType&& newElement) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.ensureAllocatedSize (numUsed + 1); | |||
new (data.elements + numUsed++) ElementType (static_cast<ElementType&&> (newElement)); | |||
} | |||
#endif | |||
/** Inserts a new element into the array at a given position. | |||
If the index is less than 0 or greater than the size of the array, the | |||
@@ -519,6 +550,7 @@ public: | |||
if (isPositiveAndBelow (indexToChange, numUsed)) | |||
{ | |||
jassert (data.elements != nullptr); | |||
data.elements [indexToChange] = newValue; | |||
} | |||
else if (indexToChange >= 0) | |||
@@ -60,20 +60,79 @@ bool DynamicObject::hasMethod (const Identifier& methodName) const | |||
return getProperty (methodName).isMethod(); | |||
} | |||
var DynamicObject::invokeMethod (const Identifier& methodName, | |||
const var* parameters, | |||
int numParameters) | |||
var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArgs& args) | |||
{ | |||
return properties [methodName].invokeMethod (this, parameters, numParameters); | |||
if (var::NativeFunction function = properties [method].getNativeFunction()) | |||
return function (args); | |||
return var(); | |||
} | |||
void DynamicObject::setMethod (const Identifier& name, | |||
var::MethodFunction methodFunction) | |||
void DynamicObject::setMethod (Identifier name, var::NativeFunction function) | |||
{ | |||
properties.set (name, var (methodFunction)); | |||
properties.set (name, var (function)); | |||
} | |||
void DynamicObject::clear() | |||
{ | |||
properties.clear(); | |||
} | |||
DynamicObject::Ptr DynamicObject::clone() | |||
{ | |||
DynamicObject* newCopy = new DynamicObject(); | |||
newCopy->properties = properties; | |||
for (LinkedListPointer<NamedValueSet::NamedValue>* i = &(newCopy->properties.values);;) | |||
{ | |||
if (NamedValueSet::NamedValue* const v = i->get()) | |||
{ | |||
v->value = v->value.clone(); | |||
i = &(v->nextListItem); | |||
} | |||
else | |||
break; | |||
} | |||
return newCopy; | |||
} | |||
void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine) | |||
{ | |||
out << '{'; | |||
if (! allOnOneLine) | |||
out << newLine; | |||
for (LinkedListPointer<NamedValueSet::NamedValue>* i = &(properties.values);;) | |||
{ | |||
if (NamedValueSet::NamedValue* const v = i->get()) | |||
{ | |||
if (! allOnOneLine) | |||
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); | |||
out << '"'; | |||
JSONFormatter::writeString (out, v->name); | |||
out << "\": "; | |||
JSONFormatter::write (out, v->value, indentLevel + JSONFormatter::indentSize, allOnOneLine); | |||
if (v->nextListItem.get() != nullptr) | |||
{ | |||
if (allOnOneLine) | |||
out << ", "; | |||
else | |||
out << ',' << newLine; | |||
} | |||
else if (! allOnOneLine) | |||
out << newLine; | |||
i = &(v->nextListItem); | |||
} | |||
else | |||
break; | |||
} | |||
if (! allOnOneLine) | |||
JSONFormatter::writeSpaces (out, indentLevel); | |||
out << '}'; | |||
} |
@@ -86,23 +86,16 @@ public: | |||
This method is virtual to allow more dynamic invocation to used for objects | |||
where the methods may not already be set as properies. | |||
*/ | |||
virtual var invokeMethod (const Identifier& methodName, | |||
const var* parameters, | |||
int numParameters); | |||
virtual var invokeMethod (Identifier methodName, | |||
const var::NativeFunctionArgs& args); | |||
/** Sets up a method. | |||
/** Adds a method to the class. | |||
This is basically the same as calling setProperty (methodName, (var::MethodFunction) myFunction), but | |||
This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but | |||
helps to avoid accidentally invoking the wrong type of var constructor. It also makes | |||
the code easier to read, | |||
The compiler will probably force you to use an explicit cast your method to a (var::MethodFunction), e.g. | |||
@code | |||
setMethod ("doSomething", (var::MethodFunction) &MyClass::doSomething); | |||
@endcode | |||
*/ | |||
void setMethod (const Identifier& methodName, | |||
var::MethodFunction methodFunction); | |||
void setMethod (Identifier methodName, var::NativeFunction function); | |||
//============================================================================== | |||
/** Removes all properties and methods from the object. */ | |||
@@ -111,10 +104,31 @@ public: | |||
/** Returns the NamedValueSet that holds the object's properties. */ | |||
NamedValueSet& getProperties() noexcept { return properties; } | |||
//============================================================================== | |||
/** Returns a clone of this object. | |||
The default implementation of this method just returns a new DynamicObject | |||
with a (deep) copy of all of its properties. Subclasses can override this to | |||
implement their own custom copy routines. | |||
*/ | |||
virtual Ptr clone(); | |||
//============================================================================== | |||
/** Writes this object to a text stream in JSON format. | |||
This method is used by JSON::toString and JSON::writeToStream, and you should | |||
never need to call it directly, but it's virtual so that custom object types | |||
can stringify themselves appropriately. | |||
*/ | |||
virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine); | |||
private: | |||
//============================================================================== | |||
NamedValueSet properties; | |||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE | |||
// These methods have been deprecated - use var::invoke instead | |||
virtual void invokeMethod (const Identifier&, const var*, int) {} | |||
#endif | |||
JUCE_LEAK_DETECTOR (DynamicObject) | |||
}; | |||
@@ -155,7 +155,7 @@ private: | |||
friend class LinkedListPointer<NamedValue>; | |||
LinkedListPointer<NamedValue> values; | |||
friend class JSONFormatter; | |||
friend class DynamicObject; | |||
}; | |||
@@ -166,8 +166,14 @@ public: | |||
inline ObjectClass* getFirst() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
return numUsed > 0 ? data.elements [0] | |||
: static_cast <ObjectClass*> (nullptr); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [0]; | |||
} | |||
return nullptr; | |||
} | |||
/** Returns a pointer to the last object in the array. | |||
@@ -178,8 +184,14 @@ public: | |||
inline ObjectClass* getLast() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
return numUsed > 0 ? data.elements [numUsed - 1] | |||
: static_cast <ObjectClass*> (nullptr); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [numUsed - 1]; | |||
} | |||
return nullptr; | |||
} | |||
/** Returns a pointer to the actual array data. | |||
@@ -205,6 +217,11 @@ public: | |||
*/ | |||
inline ObjectClass** end() const noexcept | |||
{ | |||
#if JUCE_DEBUG | |||
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy) | |||
return data.elements; | |||
#endif | |||
return data.elements + numUsed; | |||
} | |||
@@ -180,8 +180,14 @@ public: | |||
inline ObjectClass* getObjectPointer (const int index) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
return isPositiveAndBelow (index, numUsed) ? data.elements [index] | |||
: nullptr; | |||
if (isPositiveAndBelow (index, numUsed)) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [index]; | |||
} | |||
return ObjectClassPtr(); | |||
} | |||
/** Returns a raw pointer to the object at this index in the array, without checking | |||
@@ -190,7 +196,7 @@ public: | |||
inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
jassert (isPositiveAndBelow (index, numUsed)); | |||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); | |||
return data.elements [index]; | |||
} | |||
@@ -202,8 +208,14 @@ public: | |||
inline ObjectClassPtr getFirst() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
return numUsed > 0 ? data.elements [0] | |||
: static_cast <ObjectClass*> (nullptr); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [0]; | |||
} | |||
return ObjectClassPtr(); | |||
} | |||
/** Returns a pointer to the last object in the array. | |||
@@ -214,8 +226,14 @@ public: | |||
inline ObjectClassPtr getLast() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
return numUsed > 0 ? data.elements [numUsed - 1] | |||
: static_cast <ObjectClass*> (nullptr); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [numUsed - 1]; | |||
} | |||
return ObjectClassPtr(); | |||
} | |||
/** Returns a pointer to the actual array data. | |||
@@ -325,35 +343,31 @@ public: | |||
ObjectClass* insert (int indexToInsertAt, | |||
ObjectClass* const newObject) noexcept | |||
{ | |||
if (indexToInsertAt >= 0) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (indexToInsertAt < 0) | |||
return add (newObject); | |||
const ScopedLockType lock (getLock()); | |||
if (indexToInsertAt > numUsed) | |||
indexToInsertAt = numUsed; | |||
if (indexToInsertAt > numUsed) | |||
indexToInsertAt = numUsed; | |||
data.ensureAllocatedSize (numUsed + 1); | |||
jassert (data.elements != nullptr); | |||
data.ensureAllocatedSize (numUsed + 1); | |||
jassert (data.elements != nullptr); | |||
ObjectClass** const e = data.elements + indexToInsertAt; | |||
const int numToMove = numUsed - indexToInsertAt; | |||
ObjectClass** const e = data.elements + indexToInsertAt; | |||
const int numToMove = numUsed - indexToInsertAt; | |||
if (numToMove > 0) | |||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); | |||
if (numToMove > 0) | |||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); | |||
*e = newObject; | |||
*e = newObject; | |||
if (newObject != nullptr) | |||
newObject->incReferenceCount(); | |||
if (newObject != nullptr) | |||
newObject->incReferenceCount(); | |||
++numUsed; | |||
++numUsed; | |||
return newObject; | |||
} | |||
else | |||
{ | |||
return add (newObject); | |||
} | |||
return newObject; | |||
} | |||
/** Appends a new object at the end of the array as long as the array doesn't | |||
@@ -122,7 +122,6 @@ public: | |||
} | |||
/** Removes all elements from the set without freeing the array's allocated storage. | |||
@see clear | |||
*/ | |||
void clearQuick() noexcept | |||
@@ -240,7 +239,8 @@ public: | |||
if (halfway == s) | |||
return -1; | |||
else if (elementToLookFor < data.getReference (halfway)) | |||
if (elementToLookFor < data.getReference (halfway)) | |||
e = halfway; | |||
else | |||
s = halfway; | |||
@@ -295,7 +295,8 @@ public: | |||
break; | |||
} | |||
else if (isBeforeHalfway) | |||
if (isBeforeHalfway) | |||
e = halfway; | |||
else | |||
s = halfway; | |||
@@ -483,7 +484,7 @@ public: | |||
private: | |||
//============================================================================== | |||
Array <ElementType, TypeOfCriticalSectionToUse> data; | |||
Array<ElementType, TypeOfCriticalSectionToUse> data; | |||
}; | |||
#if JUCE_MSVC | |||
@@ -38,7 +38,7 @@ | |||
ranges of values. It's quite a specialised class, mostly useful for things | |||
like keeping the set of selected rows in a listbox. | |||
The type used as a template paramter must be an integer type, such as int, short, | |||
The type used as a template parameter must be an integer type, such as int, short, | |||
int64, etc. | |||
*/ | |||
template <class Type> | |||
@@ -35,7 +35,8 @@ enum VariantStreamMarkers | |||
varMarker_String = 5, | |||
varMarker_Int64 = 6, | |||
varMarker_Array = 7, | |||
varMarker_Binary = 8 | |||
varMarker_Binary = 8, | |||
varMarker_Undefined = 9 | |||
}; | |||
//============================================================================== | |||
@@ -53,8 +54,10 @@ public: | |||
virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; } | |||
virtual Array<var>* toArray (const ValueUnion&) const noexcept { return nullptr; } | |||
virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; } | |||
virtual var clone (const var& original) const { return original; } | |||
virtual bool isVoid() const noexcept { return false; } | |||
virtual bool isUndefined() const noexcept { return false; } | |||
virtual bool isInt() const noexcept { return false; } | |||
virtual bool isInt64() const noexcept { return false; } | |||
virtual bool isBool() const noexcept { return false; } | |||
@@ -78,9 +81,27 @@ public: | |||
VariantType_Void() noexcept {} | |||
static const VariantType_Void instance; | |||
bool isVoid() const noexcept { return true; } | |||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept { return otherType.isVoid(); } | |||
void writeToStream (const ValueUnion&, OutputStream& output) const { output.writeCompressedInt (0); } | |||
bool isVoid() const noexcept override { return true; } | |||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); } | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Undefined : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Undefined() noexcept {} | |||
static const VariantType_Undefined instance; | |||
bool isUndefined() const noexcept override { return true; } | |||
String toString (const ValueUnion&) const override { return "undefined"; } | |||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1); | |||
output.writeByte (varMarker_Undefined); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -90,19 +111,19 @@ public: | |||
VariantType_Int() noexcept {} | |||
static const VariantType_Int instance; | |||
int toInt (const ValueUnion& data) const noexcept { return data.intValue; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept { return (int64) data.intValue; }; | |||
double toDouble (const ValueUnion& data) const noexcept { return (double) data.intValue; } | |||
String toString (const ValueUnion& data) const { return String (data.intValue); } | |||
bool toBool (const ValueUnion& data) const noexcept { return data.intValue != 0; } | |||
bool isInt() const noexcept { return true; } | |||
int toInt (const ValueUnion& data) const noexcept override { return data.intValue; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.intValue; }; | |||
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; } | |||
String toString (const ValueUnion& data) const override { return String (data.intValue); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; } | |||
bool isInt() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toInt (otherData) == data.intValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (5); | |||
output.writeByte (varMarker_Int); | |||
@@ -117,19 +138,19 @@ public: | |||
VariantType_Int64() noexcept {} | |||
static const VariantType_Int64 instance; | |||
int toInt (const ValueUnion& data) const noexcept { return (int) data.int64Value; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept { return data.int64Value; }; | |||
double toDouble (const ValueUnion& data) const noexcept { return (double) data.int64Value; } | |||
String toString (const ValueUnion& data) const { return String (data.int64Value); } | |||
bool toBool (const ValueUnion& data) const noexcept { return data.int64Value != 0; } | |||
bool isInt64() const noexcept { return true; } | |||
int toInt (const ValueUnion& data) const noexcept override { return (int) data.int64Value; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.int64Value; }; | |||
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; } | |||
String toString (const ValueUnion& data) const override { return String (data.int64Value); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; } | |||
bool isInt64() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toInt64 (otherData) == data.int64Value; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (9); | |||
output.writeByte (varMarker_Int64); | |||
@@ -144,19 +165,19 @@ public: | |||
VariantType_Double() noexcept {} | |||
static const VariantType_Double instance; | |||
int toInt (const ValueUnion& data) const noexcept { return (int) data.doubleValue; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept { return (int64) data.doubleValue; }; | |||
double toDouble (const ValueUnion& data) const noexcept { return data.doubleValue; } | |||
String toString (const ValueUnion& data) const { return String (data.doubleValue); } | |||
bool toBool (const ValueUnion& data) const noexcept { return data.doubleValue != 0; } | |||
bool isDouble() const noexcept { return true; } | |||
int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; }; | |||
double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; } | |||
String toString (const ValueUnion& data) const override { return String (data.doubleValue); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0; } | |||
bool isDouble() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon(); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (9); | |||
output.writeByte (varMarker_Double); | |||
@@ -171,19 +192,19 @@ public: | |||
VariantType_Bool() noexcept {} | |||
static const VariantType_Bool instance; | |||
int toInt (const ValueUnion& data) const noexcept { return data.boolValue ? 1 : 0; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept { return data.boolValue ? 1 : 0; }; | |||
double toDouble (const ValueUnion& data) const noexcept { return data.boolValue ? 1.0 : 0.0; } | |||
String toString (const ValueUnion& data) const { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } | |||
bool toBool (const ValueUnion& data) const noexcept { return data.boolValue; } | |||
bool isBool() const noexcept { return true; } | |||
int toInt (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; }; | |||
double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; } | |||
String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; } | |||
bool isBool() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toBool (otherData) == data.boolValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1); | |||
output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse); | |||
@@ -197,24 +218,24 @@ public: | |||
VariantType_String() noexcept {} | |||
static const VariantType_String instance; | |||
void cleanUp (ValueUnion& data) const noexcept { getString (data)-> ~String(); } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const { new (dest.stringValue) String (*getString (source)); } | |||
void cleanUp (ValueUnion& data) const noexcept override { getString (data)-> ~String(); } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { new (dest.stringValue) String (*getString (source)); } | |||
bool isString() const noexcept { return true; } | |||
int toInt (const ValueUnion& data) const noexcept { return getString (data)->getIntValue(); }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept { return getString (data)->getLargeIntValue(); }; | |||
double toDouble (const ValueUnion& data) const noexcept { return getString (data)->getDoubleValue(); } | |||
String toString (const ValueUnion& data) const { return *getString (data); } | |||
bool toBool (const ValueUnion& data) const noexcept { return getString (data)->getIntValue() != 0 | |||
|| getString (data)->trim().equalsIgnoreCase ("true") | |||
|| getString (data)->trim().equalsIgnoreCase ("yes"); } | |||
bool isString() const noexcept override { return true; } | |||
int toInt (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue(); }; | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return getString (data)->getLargeIntValue(); }; | |||
double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); } | |||
String toString (const ValueUnion& data) const override { return *getString (data); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0 | |||
|| getString (data)->trim().equalsIgnoreCase ("true") | |||
|| getString (data)->trim().equalsIgnoreCase ("yes"); } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toString (otherData) == *getString (data); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
const String* const s = getString (data); | |||
const size_t len = s->getNumBytesAsUTF8() + 1; | |||
@@ -237,26 +258,35 @@ public: | |||
VariantType_Object() noexcept {} | |||
static const VariantType_Object instance; | |||
void cleanUp (ValueUnion& data) const noexcept { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } | |||
void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override | |||
{ | |||
dest.objectValue = source.objectValue; | |||
if (dest.objectValue != nullptr) | |||
dest.objectValue->incReferenceCount(); | |||
} | |||
String toString (const ValueUnion& data) const { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } | |||
bool toBool (const ValueUnion& data) const noexcept { return data.objectValue != 0; } | |||
ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept { return data.objectValue; } | |||
bool isObject() const noexcept { return true; } | |||
String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != 0; } | |||
ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; } | |||
bool isObject() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toObject (otherData) == data.objectValue; | |||
} | |||
void writeToStream (const ValueUnion&, OutputStream& output) const | |||
var clone (const var& original) const override | |||
{ | |||
if (DynamicObject* d = original.getDynamicObject()) | |||
return d->clone().get(); | |||
jassertfalse; // can only clone DynamicObjects! | |||
return var(); | |||
} | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override | |||
{ | |||
jassertfalse; // Can't write an object to a stream! | |||
output.writeCompressedInt (0); | |||
@@ -264,38 +294,67 @@ public: | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Array : public var::VariantType | |||
class var::VariantType_Array : public var::VariantType_Object | |||
{ | |||
public: | |||
VariantType_Array() noexcept {} | |||
static const VariantType_Array instance; | |||
void cleanUp (ValueUnion& data) const noexcept { delete data.arrayValue; } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest.arrayValue = new Array<var> (*(source.arrayValue)); } | |||
String toString (const ValueUnion&) const override { return "[Array]"; } | |||
ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; } | |||
bool isArray() const noexcept override { return true; } | |||
Array<var>* toArray (const ValueUnion& data) const noexcept override | |||
{ | |||
if (RefCountedArray* a = dynamic_cast<RefCountedArray*> (data.objectValue)) | |||
return &(a->array); | |||
String toString (const ValueUnion&) const { return "[Array]"; } | |||
bool isArray() const noexcept { return true; } | |||
Array<var>* toArray (const ValueUnion& data) const noexcept { return data.arrayValue; } | |||
return nullptr; | |||
} | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
const Array<var>* const thisArray = toArray (data); | |||
const Array<var>* const otherArray = otherType.toArray (otherData); | |||
return otherArray != nullptr && *otherArray == *(data.arrayValue); | |||
return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray); | |||
} | |||
var clone (const var& original) const override | |||
{ | |||
Array<var> arrayCopy; | |||
if (const Array<var>* array = toArray (original.value)) | |||
for (int i = 0; i < array->size(); ++i) | |||
arrayCopy.add (array->getReference(i).clone()); | |||
return var (arrayCopy); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
MemoryOutputStream buffer (512); | |||
const int numItems = data.arrayValue->size(); | |||
buffer.writeCompressedInt (numItems); | |||
if (const Array<var>* array = toArray (data)) | |||
{ | |||
MemoryOutputStream buffer (512); | |||
const int numItems = array->size(); | |||
buffer.writeCompressedInt (numItems); | |||
for (int i = 0; i < numItems; ++i) | |||
data.arrayValue->getReference(i).writeToStream (buffer); | |||
for (int i = 0; i < numItems; ++i) | |||
array->getReference(i).writeToStream (buffer); | |||
output.writeCompressedInt (1 + (int) buffer.getDataSize()); | |||
output.writeByte (varMarker_Array); | |||
output << buffer; | |||
output.writeCompressedInt (1 + (int) buffer.getDataSize()); | |||
output.writeByte (varMarker_Array); | |||
output << buffer; | |||
} | |||
} | |||
struct RefCountedArray : public ReferenceCountedObject | |||
{ | |||
RefCountedArray (const Array<var>& a) : array (a) { incReferenceCount(); } | |||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
RefCountedArray (Array<var>&& a) : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); } | |||
#endif | |||
Array<var> array; | |||
}; | |||
}; | |||
//============================================================================== | |||
@@ -306,20 +365,20 @@ public: | |||
static const VariantType_Binary instance; | |||
void cleanUp (ValueUnion& data) const noexcept { delete data.binaryValue; } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest.binaryValue = new MemoryBlock (*source.binaryValue); } | |||
void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); } | |||
String toString (const ValueUnion& data) const { return data.binaryValue->toBase64Encoding(); } | |||
bool isBinary() const noexcept { return true; } | |||
MemoryBlock* toBinary (const ValueUnion& data) const noexcept { return data.binaryValue; } | |||
String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); } | |||
bool isBinary() const noexcept override { return true; } | |||
MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
const MemoryBlock* const otherBlock = otherType.toBinary (otherData); | |||
return otherBlock != nullptr && *otherBlock == *data.binaryValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1 + (int) data.binaryValue->getSize()); | |||
output.writeByte (varMarker_Binary); | |||
@@ -334,16 +393,16 @@ public: | |||
VariantType_Method() noexcept {} | |||
static const VariantType_Method instance; | |||
String toString (const ValueUnion&) const { return "Method"; } | |||
bool toBool (const ValueUnion& data) const noexcept { return data.methodValue != nullptr; } | |||
bool isMethod() const noexcept { return true; } | |||
String toString (const ValueUnion&) const override { return "Method"; } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; } | |||
bool isMethod() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.isMethod() && otherData.methodValue == data.methodValue; | |||
} | |||
void writeToStream (const ValueUnion&, OutputStream& output) const | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override | |||
{ | |||
jassertfalse; // Can't write a method to a stream! | |||
output.writeCompressedInt (0); | |||
@@ -351,27 +410,23 @@ public: | |||
}; | |||
//============================================================================== | |||
const var::VariantType_Void var::VariantType_Void::instance; | |||
const var::VariantType_Int var::VariantType_Int::instance; | |||
const var::VariantType_Int64 var::VariantType_Int64::instance; | |||
const var::VariantType_Bool var::VariantType_Bool::instance; | |||
const var::VariantType_Double var::VariantType_Double::instance; | |||
const var::VariantType_String var::VariantType_String::instance; | |||
const var::VariantType_Object var::VariantType_Object::instance; | |||
const var::VariantType_Array var::VariantType_Array::instance; | |||
const var::VariantType_Binary var::VariantType_Binary::instance; | |||
const var::VariantType_Method var::VariantType_Method::instance; | |||
const var::VariantType_Void var::VariantType_Void::instance; | |||
const var::VariantType_Undefined var::VariantType_Undefined::instance; | |||
const var::VariantType_Int var::VariantType_Int::instance; | |||
const var::VariantType_Int64 var::VariantType_Int64::instance; | |||
const var::VariantType_Bool var::VariantType_Bool::instance; | |||
const var::VariantType_Double var::VariantType_Double::instance; | |||
const var::VariantType_String var::VariantType_String::instance; | |||
const var::VariantType_Object var::VariantType_Object::instance; | |||
const var::VariantType_Array var::VariantType_Array::instance; | |||
const var::VariantType_Binary var::VariantType_Binary::instance; | |||
const var::VariantType_Method var::VariantType_Method::instance; | |||
//============================================================================== | |||
var::var() noexcept : type (&VariantType_Void::instance) | |||
{ | |||
} | |||
var::~var() noexcept | |||
{ | |||
type->cleanUp (value); | |||
} | |||
var::var() noexcept : type (&VariantType_Void::instance) {} | |||
var::var (const VariantType& t) noexcept : type (&t) {} | |||
var::~var() noexcept { type->cleanUp (value); } | |||
const var var::null; | |||
@@ -385,8 +440,8 @@ var::var (const int v) noexcept : type (&VariantType_Int::instance) { v | |||
var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; } | |||
var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; } | |||
var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; } | |||
var::var (MethodFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = m; } | |||
var::var (const Array<var>& v) : type (&VariantType_Array::instance) { value.arrayValue = new Array<var> (v); } | |||
var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = m; } | |||
var::var (const Array<var>& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); } | |||
var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } | |||
var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } | |||
var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } | |||
@@ -401,9 +456,11 @@ var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::in | |||
object->incReferenceCount(); | |||
} | |||
var var::undefined() noexcept { return var (VariantType_Undefined::instance); } | |||
//============================================================================== | |||
bool var::isVoid() const noexcept { return type->isVoid(); } | |||
bool var::isUndefined() const noexcept { return type->isUndefined(); } | |||
bool var::isInt() const noexcept { return type->isInt(); } | |||
bool var::isInt64() const noexcept { return type->isInt64(); } | |||
bool var::isBool() const noexcept { return type->isBool(); } | |||
@@ -443,7 +500,7 @@ var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = | |||
var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } | |||
var& var::operator= (const Array<var>& v) { var v2 (v); swapWith (v2); return *this; } | |||
var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; } | |||
var& var::operator= (MethodFunction v) { var v2 (v); swapWith (v2); return *this; } | |||
var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; } | |||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
var::var (var&& other) noexcept | |||
@@ -469,6 +526,11 @@ var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance) | |||
value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v)); | |||
} | |||
var::var (Array<var>&& v) : type (&VariantType_Array::instance) | |||
{ | |||
value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (v)); | |||
} | |||
var& var::operator= (String&& v) | |||
{ | |||
type->cleanUp (value); | |||
@@ -489,6 +551,11 @@ bool var::equalsWithSameType (const var& other) const noexcept | |||
return type == other.type && equals (other); | |||
} | |||
bool var::hasSameTypeAs (const var& other) const noexcept | |||
{ | |||
return type == other.type; | |||
} | |||
bool operator== (const var& v1, const var& v2) noexcept { return v1.equals (v2); } | |||
bool operator!= (const var& v1, const var& v2) noexcept { return ! v1.equals (v2); } | |||
bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; } | |||
@@ -496,6 +563,11 @@ bool operator!= (const var& v1, const String& v2) { return v1.toString | |||
bool operator== (const var& v1, const char* const v2) { return v1.toString() == v2; } | |||
bool operator!= (const var& v1, const char* const v2) { return v1.toString() != v2; } | |||
//============================================================================== | |||
var var::clone() const noexcept | |||
{ | |||
return type->clone (*this); | |||
} | |||
//============================================================================== | |||
var var::operator[] (const Identifier propertyName) const | |||
@@ -503,7 +575,7 @@ var var::operator[] (const Identifier propertyName) const | |||
if (DynamicObject* const o = getDynamicObject()) | |||
return o->getProperty (propertyName); | |||
return var::null; | |||
return var(); | |||
} | |||
var var::operator[] (const char* const propertyName) const | |||
@@ -519,22 +591,17 @@ var var::getProperty (const Identifier propertyName, const var& defaultReturnVal | |||
return defaultReturnValue; | |||
} | |||
var var::invoke (const Identifier method, const var* arguments, int numArguments) const | |||
var::NativeFunction var::getNativeFunction() const | |||
{ | |||
if (DynamicObject* const o = getDynamicObject()) | |||
return o->invokeMethod (method, arguments, numArguments); | |||
return var::null; | |||
return isMethod() ? value.methodValue : nullptr; | |||
} | |||
var var::invokeMethod (DynamicObject* const target, const var* const arguments, const int numArguments) const | |||
var var::invoke (Identifier method, const var* arguments, int numArguments) const | |||
{ | |||
jassert (target != nullptr); | |||
if (isMethod()) | |||
return (target->*(value.methodValue)) (arguments, numArguments); | |||
if (DynamicObject* const o = getDynamicObject()) | |||
return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments)); | |||
return var::null; | |||
return var(); | |||
} | |||
var var::call (const Identifier method) const | |||
@@ -604,21 +671,15 @@ var& var::operator[] (int arrayIndex) | |||
Array<var>* var::convertToArray() | |||
{ | |||
Array<var>* array = getArray(); | |||
if (array == nullptr) | |||
{ | |||
const Array<var> tempVar; | |||
var v (tempVar); | |||
array = v.value.arrayValue; | |||
if (Array<var>* array = getArray()) | |||
return array; | |||
if (! isVoid()) | |||
array->add (*this); | |||
Array<var> tempVar; | |||
if (! isVoid()) | |||
tempVar.add (*this); | |||
swapWith (v); | |||
} | |||
return array; | |||
*this = tempVar; | |||
return getArray(); | |||
} | |||
void var::append (const var& n) | |||
@@ -705,5 +766,9 @@ var var::readFromStream (InputStream& input) | |||
} | |||
} | |||
return var::null; | |||
return var(); | |||
} | |||
var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept | |||
: thisObject (t), arguments (args), numArguments (numArgs) | |||
{} |
@@ -47,7 +47,21 @@ class JUCE_API var | |||
{ | |||
public: | |||
//============================================================================== | |||
typedef const var (DynamicObject::*MethodFunction) (const var* arguments, int numArguments); | |||
/** This structure is passed to a NativeFunction callback, and contains invocation | |||
details about the function's arguments and context. | |||
*/ | |||
struct NativeFunctionArgs | |||
{ | |||
NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept; | |||
const var& thisObject; | |||
const var* arguments; | |||
int numArguments; | |||
JUCE_DECLARE_NON_COPYABLE (NativeFunctionArgs) | |||
}; | |||
typedef var (*NativeFunction) (const NativeFunctionArgs&); | |||
typedef Identifier identifier; | |||
//============================================================================== | |||
@@ -70,7 +84,7 @@ public: | |||
var (const String& value); | |||
var (const Array<var>& value); | |||
var (ReferenceCountedObject* object); | |||
var (MethodFunction method) noexcept; | |||
var (NativeFunction method) noexcept; | |||
var (const void* binaryData, size_t dataSize); | |||
var (const MemoryBlock& binaryData); | |||
@@ -84,18 +98,22 @@ public: | |||
var& operator= (const String& value); | |||
var& operator= (const Array<var>& value); | |||
var& operator= (ReferenceCountedObject* object); | |||
var& operator= (MethodFunction method); | |||
var& operator= (NativeFunction method); | |||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
var (var&& other) noexcept; | |||
var (String&& value); | |||
var (MemoryBlock&& binaryData); | |||
var (Array<var>&& value); | |||
var& operator= (var&& other) noexcept; | |||
var& operator= (String&& value); | |||
#endif | |||
void swapWith (var& other) noexcept; | |||
/** Returns a var object that can be used where you need the javascript "undefined" value. */ | |||
static var undefined() noexcept; | |||
//============================================================================== | |||
operator int() const noexcept; | |||
operator int64() const noexcept; | |||
@@ -126,6 +144,7 @@ public: | |||
//============================================================================== | |||
bool isVoid() const noexcept; | |||
bool isUndefined() const noexcept; | |||
bool isInt() const noexcept; | |||
bool isInt64() const noexcept; | |||
bool isBool() const noexcept; | |||
@@ -149,6 +168,15 @@ public: | |||
*/ | |||
bool equalsWithSameType (const var& other) const noexcept; | |||
/** Returns true if this var has the same type as the one supplied. */ | |||
bool hasSameTypeAs (const var& other) const noexcept; | |||
/** Returns a deep copy of this object. | |||
For simple types this just returns a copy, but if the object contains any arrays | |||
or DynamicObjects, they will be cloned (recursively). | |||
*/ | |||
var clone() const noexcept; | |||
//============================================================================== | |||
/** If the var is an array, this returns the number of elements. | |||
If the var isn't actually an array, this will return 0. | |||
@@ -214,27 +242,29 @@ public: | |||
//============================================================================== | |||
/** If this variant is an object, this returns one of its properties. */ | |||
var operator[] (const Identifier propertyName) const; | |||
var operator[] (Identifier propertyName) const; | |||
/** If this variant is an object, this returns one of its properties. */ | |||
var operator[] (const char* propertyName) const; | |||
/** If this variant is an object, this returns one of its properties, or a default | |||
fallback value if the property is not set. */ | |||
var getProperty (const Identifier propertyName, const var& defaultReturnValue) const; | |||
/** If this variant is an object, this invokes one of its methods with no arguments. */ | |||
var call (const Identifier method) const; | |||
/** If this variant is an object, this invokes one of its methods with one argument. */ | |||
var call (const Identifier method, const var& arg1) const; | |||
/** If this variant is an object, this invokes one of its methods with 2 arguments. */ | |||
var call (const Identifier method, const var& arg1, const var& arg2) const; | |||
/** If this variant is an object, this invokes one of its methods with 3 arguments. */ | |||
var call (const Identifier method, const var& arg1, const var& arg2, const var& arg3); | |||
/** If this variant is an object, this invokes one of its methods with 4 arguments. */ | |||
var call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; | |||
/** If this variant is an object, this invokes one of its methods with 5 arguments. */ | |||
var call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const; | |||
/** If this variant is an object, this invokes one of its methods with a list of arguments. */ | |||
var invoke (const Identifier method, const var* arguments, int numArguments) const; | |||
var getProperty (Identifier propertyName, const var& defaultReturnValue) const; | |||
/** Invokes a named method call with no arguments. */ | |||
var call (Identifier method) const; | |||
/** Invokes a named method call with one argument. */ | |||
var call (Identifier method, const var& arg1) const; | |||
/** Invokes a named method call with 2 arguments. */ | |||
var call (Identifier method, const var& arg1, const var& arg2) const; | |||
/** Invokes a named method call with 3 arguments. */ | |||
var call (Identifier method, const var& arg1, const var& arg2, const var& arg3); | |||
/** Invokes a named method call with 4 arguments. */ | |||
var call (Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; | |||
/** Invokes a named method call with 5 arguments. */ | |||
var call (Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const; | |||
/** Invokes a named method call with a list of arguments. */ | |||
var invoke (Identifier method, const var* arguments, int numArguments) const; | |||
/** If this object is a method, this returns the function pointer. */ | |||
NativeFunction getNativeFunction() const; | |||
//============================================================================== | |||
/** Writes a binary representation of this value to a stream. | |||
@@ -252,17 +282,18 @@ public: | |||
private: | |||
//============================================================================== | |||
class VariantType; friend class VariantType; | |||
class VariantType_Void; friend class VariantType_Void; | |||
class VariantType_Int; friend class VariantType_Int; | |||
class VariantType_Int64; friend class VariantType_Int64; | |||
class VariantType_Double; friend class VariantType_Double; | |||
class VariantType_Bool; friend class VariantType_Bool; | |||
class VariantType_String; friend class VariantType_String; | |||
class VariantType_Object; friend class VariantType_Object; | |||
class VariantType_Array; friend class VariantType_Array; | |||
class VariantType_Binary; friend class VariantType_Binary; | |||
class VariantType_Method; friend class VariantType_Method; | |||
class VariantType; friend class VariantType; | |||
class VariantType_Void; friend class VariantType_Void; | |||
class VariantType_Undefined; friend class VariantType_Undefined; | |||
class VariantType_Int; friend class VariantType_Int; | |||
class VariantType_Int64; friend class VariantType_Int64; | |||
class VariantType_Double; friend class VariantType_Double; | |||
class VariantType_Bool; friend class VariantType_Bool; | |||
class VariantType_String; friend class VariantType_String; | |||
class VariantType_Object; friend class VariantType_Object; | |||
class VariantType_Array; friend class VariantType_Array; | |||
class VariantType_Binary; friend class VariantType_Binary; | |||
class VariantType_Method; friend class VariantType_Method; | |||
union ValueUnion | |||
{ | |||
@@ -272,17 +303,15 @@ private: | |||
double doubleValue; | |||
char stringValue [sizeof (String)]; | |||
ReferenceCountedObject* objectValue; | |||
Array<var>* arrayValue; | |||
MemoryBlock* binaryValue; | |||
MethodFunction methodValue; | |||
NativeFunction methodValue; | |||
}; | |||
const VariantType* type; | |||
ValueUnion value; | |||
Array<var>* convertToArray(); | |||
friend class DynamicObject; | |||
var invokeMethod (DynamicObject*, const var*, int) const; | |||
var (const VariantType&) noexcept; | |||
}; | |||
/** Compares the values of two var objects, using the var::equals() comparison. */ | |||
@@ -500,10 +500,9 @@ int File::findChildFiles (Array<File>& results, | |||
const bool searchRecursively, | |||
const String& wildCardPattern) const | |||
{ | |||
DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor); | |||
int total = 0; | |||
while (di.next()) | |||
for (DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor); di.next();) | |||
{ | |||
results.add (di.getFile()); | |||
++total; | |||
@@ -514,10 +513,9 @@ int File::findChildFiles (Array<File>& results, | |||
int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const | |||
{ | |||
DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor); | |||
int total = 0; | |||
while (di.next()) | |||
for (DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor); di.next();) | |||
++total; | |||
return total; | |||
@@ -611,22 +609,18 @@ bool File::hasFileExtension (StringRef possibleSuffix) const | |||
const int semicolon = possibleSuffix.text.indexOf ((juce_wchar) ';'); | |||
if (semicolon >= 0) | |||
{ | |||
return hasFileExtension (String (possibleSuffix.text).substring (0, semicolon).trimEnd()) | |||
|| hasFileExtension ((possibleSuffix.text + (semicolon + 1)).findEndOfWhitespace()); | |||
} | |||
else | |||
if (fullPath.endsWithIgnoreCase (possibleSuffix)) | |||
{ | |||
if (fullPath.endsWithIgnoreCase (possibleSuffix)) | |||
{ | |||
if (possibleSuffix.text[0] == '.') | |||
return true; | |||
if (possibleSuffix.text[0] == '.') | |||
return true; | |||
const int dotPos = fullPath.length() - possibleSuffix.length() - 1; | |||
const int dotPos = fullPath.length() - possibleSuffix.length() - 1; | |||
if (dotPos >= 0) | |||
return fullPath [dotPos] == '.'; | |||
} | |||
if (dotPos >= 0) | |||
return fullPath [dotPos] == '.'; | |||
} | |||
return false; | |||
@@ -353,8 +353,11 @@ public: | |||
*/ | |||
bool isHidden() const; | |||
/** If this file is a link, this returns the file that it points to. | |||
If this file isn't actually link, it'll just return itself. | |||
/** Returns true if this file is a link or alias that can be followed using getLinkedTarget(). */ | |||
bool isLink() const; | |||
/** If this file is a link or alias, this returns the file that it points to. | |||
If the file isn't actually link, it'll just return itself. | |||
*/ | |||
File getLinkedTarget() const; | |||
@@ -43,6 +43,63 @@ public: | |||
return createFail ("Expected '{' or '['", &t); | |||
} | |||
static Result parseString (const juce_wchar quoteChar, String::CharPointerType& t, var& result) | |||
{ | |||
MemoryOutputStream buffer (256); | |||
for (;;) | |||
{ | |||
juce_wchar c = t.getAndAdvance(); | |||
if (c == quoteChar) | |||
break; | |||
if (c == '\\') | |||
{ | |||
c = t.getAndAdvance(); | |||
switch (c) | |||
{ | |||
case '"': | |||
case '\'': | |||
case '\\': | |||
case '/': break; | |||
case 'a': c = '\a'; break; | |||
case 'b': c = '\b'; break; | |||
case 'f': c = '\f'; break; | |||
case 'n': c = '\n'; break; | |||
case 'r': c = '\r'; break; | |||
case 't': c = '\t'; break; | |||
case 'u': | |||
{ | |||
c = 0; | |||
for (int i = 4; --i >= 0;) | |||
{ | |||
const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); | |||
if (digitValue < 0) | |||
return createFail ("Syntax error in unicode escape sequence"); | |||
c = (juce_wchar) ((c << 4) + digitValue); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
if (c == 0) | |||
return createFail ("Unexpected end-of-input in string constant"); | |||
buffer.appendUTF8Char (c); | |||
} | |||
result = buffer.toUTF8(); | |||
return Result::ok(); | |||
} | |||
private: | |||
static Result parseAny (String::CharPointerType& t, var& result) | |||
{ | |||
@@ -53,7 +110,8 @@ private: | |||
{ | |||
case '{': t = t2; return parseObject (t, result); | |||
case '[': t = t2; return parseArray (t, result); | |||
case '"': t = t2; return parseString (t, result); | |||
case '"': t = t2; return parseString ('"', t, result); | |||
case '\'': t = t2; return parseString ('\'', t, result); | |||
case '-': | |||
t2 = t2.findEndOfWhitespace(); | |||
@@ -180,7 +238,7 @@ private: | |||
if (c == '"') | |||
{ | |||
var propertyNameVar; | |||
Result r (parseString (t, propertyNameVar)); | |||
Result r (parseString ('"', t, propertyNameVar)); | |||
if (r.failed()) | |||
return r; | |||
@@ -264,61 +322,6 @@ private: | |||
return Result::ok(); | |||
} | |||
static Result parseString (String::CharPointerType& t, var& result) | |||
{ | |||
MemoryOutputStream buffer (256); | |||
for (;;) | |||
{ | |||
juce_wchar c = t.getAndAdvance(); | |||
if (c == '"') | |||
break; | |||
if (c == '\\') | |||
{ | |||
c = t.getAndAdvance(); | |||
switch (c) | |||
{ | |||
case '"': | |||
case '\\': | |||
case '/': break; | |||
case 'b': c = '\b'; break; | |||
case 'f': c = '\f'; break; | |||
case 'n': c = '\n'; break; | |||
case 'r': c = '\r'; break; | |||
case 't': c = '\t'; break; | |||
case 'u': | |||
{ | |||
c = 0; | |||
for (int i = 4; --i >= 0;) | |||
{ | |||
const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); | |||
if (digitValue < 0) | |||
return createFail ("Syntax error in unicode escape sequence"); | |||
c = (juce_wchar) ((c << 4) + digitValue); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
if (c == 0) | |||
return createFail ("Unexpected end-of-input in string constant"); | |||
buffer.appendUTF8Char (c); | |||
} | |||
result = buffer.toUTF8(); | |||
return Result::ok(); | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -338,6 +341,10 @@ public: | |||
{ | |||
out << "null"; | |||
} | |||
else if (v.isUndefined()) | |||
{ | |||
out << "undefined"; | |||
} | |||
else if (v.isBool()) | |||
{ | |||
out << (static_cast<bool> (v) ? "true" : "false"); | |||
@@ -348,8 +355,8 @@ public: | |||
} | |||
else if (v.isObject()) | |||
{ | |||
if (DynamicObject* const object = v.getDynamicObject()) | |||
writeObject (out, *object, indentLevel, allOnOneLine); | |||
if (DynamicObject* object = v.getDynamicObject()) | |||
object->writeAsJSON (out, indentLevel, allOnOneLine); | |||
else | |||
jassertfalse; // Only DynamicObjects can be converted to JSON! | |||
} | |||
@@ -379,6 +386,7 @@ public: | |||
case '\"': out << "\\\""; break; | |||
case '\\': out << "\\\\"; break; | |||
case '\a': out << "\\a"; break; | |||
case '\b': out << "\\b"; break; | |||
case '\f': out << "\\f"; break; | |||
case '\t': out << "\\t"; break; | |||
@@ -421,34 +429,38 @@ public: | |||
const int indentLevel, const bool allOnOneLine) | |||
{ | |||
out << '['; | |||
if (! allOnOneLine) | |||
out << newLine; | |||
for (int i = 0; i < array.size(); ++i) | |||
if (array.size() > 0) | |||
{ | |||
if (! allOnOneLine) | |||
writeSpaces (out, indentLevel + indentSize); | |||
write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine); | |||
out << newLine; | |||
if (i < array.size() - 1) | |||
for (int i = 0; i < array.size(); ++i) | |||
{ | |||
if (allOnOneLine) | |||
out << ", "; | |||
else | |||
out << ',' << newLine; | |||
if (! allOnOneLine) | |||
writeSpaces (out, indentLevel + indentSize); | |||
write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine); | |||
if (i < array.size() - 1) | |||
{ | |||
if (allOnOneLine) | |||
out << ", "; | |||
else | |||
out << ',' << newLine; | |||
} | |||
else if (! allOnOneLine) | |||
out << newLine; | |||
} | |||
else if (! allOnOneLine) | |||
out << newLine; | |||
} | |||
if (! allOnOneLine) | |||
writeSpaces (out, indentLevel); | |||
if (! allOnOneLine) | |||
writeSpaces (out, indentLevel); | |||
} | |||
out << ']'; | |||
} | |||
static void writeObject (OutputStream& out, DynamicObject& object, | |||
/* static void writeObject (OutputStream& out, DynamicObject& object, | |||
const int indentLevel, const bool allOnOneLine) | |||
{ | |||
NamedValueSet& props = object.getProperties(); | |||
@@ -491,7 +503,7 @@ public: | |||
writeSpaces (out, indentLevel); | |||
out << '}'; | |||
} | |||
}*/ | |||
enum { indentSize = 2 }; | |||
}; | |||
@@ -541,6 +553,15 @@ String JSON::escapeString (StringRef s) | |||
return mo.toString(); | |||
} | |||
Result JSON::parseQuotedString (String::CharPointerType& t, var& result) | |||
{ | |||
const juce_wchar quote = t.getAndAdvance(); | |||
if (quote == '"' || quote == '\'') | |||
return JSONParser::parseString (quote, t, result); | |||
return Result::fail ("Not a quoted string!"); | |||
} | |||
//============================================================================== | |||
//============================================================================== |
@@ -106,9 +106,15 @@ public: | |||
/** Returns a version of a string with any extended characters escaped. */ | |||
static String escapeString (StringRef); | |||
/** Parses a quoted string in JSON format, returning the un-escaped result in the | |||
result parameter, and an error message in case the content was illegal. | |||
This advances the text parameter, leaving it positioned after the closing quote. | |||
*/ | |||
static Result parseQuotedString (String::CharPointerType& text, var& result); | |||
private: | |||
//============================================================================== | |||
JSON(); // This class can't be instantiated - just use its static methods. | |||
JSON() JUCE_DELETED_FUNCTION; // This class can't be instantiated - just use its static methods. | |||
}; | |||
@@ -0,0 +1,106 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the juce_core module of the JUCE library. | |||
Copyright (c) 2013 - Raw Material Software Ltd. | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
or without fee is hereby granted, provided that the above copyright notice and this | |||
permission notice appear in all copies. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
------------------------------------------------------------------------------ | |||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
using any other modules, be sure to check that you also comply with their license. | |||
For more details, visit www.juce.com | |||
============================================================================== | |||
*/ | |||
/** | |||
A simple javascript interpreter! | |||
It's not fully standards-compliant, and won't be as fast as the fancy JIT-compiled | |||
engines that you get in browsers, but this is an extremely compact, low-overhead javascript | |||
interpreter, which is integrated with the juce var and DynamicObject classes. If you need | |||
a few simple bits of scripting in your app, and want to be able to easily let the JS | |||
work with native objects defined as DynamicObject subclasses, then this might do the job. | |||
To use, simply create an instance of this class and call execute() to run your code. | |||
Variables that the script sets can be retrieved with evaluate(), and if you need to provide | |||
native objects for the script to use, you can add them with registerNativeObject(). | |||
One caveat: Because the values and objects that the engine works with are DynamicObject | |||
and var objects, they use reference-counting rather than garbage-collection, so if your | |||
script creates complex connections between objects, you run the risk of creating cyclic | |||
dependencies and hence leaking. | |||
*/ | |||
class JavascriptEngine | |||
{ | |||
public: | |||
/** Creates an instance of the engine. | |||
This creates a root namespace and defines some basic Object, String, Array | |||
and Math library methods. | |||
*/ | |||
JavascriptEngine(); | |||
/** Destructor. */ | |||
~JavascriptEngine(); | |||
/** Attempts to parse and run a block of javascript code. | |||
If there's a parse or execution error, the error description is returned in | |||
the result. | |||
You can specify a maximum time for which the program is allowed to run, and | |||
it'll return with an error message if this time is exceeded. | |||
*/ | |||
Result execute (const String& javascriptCode); | |||
/** Attempts to parse and run a javascript expression, and returns the result. | |||
If there's a syntax error, or the expression can't be evaluated, the return value | |||
will be var::undefined(). The errorMessage parameter gives you a way to find out | |||
any parsing errors. | |||
You can specify a maximum time for which the program is allowed to run, and | |||
it'll return with an error message if this time is exceeded. | |||
*/ | |||
var evaluate (const String& javascriptCode, | |||
Result* errorMessage = nullptr); | |||
/** Calls a function in the root namespace, and returns the result. | |||
The function arguments are passed in the same format as used by native | |||
methods in the var class. | |||
*/ | |||
var callFunction (Identifier function, | |||
const var::NativeFunctionArgs& args, | |||
Result* errorMessage = nullptr); | |||
/** Adds a native object to the root namespace. | |||
The object passed-in is reference-counted, and will be retained by the | |||
engine until the engine is deleted. The name must be a simple JS identifier, | |||
without any dots. | |||
*/ | |||
void registerNativeObject (Identifier objectName, DynamicObject* object); | |||
/** This value indicates how long a call to one of the evaluate methods is permitted | |||
to run before timing-out and failing. | |||
The default value is a number of seconds, but you can change this to whatever value | |||
suits your application. | |||
*/ | |||
RelativeTime maximumExecutionTime; | |||
private: | |||
struct RootObject; | |||
ReferenceCountedObjectPtr<RootObject> root; | |||
void prepareTimeout() const; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine) | |||
}; |
@@ -110,7 +110,6 @@ namespace juce | |||
{ | |||
#include "containers/juce_AbstractFifo.cpp" | |||
#include "containers/juce_DynamicObject.cpp" | |||
#include "containers/juce_NamedValueSet.cpp" | |||
#include "containers/juce_PropertySet.cpp" | |||
#include "containers/juce_Variant.cpp" | |||
@@ -120,7 +119,9 @@ namespace juce | |||
#include "files/juce_FileOutputStream.cpp" | |||
#include "files/juce_FileSearchPath.cpp" | |||
#include "files/juce_TemporaryFile.cpp" | |||
#include "json/juce_JSON.cpp" | |||
#include "javascript/juce_JSON.cpp" | |||
#include "javascript/juce_Javascript.cpp" | |||
#include "containers/juce_DynamicObject.cpp" | |||
#include "logging/juce_FileLogger.cpp" | |||
#include "logging/juce_Logger.cpp" | |||
#include "maths/juce_BigInteger.cpp" | |||
@@ -139,13 +140,13 @@ namespace juce | |||
#include "streams/juce_InputStream.cpp" | |||
#include "streams/juce_MemoryInputStream.cpp" | |||
#include "streams/juce_MemoryOutputStream.cpp" | |||
#include "streams/juce_OutputStream.cpp" | |||
#include "streams/juce_SubregionStream.cpp" | |||
#include "system/juce_SystemStats.cpp" | |||
#include "text/juce_CharacterFunctions.cpp" | |||
#include "text/juce_Identifier.cpp" | |||
#include "text/juce_LocalisedStrings.cpp" | |||
#include "text/juce_String.cpp" | |||
#include "streams/juce_OutputStream.cpp" | |||
#include "text/juce_StringArray.cpp" | |||
#include "text/juce_StringPairArray.cpp" | |||
#include "text/juce_StringPool.cpp" | |||
@@ -198,6 +199,7 @@ namespace juce | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
#include "native/juce_linux_CommonFile.cpp" | |||
#include "native/juce_linux_Files.cpp" | |||
#include "native/juce_linux_Network.cpp" | |||
#include "native/juce_linux_SystemStats.cpp" | |||
@@ -205,6 +207,7 @@ namespace juce | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#include "native/juce_linux_CommonFile.cpp" | |||
#include "native/juce_android_Files.cpp" | |||
#include "native/juce_android_Misc.cpp" | |||
#include "native/juce_android_Network.cpp" | |||
@@ -234,7 +234,8 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe | |||
#include "files/juce_TemporaryFile.h" | |||
#include "streams/juce_FileInputSource.h" | |||
#include "logging/juce_FileLogger.h" | |||
#include "json/juce_JSON.h" | |||
#include "javascript/juce_JSON.h" | |||
#include "javascript/juce_Javascript.h" | |||
#include "maths/juce_BigInteger.h" | |||
#include "maths/juce_Expression.h" | |||
#include "maths/juce_Random.h" | |||
@@ -1,7 +1,7 @@ | |||
{ | |||
"id": "juce_core", | |||
"name": "JUCE core classes", | |||
"version": "2.1.3", | |||
"version": "2.1.6", | |||
"description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "ISC Permissive", | |||
@@ -25,7 +25,7 @@ | |||
"logging/*", | |||
"system/*", | |||
"xml/*", | |||
"json/*", | |||
"javascript/*", | |||
"zip/*", | |||
"unit_tests/*", | |||
"misc/*", | |||
@@ -157,6 +157,12 @@ int BigInteger::toInteger() const noexcept | |||
return negative ? -n : n; | |||
} | |||
int64 BigInteger::toInt64() const noexcept | |||
{ | |||
const int64 n = (((int64) (values[1] & 0x7fffffff)) << 32) | values[0]; | |||
return negative ? -n : n; | |||
} | |||
BigInteger BigInteger::getBitRange (int startBit, int numBits) const | |||
{ | |||
BigInteger r; | |||
@@ -94,11 +94,16 @@ public: | |||
/** Returns true if the value is 1. */ | |||
bool isOne() const noexcept; | |||
/** Attempts to get the lowest bits of the value as an integer. | |||
/** Attempts to get the lowest 32 bits of the value as an integer. | |||
If the value is bigger than the integer limits, this will return only the lower bits. | |||
*/ | |||
int toInteger() const noexcept; | |||
/** Attempts to get the lowest 64 bits of the value as an integer. | |||
If the value is bigger than the integer limits, this will return only the lower bits. | |||
*/ | |||
int64 toInt64() const noexcept; | |||
//============================================================================== | |||
/** Resets the value to 0. */ | |||
void clear(); | |||
@@ -295,6 +295,12 @@ inline int64 abs64 (const int64 n) noexcept | |||
return (n >= 0) ? n : -n; | |||
} | |||
#if JUCE_MSVC && ! defined (DOXYGEN) // The MSVC libraries omit these functions for some reason... | |||
template<typename Type> Type asinh (Type x) noexcept { return std::log (x + std::sqrt (x * x + (Type) 1)); } | |||
template<typename Type> Type acosh (Type x) noexcept { return std::log (x + std::sqrt (x * x - (Type) 1)); } | |||
template<typename Type> Type atanh (Type x) noexcept { return (std::log (x + (Type) 1) - std::log (((Type) 1) - x)) / (Type) 2; } | |||
#endif | |||
//============================================================================== | |||
/** A predefined value for Pi, at double-precision. | |||
@see float_Pi | |||
@@ -81,6 +81,11 @@ int Random::nextInt (const int maxValue) noexcept | |||
return (int) ((((unsigned int) nextInt()) * (uint64) maxValue) >> 32); | |||
} | |||
int Random::nextInt (Range<int> range) noexcept | |||
{ | |||
return range.getStart() + nextInt (range.getLength()); | |||
} | |||
int64 Random::nextInt64() noexcept | |||
{ | |||
return (((int64) nextInt()) << 32) | (int64) (uint64) (uint32) nextInt(); | |||
@@ -93,12 +98,12 @@ bool Random::nextBool() noexcept | |||
float Random::nextFloat() noexcept | |||
{ | |||
return static_cast <uint32> (nextInt()) / (float) 0xffffffff; | |||
return static_cast<uint32> (nextInt()) / (std::numeric_limits<uint32>::max() + 1.0f); | |||
} | |||
double Random::nextDouble() noexcept | |||
{ | |||
return static_cast <uint32> (nextInt()) / (double) 0xffffffff; | |||
return static_cast<uint32> (nextInt()) / (std::numeric_limits<uint32>::max() + 1.0); | |||
} | |||
BigInteger Random::nextLargeNumber (const BigInteger& maximumValue) | |||
@@ -69,6 +69,11 @@ public: | |||
*/ | |||
int nextInt (int maxValue) noexcept; | |||
/** Returns the next random number, limited to a given range. | |||
@returns a random integer between the range start (inclusive) and its end (exclusive). | |||
*/ | |||
int nextInt (Range<int> range) noexcept; | |||
/** Returns the next 64-bit random number. | |||
@returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff | |||
*/ | |||
@@ -265,7 +265,7 @@ void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcep | |||
String MemoryBlock::toString() const | |||
{ | |||
return String (CharPointer_UTF8 (data), size); | |||
return String::fromUTF8 (data, size); | |||
} | |||
//============================================================================== | |||
@@ -352,7 +352,7 @@ void MemoryBlock::loadFromHexString (StringRef hex) | |||
} | |||
//============================================================================== | |||
static const char* const base64EncodingTable = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; | |||
static const char base64EncodingTable[] = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; | |||
String MemoryBlock::toBase64Encoding() const | |||
{ | |||
@@ -373,9 +373,16 @@ String MemoryBlock::toBase64Encoding() const | |||
return destString; | |||
} | |||
static const char base64DecodingTable[] = | |||
{ | |||
63, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 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, 26, | |||
0, 0, 0, 0, 0, 0, 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, 52 | |||
}; | |||
bool MemoryBlock::fromBase64Encoding (StringRef s) | |||
{ | |||
String::CharPointerType dot (CharacterFunctions::find (s.text, CharPointer_ASCII ("."))); | |||
String::CharPointerType dot (CharacterFunctions::find (s.text, (juce_wchar) '.')); | |||
if (dot.isEmpty()) | |||
return false; | |||
@@ -389,19 +396,16 @@ bool MemoryBlock::fromBase64Encoding (StringRef s) | |||
for (;;) | |||
{ | |||
const char c = (char) srcChars.getAndAdvance(); | |||
int c = (int) srcChars.getAndAdvance(); | |||
if (c == 0) | |||
return true; | |||
for (int j = 0; j < 64; ++j) | |||
c -= 43; | |||
if (isPositiveAndBelow (c, numElementsInArray (base64DecodingTable))) | |||
{ | |||
if (base64EncodingTable[j] == c) | |||
{ | |||
setBitRange ((size_t) pos, 6, j); | |||
pos += 6; | |||
break; | |||
} | |||
setBitRange ((size_t) pos, 6, base64DecodingTable [c]); | |||
pos += 6; | |||
} | |||
} | |||
} |
@@ -33,6 +33,10 @@ Uuid::Uuid() | |||
for (size_t i = 0; i < sizeof (uuid); ++i) | |||
uuid[i] = (uint8) (r.nextInt (256)); | |||
// To make it RFC 4122 compliant, need to force a few bits... | |||
uuid[6] = (uuid[6] & 0x0f) | 0x40; | |||
uuid[8] = (uuid[8] & 0x3f) | 0x80; | |||
} | |||
Uuid::~Uuid() noexcept {} | |||
@@ -65,9 +69,23 @@ bool Uuid::isNull() const noexcept | |||
return true; | |||
} | |||
String Uuid::getHexRegion (int start, int length) const | |||
{ | |||
return String::toHexString (uuid + start, length, 0); | |||
} | |||
String Uuid::toString() const | |||
{ | |||
return String::toHexString (uuid, sizeof (uuid), 0); | |||
return getHexRegion (0, 16); | |||
} | |||
String Uuid::toDashedString() const | |||
{ | |||
return getHexRegion (0, 4) | |||
+ "-" + getHexRegion (4, 2) | |||
+ "-" + getHexRegion (6, 2) | |||
+ "-" + getHexRegion (8, 2) | |||
+ "-" + getHexRegion (10, 6); | |||
} | |||
Uuid::Uuid (const String& uuidString) | |||
@@ -34,9 +34,9 @@ | |||
/** | |||
A universally unique 128-bit identifier. | |||
This class generates very random unique numbers based on the system time | |||
and MAC addresses if any are available. It's extremely unlikely that two identical | |||
UUIDs would ever be created by chance. | |||
This class generates very random unique numbers. It's vanishingly unlikely | |||
that two identical UUIDs would ever be created by chance. The values are | |||
formatted to meet the RFC 4122 version 4 standard. | |||
The class includes methods for saving the ID as a string or as raw binary data. | |||
*/ | |||
@@ -44,7 +44,7 @@ class JUCE_API Uuid | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a new unique ID. */ | |||
/** Creates a new unique ID, compliant with RFC 4122 version 4. */ | |||
Uuid(); | |||
/** Destructor. */ | |||
@@ -76,6 +76,11 @@ public: | |||
*/ | |||
String toString() const; | |||
/** Returns a stringified version of this UUID, separating it into sections with dashes. | |||
@returns a string in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | |||
*/ | |||
String toDashedString() const; | |||
/** Creates an ID from an encoded string version. | |||
@see toString | |||
*/ | |||
@@ -107,6 +112,7 @@ public: | |||
private: | |||
//============================================================================== | |||
uint8 uuid[16]; | |||
String getHexRegion (int, int) const; | |||
JUCE_LEAK_DETECTOR (Uuid) | |||
}; | |||
@@ -142,9 +142,9 @@ public final class JuceAppActivity extends Activity | |||
//============================================================================== | |||
private ViewHolder viewHolder; | |||
public final ComponentPeerView createNewView (boolean opaque) | |||
public final ComponentPeerView createNewView (boolean opaque, long host) | |||
{ | |||
ComponentPeerView v = new ComponentPeerView (this, opaque); | |||
ComponentPeerView v = new ComponentPeerView (this, opaque, host); | |||
viewHolder.addView (v); | |||
return v; | |||
} | |||
@@ -312,9 +312,10 @@ public final class JuceAppActivity extends Activity | |||
public final class ComponentPeerView extends ViewGroup | |||
implements View.OnFocusChangeListener | |||
{ | |||
public ComponentPeerView (Context context, boolean opaque_) | |||
public ComponentPeerView (Context context, boolean opaque_, long host) | |||
{ | |||
super (context); | |||
this.host = host; | |||
setWillNotDraw (false); | |||
opaque = opaque_; | |||
@@ -325,13 +326,12 @@ public final class JuceAppActivity extends Activity | |||
} | |||
//============================================================================== | |||
private native void handlePaint (Canvas canvas); | |||
private native void handlePaint (long host, Canvas canvas); | |||
@Override | |||
public void draw (Canvas canvas) | |||
public void onDraw (Canvas canvas) | |||
{ | |||
super.draw (canvas); | |||
handlePaint (canvas); | |||
handlePaint (host, canvas); | |||
} | |||
@Override | |||
@@ -341,11 +341,12 @@ public final class JuceAppActivity extends Activity | |||
} | |||
private boolean opaque; | |||
private long host; | |||
//============================================================================== | |||
private native void handleMouseDown (int index, float x, float y, long time); | |||
private native void handleMouseDrag (int index, float x, float y, long time); | |||
private native void handleMouseUp (int index, float x, float y, long time); | |||
private native void handleMouseDown (long host, int index, float x, float y, long time); | |||
private native void handleMouseDrag (long host, int index, float x, float y, long time); | |||
private native void handleMouseUp (long host, int index, float x, float y, long time); | |||
@Override | |||
public boolean onTouchEvent (MotionEvent event) | |||
@@ -356,19 +357,19 @@ public final class JuceAppActivity extends Activity | |||
switch (action & MotionEvent.ACTION_MASK) | |||
{ | |||
case MotionEvent.ACTION_DOWN: | |||
handleMouseDown (event.getPointerId(0), event.getX(), event.getY(), time); | |||
handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time); | |||
return true; | |||
case MotionEvent.ACTION_CANCEL: | |||
case MotionEvent.ACTION_UP: | |||
handleMouseUp (event.getPointerId(0), event.getX(), event.getY(), time); | |||
handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time); | |||
return true; | |||
case MotionEvent.ACTION_MOVE: | |||
{ | |||
int n = event.getPointerCount(); | |||
for (int i = 0; i < n; ++i) | |||
handleMouseDrag (event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
return true; | |||
} | |||
@@ -376,14 +377,14 @@ public final class JuceAppActivity extends Activity | |||
case MotionEvent.ACTION_POINTER_UP: | |||
{ | |||
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; | |||
handleMouseUp (event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
return true; | |||
} | |||
case MotionEvent.ACTION_POINTER_DOWN: | |||
{ | |||
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; | |||
handleMouseDown (event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
return true; | |||
} | |||
@@ -395,8 +396,8 @@ public final class JuceAppActivity extends Activity | |||
} | |||
//============================================================================== | |||
private native void handleKeyDown (int keycode, int textchar); | |||
private native void handleKeyUp (int keycode, int textchar); | |||
private native void handleKeyDown (long host, int keycode, int textchar); | |||
private native void handleKeyUp (long host, int keycode, int textchar); | |||
public void showKeyboard (boolean shouldShow) | |||
{ | |||
@@ -414,14 +415,23 @@ public final class JuceAppActivity extends Activity | |||
@Override | |||
public boolean onKeyDown (int keyCode, KeyEvent event) | |||
{ | |||
handleKeyDown (keyCode, event.getUnicodeChar()); | |||
switch (keyCode) | |||
{ | |||
case KeyEvent.KEYCODE_VOLUME_UP: | |||
case KeyEvent.KEYCODE_VOLUME_DOWN: | |||
return super.onKeyDown (keyCode, event); | |||
default: break; | |||
} | |||
handleKeyDown (host, keyCode, event.getUnicodeChar()); | |||
return true; | |||
} | |||
@Override | |||
public boolean onKeyUp (int keyCode, KeyEvent event) | |||
{ | |||
handleKeyUp (keyCode, event.getUnicodeChar()); | |||
handleKeyUp (host, keyCode, event.getUnicodeChar()); | |||
return true; | |||
} | |||
@@ -445,7 +455,7 @@ public final class JuceAppActivity extends Activity | |||
protected void onSizeChanged (int w, int h, int oldw, int oldh) | |||
{ | |||
super.onSizeChanged (w, h, oldw, oldh); | |||
viewSizeChanged(); | |||
viewSizeChanged (host); | |||
} | |||
@Override | |||
@@ -455,16 +465,16 @@ public final class JuceAppActivity extends Activity | |||
requestTransparentRegion (getChildAt (i)); | |||
} | |||
private native void viewSizeChanged(); | |||
private native void viewSizeChanged (long host); | |||
@Override | |||
public void onFocusChange (View v, boolean hasFocus) | |||
{ | |||
if (v == this) | |||
focusChanged (hasFocus); | |||
focusChanged (host, hasFocus); | |||
} | |||
private native void focusChanged (boolean hasFocus); | |||
private native void focusChanged (long host, boolean hasFocus); | |||
public void setViewName (String newName) {} | |||
@@ -94,7 +94,7 @@ | |||
#define _UNICODE 1 | |||
#define UNICODE 1 | |||
#ifndef _WIN32_IE | |||
#define _WIN32_IE 0x0400 | |||
#define _WIN32_IE 0x0500 | |||
#endif | |||
#include <windows.h> | |||
@@ -26,34 +26,6 @@ | |||
============================================================================== | |||
*/ | |||
bool File::copyInternal (const File& dest) const | |||
{ | |||
FileInputStream in (*this); | |||
if (dest.deleteFile()) | |||
{ | |||
{ | |||
FileOutputStream out (dest); | |||
if (out.failedToOpen()) | |||
return false; | |||
if (out.writeFromInputStream (in, -1) == getSize()) | |||
return true; | |||
} | |||
dest.deleteFile(); | |||
} | |||
return false; | |||
} | |||
void File::findFileSystemRoots (Array<File>& destArray) | |||
{ | |||
destArray.add (File ("/")); | |||
} | |||
//============================================================================== | |||
bool File::isOnCDRomDrive() const | |||
{ | |||
return false; | |||
@@ -69,35 +41,11 @@ bool File::isOnRemovableDrive() const | |||
return false; | |||
} | |||
bool File::isHidden() const | |||
{ | |||
return getFileName().startsWithChar ('.'); | |||
} | |||
//============================================================================== | |||
namespace | |||
{ | |||
File juce_readlink (const String& file, const File& defaultFile) | |||
{ | |||
const int size = 8192; | |||
HeapBlock<char> buffer; | |||
buffer.malloc (size + 4); | |||
const size_t numBytes = readlink (file.toUTF8(), buffer, size); | |||
if (numBytes > 0 && numBytes <= size) | |||
return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); | |||
return defaultFile; | |||
} | |||
} | |||
File File::getLinkedTarget() const | |||
String File::getVersion() const | |||
{ | |||
return juce_readlink (getFullPathName().toUTF8(), *this); | |||
return String::empty; | |||
} | |||
//============================================================================== | |||
File File::getSpecialLocation (const SpecialLocationType type) | |||
{ | |||
switch (type) | |||
@@ -135,101 +83,15 @@ File File::getSpecialLocation (const SpecialLocationType type) | |||
return File::nonexistent; | |||
} | |||
//============================================================================== | |||
String File::getVersion() const | |||
{ | |||
return String::empty; | |||
} | |||
//============================================================================== | |||
bool File::moveToTrash() const | |||
{ | |||
if (! exists()) | |||
return true; | |||
// TODO | |||
return false; | |||
} | |||
//============================================================================== | |||
class DirectoryIterator::NativeIterator::Pimpl | |||
{ | |||
public: | |||
Pimpl (const File& directory, const String& wildCard_) | |||
: parentDir (File::addTrailingSeparator (directory.getFullPathName())), | |||
wildCard (wildCard_), | |||
dir (opendir (directory.getFullPathName().toUTF8())) | |||
{ | |||
} | |||
~Pimpl() | |||
{ | |||
if (dir != 0) | |||
closedir (dir); | |||
} | |||
bool next (String& filenameFound, | |||
bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
{ | |||
if (dir != 0) | |||
{ | |||
const char* wildcardUTF8 = nullptr; | |||
for (;;) | |||
{ | |||
struct dirent* const de = readdir (dir); | |||
if (de == nullptr) | |||
break; | |||
if (wildcardUTF8 == nullptr) | |||
wildcardUTF8 = wildCard.toUTF8(); | |||
if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) | |||
{ | |||
filenameFound = CharPointer_UTF8 (de->d_name); | |||
updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); | |||
if (isHidden != 0) | |||
*isHidden = filenameFound.startsWithChar ('.'); | |||
return true; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
private: | |||
String parentDir, wildCard; | |||
DIR* dir; | |||
JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
}; | |||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) | |||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) | |||
{ | |||
} | |||
DirectoryIterator::NativeIterator::~NativeIterator() | |||
{ | |||
} | |||
bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
{ | |||
return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
} | |||
//============================================================================== | |||
JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) | |||
{ | |||
const LocalRef<jstring> t (javaString (fileName)); | |||
@@ -349,7 +349,7 @@ extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (createNewView, "createNewView", "(Z)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ | |||
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ | |||
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ | |||
METHOD (postMessage, "postMessage", "(J)V") \ | |||
METHOD (finish, "finish", "()V") \ | |||
@@ -0,0 +1,153 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the juce_core module of the JUCE library. | |||
Copyright (c) 2013 - Raw Material Software Ltd. | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
or without fee is hereby granted, provided that the above copyright notice and this | |||
permission notice appear in all copies. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
------------------------------------------------------------------------------ | |||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
using any other modules, be sure to check that you also comply with their license. | |||
For more details, visit www.juce.com | |||
============================================================================== | |||
*/ | |||
bool File::copyInternal (const File& dest) const | |||
{ | |||
FileInputStream in (*this); | |||
if (dest.deleteFile()) | |||
{ | |||
{ | |||
FileOutputStream out (dest); | |||
if (out.failedToOpen()) | |||
return false; | |||
if (out.writeFromInputStream (in, -1) == getSize()) | |||
return true; | |||
} | |||
dest.deleteFile(); | |||
} | |||
return false; | |||
} | |||
void File::findFileSystemRoots (Array<File>& destArray) | |||
{ | |||
destArray.add (File ("/")); | |||
} | |||
bool File::isHidden() const | |||
{ | |||
return getFileName().startsWithChar ('.'); | |||
} | |||
static String getLinkedFile (StringRef file) | |||
{ | |||
HeapBlock<char> buffer (8194); | |||
const int numBytes = (int) readlink (file.text, buffer, 8192); | |||
return String::fromUTF8 (buffer, jmax (0, numBytes)); | |||
}; | |||
bool File::isLink() const | |||
{ | |||
return getLinkedFile (getFullPathName()).isNotEmpty(); | |||
} | |||
File File::getLinkedTarget() const | |||
{ | |||
String f (getLinkedFile (getFullPathName())); | |||
if (f.isNotEmpty()) | |||
return getSiblingFile (f); | |||
return *this; | |||
} | |||
//============================================================================== | |||
class DirectoryIterator::NativeIterator::Pimpl | |||
{ | |||
public: | |||
Pimpl (const File& directory, const String& wc) | |||
: parentDir (File::addTrailingSeparator (directory.getFullPathName())), | |||
wildCard (wc), dir (opendir (directory.getFullPathName().toUTF8())) | |||
{ | |||
} | |||
~Pimpl() | |||
{ | |||
if (dir != nullptr) | |||
closedir (dir); | |||
} | |||
bool next (String& filenameFound, | |||
bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
{ | |||
if (dir != nullptr) | |||
{ | |||
const char* wildcardUTF8 = nullptr; | |||
for (;;) | |||
{ | |||
struct dirent* const de = readdir (dir); | |||
if (de == nullptr) | |||
break; | |||
if (wildcardUTF8 == nullptr) | |||
wildcardUTF8 = wildCard.toUTF8(); | |||
if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) | |||
{ | |||
filenameFound = CharPointer_UTF8 (de->d_name); | |||
updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); | |||
if (isHidden != nullptr) | |||
*isHidden = filenameFound.startsWithChar ('.'); | |||
return true; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
private: | |||
String parentDir, wildCard; | |||
DIR* dir; | |||
JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
}; | |||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) | |||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) | |||
{ | |||
} | |||
DirectoryIterator::NativeIterator::~NativeIterator() {} | |||
bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
bool* isDir, bool* isHidden, int64* fileSize, | |||
Time* modTime, Time* creationTime, bool* isReadOnly) | |||
{ | |||
return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
} |
@@ -34,35 +34,6 @@ enum | |||
U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h | |||
}; | |||
//============================================================================== | |||
bool File::copyInternal (const File& dest) const | |||
{ | |||
FileInputStream in (*this); | |||
if (dest.deleteFile()) | |||
{ | |||
{ | |||
FileOutputStream out (dest); | |||
if (out.failedToOpen()) | |||
return false; | |||
if (out.writeFromInputStream (in, -1) == getSize()) | |||
return true; | |||
} | |||
dest.deleteFile(); | |||
} | |||
return false; | |||
} | |||
void File::findFileSystemRoots (Array<File>& destArray) | |||
{ | |||
destArray.add (File ("/")); | |||
} | |||
//============================================================================== | |||
bool File::isOnCDRomDrive() const | |||
{ | |||
struct statfs buf; | |||
@@ -85,11 +56,7 @@ bool File::isOnHardDisk() const | |||
case U_SMB_SUPER_MAGIC: // Network Samba | |||
return false; | |||
default: | |||
// Assume anything else is a hard-disk (but note it could | |||
// be a RAM disk. There isn't a good way of determining | |||
// this for sure) | |||
return true; | |||
default: break; | |||
} | |||
} | |||
@@ -103,32 +70,9 @@ bool File::isOnRemovableDrive() const | |||
return false; | |||
} | |||
bool File::isHidden() const | |||
{ | |||
return getFileName().startsWithChar ('.'); | |||
} | |||
//============================================================================== | |||
namespace | |||
{ | |||
File juce_readlink (const String& file, const File& defaultFile) | |||
{ | |||
const size_t size = 8192; | |||
HeapBlock<char> buffer; | |||
buffer.malloc (size + 4); | |||
const size_t numBytes = readlink (file.toUTF8(), buffer, size); | |||
if (numBytes > 0 && numBytes <= size) | |||
return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); | |||
return defaultFile; | |||
} | |||
} | |||
File File::getLinkedTarget() const | |||
String File::getVersion() const | |||
{ | |||
return juce_readlink (getFullPathName().toUTF8(), *this); | |||
return String::empty; // xxx not yet implemented | |||
} | |||
//============================================================================== | |||
@@ -209,7 +153,10 @@ File File::getSpecialLocation (const SpecialLocationType type) | |||
return juce_getExecutableFile(); | |||
case hostApplicationPath: | |||
return juce_readlink ("/proc/self/exe", juce_getExecutableFile()); | |||
{ | |||
const File f ("/proc/self/exe"); | |||
return f.isLink() ? f.getLinkedTarget() : juce_getExecutableFile(); | |||
} | |||
default: | |||
jassertfalse; // unknown type? | |||
@@ -219,12 +166,6 @@ File File::getSpecialLocation (const SpecialLocationType type) | |||
return File::nonexistent; | |||
} | |||
//============================================================================== | |||
String File::getVersion() const | |||
{ | |||
return String::empty; // xxx not yet implemented | |||
} | |||
//============================================================================== | |||
bool File::moveToTrash() const | |||
{ | |||
@@ -243,81 +184,6 @@ bool File::moveToTrash() const | |||
getFileExtension())); | |||
} | |||
//============================================================================== | |||
class DirectoryIterator::NativeIterator::Pimpl | |||
{ | |||
public: | |||
Pimpl (const File& directory, const String& wc) | |||
: parentDir (File::addTrailingSeparator (directory.getFullPathName())), | |||
wildCard (wc), dir (opendir (directory.getFullPathName().toUTF8())) | |||
{ | |||
} | |||
~Pimpl() | |||
{ | |||
if (dir != nullptr) | |||
closedir (dir); | |||
} | |||
bool next (String& filenameFound, | |||
bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
{ | |||
if (dir != nullptr) | |||
{ | |||
const char* wildcardUTF8 = nullptr; | |||
for (;;) | |||
{ | |||
struct dirent* const de = readdir (dir); | |||
if (de == nullptr) | |||
break; | |||
if (wildcardUTF8 == nullptr) | |||
wildcardUTF8 = wildCard.toUTF8(); | |||
if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) | |||
{ | |||
filenameFound = CharPointer_UTF8 (de->d_name); | |||
updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); | |||
if (isHidden != nullptr) | |||
*isHidden = filenameFound.startsWithChar ('.'); | |||
return true; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
private: | |||
String parentDir, wildCard; | |||
DIR* dir; | |||
JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
}; | |||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) | |||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) | |||
{ | |||
} | |||
DirectoryIterator::NativeIterator::~NativeIterator() | |||
{ | |||
} | |||
bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
{ | |||
return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
} | |||
//============================================================================== | |||
static bool isFileExecutable (const String& filename) | |||
{ | |||
@@ -340,8 +206,8 @@ bool Process::openDocument (const String& fileName, const String& parameters) | |||
|| ! isFileExecutable (fileName)) | |||
{ | |||
// create a command that tries to launch a bunch of likely browsers | |||
const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", | |||
"google-chrome", "chromium-browser", "opera", "konqueror" }; | |||
static const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", | |||
"google-chrome", "chromium-browser", "opera", "konqueror" }; | |||
StringArray cmdLines; | |||
for (int i = 0; i < numElementsInArray (browserNames); ++i) | |||
@@ -126,6 +126,7 @@ public: | |||
const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||
if (bytesRead == 0) | |||
finished = true; | |||
position += bytesRead; | |||
return bytesRead; | |||
} | |||
@@ -249,7 +250,8 @@ private: | |||
const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, | |||
hostPath, address, headers, postData, isPost)); | |||
if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext)) | |||
if (! sendHeader (socketHandle, requestHeader, timeOutTime, | |||
progressCallback, progressCallbackContext)) | |||
{ | |||
closeSocket(); | |||
return; | |||
@@ -257,6 +259,7 @@ private: | |||
} | |||
String responseHeader (readResponse (socketHandle, timeOutTime)); | |||
position = 0; | |||
if (responseHeader.isNotEmpty()) | |||
{ | |||
@@ -270,7 +273,8 @@ private: | |||
String location (findHeaderItem (headerLines, "Location:")); | |||
if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty()) | |||
if (statusCode >= 300 && statusCode < 400 | |||
&& location.isNotEmpty() && location != address) | |||
{ | |||
if (! location.startsWithIgnoreCase ("http://")) | |||
location = "http://" + location; | |||
@@ -293,44 +297,32 @@ private: | |||
} | |||
//============================================================================== | |||
static String readResponse (const int socketHandle, const uint32 timeOutTime) | |||
String readResponse (const int socketHandle, const uint32 timeOutTime) | |||
{ | |||
int bytesRead = 0, numConsecutiveLFs = 0; | |||
MemoryBlock buffer (1024, true); | |||
int numConsecutiveLFs = 0; | |||
MemoryOutputStream buffer; | |||
while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||
&& Time::getMillisecondCounter() <= timeOutTime) | |||
while (numConsecutiveLFs < 2 | |||
&& buffer.getDataSize() < 32768 | |||
&& Time::getMillisecondCounter() <= timeOutTime | |||
&& ! (finished || isError())) | |||
{ | |||
fd_set readbits; | |||
FD_ZERO (&readbits); | |||
FD_SET (socketHandle, &readbits); | |||
struct timeval tv; | |||
tv.tv_sec = jmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000); | |||
tv.tv_usec = 0; | |||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
return String::empty; // (timeout) | |||
buffer.ensureSize (bytesRead + 8, true); | |||
char* const dest = (char*) buffer.getData() + bytesRead; | |||
if (recv (socketHandle, dest, 1, 0) == -1) | |||
char c = 0; | |||
if (read (&c, 1) != 1) | |||
return String::empty; | |||
const char lastByte = *dest; | |||
++bytesRead; | |||
buffer.writeByte (c); | |||
if (lastByte == '\n') | |||
if (c == '\n') | |||
++numConsecutiveLFs; | |||
else if (lastByte != '\r') | |||
else if (c != '\r') | |||
numConsecutiveLFs = 0; | |||
} | |||
const String header (CharPointer_UTF8 ((const char*) buffer.getData())); | |||
const String header (buffer.toString().trimEnd()); | |||
if (header.startsWithIgnoreCase ("HTTP/")) | |||
return header.trimEnd(); | |||
return header; | |||
return String::empty; | |||
} | |||
@@ -344,9 +336,6 @@ private: | |||
static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, const int port) | |||
{ | |||
dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host; | |||
if (port > 0) | |||
dest << ':' << port; | |||
} | |||
static MemoryBlock createRequestHeader (const String& hostName, const int hostPort, | |||
@@ -365,7 +354,7 @@ private: | |||
writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) | |||
"." JUCE_STRINGIFY(JUCE_MINOR_VERSION) | |||
"." JUCE_STRINGIFY(JUCE_BUILDNUMBER)); | |||
writeValueIfNotPresent (header, userHeaders, "Connection:", "Close"); | |||
writeValueIfNotPresent (header, userHeaders, "Connection:", "close"); | |||
if (isPost) | |||
writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); | |||
@@ -453,9 +442,9 @@ InputStream* URL::createNativeStream (const String& address, bool isPost, const | |||
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||
{ | |||
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||
progressCallback, progressCallbackContext, | |||
headers, timeOutMs, responseHeaders)); | |||
ScopedPointer<WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||
progressCallback, progressCallbackContext, | |||
headers, timeOutMs, responseHeaders)); | |||
return wi->isError() ? nullptr : wi.release(); | |||
} |
@@ -101,7 +101,7 @@ namespace FileHelpers | |||
} | |||
#if JUCE_IOS | |||
String getIOSSystemLocation (NSSearchPathDirectory type) | |||
static String getIOSSystemLocation (NSSearchPathDirectory type) | |||
{ | |||
return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) | |||
objectAtIndex: 0]); | |||
@@ -132,14 +132,14 @@ namespace FileHelpers | |||
bool File::isOnCDRomDrive() const | |||
{ | |||
const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", 0 }; | |||
static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", nullptr }; | |||
return FileHelpers::isFileOnDriveType (*this, cdTypes); | |||
} | |||
bool File::isOnHardDisk() const | |||
{ | |||
const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", 0 }; | |||
static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", nullptr }; | |||
return ! (isOnCDRomDrive() || FileHelpers::isFileOnDriveType (*this, nonHDTypes)); | |||
} | |||
@@ -275,17 +275,25 @@ String File::getVersion() const | |||
} | |||
//============================================================================== | |||
File File::getLinkedTarget() const | |||
static NSString* getFileLink (const String& path) | |||
{ | |||
#if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) | |||
NSString* dest = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (getFullPathName()) error: nil]; | |||
return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (path) error: nil]; | |||
#else | |||
// (the cast here avoids a deprecation warning) | |||
NSString* dest = [((id) [NSFileManager defaultManager]) pathContentOfSymbolicLinkAtPath: juceStringToNS (getFullPathName())]; | |||
return [((id) [NSFileManager defaultManager]) pathContentOfSymbolicLinkAtPath: juceStringToNS (path)]; | |||
#endif | |||
} | |||
if (dest != nil) | |||
return File (nsStringToJuce (dest)); | |||
bool File::isLink() const | |||
{ | |||
return getFileLink (fullPath) != nil; | |||
} | |||
File File::getLinkedTarget() const | |||
{ | |||
if (NSString* dest = getFileLink (fullPath)) | |||
return getSiblingFile (nsStringToJuce (dest)); | |||
return *this; | |||
} | |||
@@ -399,6 +407,7 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& | |||
NSURL* filenameAsURL = [NSURL URLWithString: juceStringToNS (fileName)]; | |||
#if JUCE_IOS | |||
(void) parameters; | |||
return [[UIApplication sharedApplication] openURL: filenameAsURL]; | |||
#else | |||
NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; | |||
@@ -59,6 +59,11 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA | |||
const StringArray& filesToAttach) | |||
{ | |||
#if JUCE_IOS | |||
(void) targetEmailAddress; | |||
(void) emailSubject; | |||
(void) bodyText; | |||
(void) filesToAttach; | |||
//xxx probably need to use MFMailComposeViewController | |||
jassertfalse; | |||
return false; | |||
@@ -988,7 +988,7 @@ void* DynamicLibrary::getFunction (const String& functionName) noexcept | |||
class ChildProcess::ActiveProcess | |||
{ | |||
public: | |||
ActiveProcess (const StringArray& arguments) | |||
ActiveProcess (const StringArray& arguments, int streamFlags) | |||
: childPID (0), pipeHandle (0), readHandle (0) | |||
{ | |||
int pipeHandles[2] = { 0 }; | |||
@@ -1006,8 +1006,17 @@ public: | |||
{ | |||
// we're the child process.. | |||
close (pipeHandles[0]); // close the read handle | |||
dup2 (pipeHandles[1], 1); // turns the pipe into stdout | |||
dup2 (pipeHandles[1], 2); // + stderr | |||
if ((streamFlags | wantStdOut) != 0) | |||
dup2 (pipeHandles[1], 1); // turns the pipe into stdout | |||
else | |||
close (STDOUT_FILENO); | |||
if ((streamFlags | wantStdErr) != 0) | |||
dup2 (pipeHandles[1], 2); | |||
else | |||
close (STDERR_FILENO); | |||
close (pipeHandles[1]); | |||
Array<char*> argv; | |||
@@ -1082,17 +1091,17 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) | |||
}; | |||
bool ChildProcess::start (const String& command) | |||
bool ChildProcess::start (const String& command, int streamFlags) | |||
{ | |||
return start (StringArray::fromTokens (command, true)); | |||
return start (StringArray::fromTokens (command, true), streamFlags); | |||
} | |||
bool ChildProcess::start (const StringArray& args) | |||
bool ChildProcess::start (const StringArray& args, int streamFlags) | |||
{ | |||
if (args.size() == 0) | |||
return false; | |||
activeProcess = new ActiveProcess (args); | |||
activeProcess = new ActiveProcess (args, streamFlags); | |||
if (activeProcess->childPID == 0) | |||
activeProcess = nullptr; | |||
@@ -590,6 +590,11 @@ String File::getVersion() const | |||
} | |||
//============================================================================== | |||
bool File::isLink() const | |||
{ | |||
return hasFileExtension (".lnk"); | |||
} | |||
File File::getLinkedTarget() const | |||
{ | |||
File result (*this); | |||
@@ -600,8 +605,8 @@ File File::getLinkedTarget() const | |||
else if (! hasFileExtension (".lnk")) | |||
return result; | |||
ComSmartPtr <IShellLink> shellLink; | |||
ComSmartPtr <IPersistFile> persistFile; | |||
ComSmartPtr<IShellLink> shellLink; | |||
ComSmartPtr<IPersistFile> persistFile; | |||
if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) | |||
&& SUCCEEDED (shellLink.QueryInterface (persistFile)) | |||
@@ -622,8 +627,8 @@ bool File::createLink (const String& description, const File& linkFileToCreate) | |||
{ | |||
linkFileToCreate.deleteFile(); | |||
ComSmartPtr <IShellLink> shellLink; | |||
ComSmartPtr <IPersistFile> persistFile; | |||
ComSmartPtr<IShellLink> shellLink; | |||
ComSmartPtr<IPersistFile> persistFile; | |||
return SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) | |||
&& SUCCEEDED (shellLink->SetPath (getFullPathName().toWideCharPointer())) | |||
@@ -444,7 +444,7 @@ void InterProcessLock::exit() | |||
class ChildProcess::ActiveProcess | |||
{ | |||
public: | |||
ActiveProcess (const String& command) | |||
ActiveProcess (const String& command, int streamFlags) | |||
: ok (false), readPipe (0), writePipe (0) | |||
{ | |||
SECURITY_ATTRIBUTES securityAtts = { 0 }; | |||
@@ -456,8 +456,9 @@ public: | |||
{ | |||
STARTUPINFOW startupInfo = { 0 }; | |||
startupInfo.cb = sizeof (startupInfo); | |||
startupInfo.hStdError = writePipe; | |||
startupInfo.hStdOutput = writePipe; | |||
startupInfo.hStdOutput = (streamFlags | wantStdOut) != 0 ? writePipe : 0; | |||
startupInfo.hStdError = (streamFlags | wantStdErr) != 0 ? writePipe : 0; | |||
startupInfo.dwFlags = STARTF_USESTDHANDLES; | |||
ok = CreateProcess (nullptr, const_cast <LPWSTR> (command.toWideCharPointer()), | |||
@@ -535,9 +536,9 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) | |||
}; | |||
bool ChildProcess::start (const String& command) | |||
bool ChildProcess::start (const String& command, int streamFlags) | |||
{ | |||
activeProcess = new ActiveProcess (command); | |||
activeProcess = new ActiveProcess (command, streamFlags); | |||
if (! activeProcess->ok) | |||
activeProcess = nullptr; | |||
@@ -545,9 +546,9 @@ bool ChildProcess::start (const String& command) | |||
return activeProcess != nullptr; | |||
} | |||
bool ChildProcess::start (const StringArray& args) | |||
bool ChildProcess::start (const StringArray& args, int streamFlags) | |||
{ | |||
return start (args.joinIntoString (" ")); | |||
return start (args.joinIntoString (" "), streamFlags); | |||
} | |||
bool ChildProcess::isRunning() const | |||
@@ -292,7 +292,7 @@ URL URL::getChildURL (const String& subPath) const | |||
//============================================================================== | |||
bool URL::isProbablyAWebsiteURL (const String& possibleURL) | |||
{ | |||
const char* validProtocols[] = { "http:", "ftp:", "https:" }; | |||
static const char* validProtocols[] = { "http:", "ftp:", "https:" }; | |||
for (int i = 0; i < numElementsInArray (validProtocols); ++i) | |||
if (possibleURL.startsWithIgnoreCase (validProtocols[i])) | |||
@@ -230,7 +230,7 @@ public: | |||
/** Attempts to open a stream that can read from this URL. | |||
@param usePostCommand if true, it will try to do use a http 'POST' to pass | |||
the paramters, otherwise it'll encode them into the | |||
the parameters, otherwise it'll encode them into the | |||
URL and do a 'GET'. | |||
@param progressCallback if this is non-zero, it lets you supply a callback function | |||
to keep track of the operation's progress. This can be useful | |||
@@ -175,8 +175,7 @@ String InputStream::readString() | |||
} | |||
} | |||
return String (CharPointer_UTF8 (data), | |||
CharPointer_UTF8 (data + i)); | |||
return String::fromUTF8 (data, i); | |||
} | |||
String InputStream::readNextLine() | |||
@@ -279,14 +279,25 @@ void OutputStream::setNewLineString (const String& newLineString_) | |||
} | |||
//============================================================================== | |||
template <typename IntegerType> | |||
static void writeIntToStream (OutputStream& stream, IntegerType number) | |||
{ | |||
char buffer [NumberToStringConverters::charsNeededForInt]; | |||
char* end = buffer + numElementsInArray (buffer); | |||
const char* start = NumberToStringConverters::numberToString (end, number); | |||
stream.write (start, (size_t) (end - start - 1)); | |||
} | |||
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int number) | |||
{ | |||
return stream << String (number); | |||
writeIntToStream (stream, number); | |||
return stream; | |||
} | |||
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int64 number) | |||
{ | |||
return stream << String (number); | |||
writeIntToStream (stream, number); | |||
return stream; | |||
} | |||
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const double number) | |||