Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions CMOD/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ file(GLOB_RECURSE CMOD_SRC
)

add_executable(CMOD ${CMOD_SRC})
# CMOD is consumed by LASSIE as a standalone subprocess (QProcess::start),
# not linked into it. No ENABLE_EXPORTS / WINDOWS_EXPORT_ALL_SYMBOLS is
# needed — and adding them just to "make the link work" backfires on
# Windows, where it requires an import library that no consumer actually uses.

### libsndfile must be linked in just the same way as it is in LASS ###
target_link_libraries(CMOD PUBLIC ${SNDFILE_LIBRARY})
Expand Down
5 changes: 2 additions & 3 deletions CMOD/src/Libraries.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <cstdarg>
#include <ctime>
#include <deque>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
Expand All @@ -49,12 +50,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <sstream>
#include <string>
#include <set>
#include <system_error>
#include <vector>
#include <thread>

//Pseudo-standard, but widely used headers
#include <dirent.h>

//The LASS library for additive sound synthesis
#include "LASS.h"
#include "Score.h"
Expand Down
62 changes: 13 additions & 49 deletions CMOD/src/SignalHandlers.cpp
Original file line number Diff line number Diff line change
@@ -1,53 +1,17 @@
#include "SignalHandlers.h"

void segfaultHandler(int signal) {
void *buf[BACKTRACE_NUM + 2];
size_t size = backtrace(buf, BACKTRACE_NUM + 2); // Do a backtrace of the stack
char **messages = backtrace_symbols(buf, size);

std::cerr << "--------------------------------------------------------------------------------\n";
std::cerr << "Segmentation Fault, printing stacktrace of " << BACKTRACE_NUM << " most recent function calls:\n\n";


for (size_t i = 2; i < size; ++i) { //Skip the first two frames since they are this function and the signal generator
size_t len=std::strlen(messages[i]);
char* parser;

//parse the backtrace message to find function name and its respective memory offset
parser = std::find(messages[i], messages[i] + len, '(');
char* function = parser==messages[i]+len ? NULL : parser;

parser = std::find(messages[i], messages[i] + len, '+');
char* start = parser==messages[i]+len ? NULL : parser;

parser = std::find(messages[i], messages[i] + len, ')');
char* end = parser==messages[i]+len ? NULL : parser;


if (function && start && end && function < start) { //If the function name and mem offset are found
*function++ = '\0';
*start++ = '\0';
*end = '\0';

int status;
char *real_name = abi::__cxa_demangle(function, NULL, NULL, &status); //Demangle the function name

if (status) //If the demangling failed, print the mangled name
std::cerr << "--External to CMOD " << function << " mem offset:" << start << "\n";
else
std::cerr << "--" << real_name << " mem offset:" << start << "\n";

free(real_name);
}

else
std::cerr << "External to CMOD " << messages[i] << "\n";
}
std::cerr << "\n--------------------------------------------------------------------------------\n";

free(messages);

std::signal(SIGSEGV, SIG_DFL); //Reset the signal handler to default and raise it again to properly exit
using namespace std;

void segfaultHandler(int /*signal*/) {
std::cerr << "--------------------------------------------------------------------------------\n"
<< "Segmentation fault. Re-raising with default handler for OS diagnostics.\n"
<< "(Run under a debugger — gdb / lldb / MSVC — for a stack trace.)\n"
<< "--------------------------------------------------------------------------------"
<< std::endl;

// Reset to the default handler and re-raise so the OS produces its usual
// crash artifacts (core dump on POSIX, Windows Error Reporting on Win32).
std::signal(SIGSEGV, SIG_DFL);
std::raise(SIGSEGV);
}

Expand All @@ -64,4 +28,4 @@ void terminateHandler() {
// Unimplemented
void abortHandler() {
exit(1);
}
}
21 changes: 10 additions & 11 deletions CMOD/src/SignalHandlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@
#ifndef SIGNALHANDLERS_H
#define SIGNALHANDLERS_H

#include <iostream>
#include <csignal>
#include <execinfo.h>
#include <cstdlib>
#include <unistd.h>
#include <cxxabi.h>
#include <algorithm>
#include <cstring>

#define BACKTRACE_NUM 10 // Number of stack frames to print
#include <iostream>

// Rubin Du 2024
// Custom signal handler to print stack trace on segfault and then exit
// Originally authored by Rubin Du (2024) with a glibc/Itanium-ABI backtrace
// printer; the backtrace was removed in 2026 when DISSCO gained Windows
// support — std::stacktrace (C++23) is the eventual portable replacement,
// but isn't broadly available yet on the toolchains we target. Until then,
// attach a debugger (gdb / lldb / MSVC) for stack-trace detail.
//
// Custom signal handler that announces a segfault and re-raises with the
// default handler so the OS produces its usual diagnostics / core dump.
void segfaultHandler(int signal);

// Unimplemented, can be used to detect ctrl+c while the output is generating and decide whether to abandon and clean up the output or not, and release resources
Expand All @@ -39,4 +38,4 @@ void terminateHandler();
//Unimplemented, could be useful in conjunction with assert to detect trash inputs or other unexpected behavior
void abortHandler();

#endif
#endif
11 changes: 6 additions & 5 deletions CMOD/src/Utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,15 @@ Utilities::Utilities(pugi::xml_node root,
pugi::xml_node envelopeLibraryElement = GNES(GNES(GFEC(root)));
string envLibContent = XMLTranscode(envelopeLibraryElement);
string fileString = "lib.temp";
FILE* file = fopen(fileString.c_str(), "w");
fputs (envLibContent.c_str(), file);
fclose(file);
{
std::ofstream file(fileString);
file << envLibContent;
}

envelopeLibrary = new EnvelopeLibrary();
envelopeLibrary->loadLibraryNewFormat((char*)fileString.c_str());
string deleteCommand = "rm " + fileString;
system(deleteCommand.c_str());
std::error_code rmEc;
std::filesystem::remove(fileString, rmEc);

// Construct Markov Model Library
pugi::xml_node markovModelLibraryElement = GNES(envelopeLibraryElement);
Expand Down
134 changes: 95 additions & 39 deletions CMOD/src/piece-experimental.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,34 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "Utilities.h"
#include <fstream>

#ifdef _WIN32
#undef SIZE
#define WIN32_LEAN_AND_MEAN
#define NOGDI
#define NOMINMAX
#include <windows.h>
#include <vector>
#else
#include <cstdlib>
#endif

//----------------------------------------------------------------------------//

int PieceHelper::getDirectoryList(string dir, vector<string> &files) {
DIR *dp;
struct dirent *dirp;
if((dp = opendir(dir.c_str())) == NULL) {
cout << "Error(" << errno << ") opening " << dir << endl;
return errno;
std::error_code ec;
std::filesystem::directory_iterator it(dir, ec);
if (ec) {
cout << "Error(" << ec.value() << ") opening " << dir << ": "
<< ec.message() << endl;
return ec.value();
}

while ((dirp = readdir(dp)) != NULL) {
files.push_back(string(dirp->d_name));
// Note: directory_iterator skips "." and ".." automatically, unlike the
// POSIX readdir() loop this replaced (which leaked them into the result).
for (const auto& entry : it) {
files.push_back(entry.path().filename().string());
}
closedir(dp);
return 0;

}

//----------------------------------------------------------------------------//
Expand Down Expand Up @@ -120,42 +132,32 @@ int PieceHelper::getSeedNumber(string seed) {
//----------------------------------------------------------------------------//

void PieceHelper::createSoundFilesDirectory(string path) {
string dir = string(path);
vector<string> files = vector<string>();
getDirectoryList(dir, files);
string g = "";
for(unsigned int i = 0; i < files.size(); i++) {
if(files[i] == "SoundFiles")
return;
std::error_code ec;
// create_directory is a no-op if the directory already exists, so the
// previous "list, then mkdir if missing" dance is unnecessary.
std::filesystem::create_directory(path + "SoundFiles", ec);
if (ec) {
cout << "Error creating SoundFiles directory: " << ec.message() << endl;
}

string h = "mkdir " + path + "SoundFiles";
system(h.c_str());

}

//----------------------------------------------------------------------------//

void PieceHelper::createScoreFilesDirectory(string path) {
std::error_code ec;
std::filesystem::path scoreDir = std::filesystem::path(path) / "ScoreFiles";
std::filesystem::create_directory(scoreDir, ec);
if (ec) {
cout << "Error creating ScoreFiles directory: " << ec.message() << endl;
return;
}

string dir = string(path);
vector<string> files = vector<string>();
getDirectoryList(dir, files);
string g = "";
bool dirExists = false;
for(unsigned int i = 0; i < files.size(); i++) {
if(files[i] == "ScoreFiles") {
dirExists = true;
break;
// Clean stale .fms files (was "rm -f .../ScoreFiles/*.fms" via the shell).
for (const auto& entry : std::filesystem::directory_iterator(scoreDir, ec)) {
if (entry.path().extension() == ".fms") {
std::filesystem::remove(entry.path(), ec);
}
}

string h = "mkdir " + path + "ScoreFiles";
if(!dirExists)
system(h.c_str());
h = "rm -f " + path + "ScoreFiles/*.fms";
system(h.c_str());

}

//----------------------------------------------------------------------------//
Expand Down Expand Up @@ -221,7 +223,12 @@ Piece::Piece(string _workingPath, string _projectTitle){
path = _workingPath;
projectName = _projectTitle;
//Change working directory.
chdir(_workingPath.c_str());
std::error_code chdirEc;
std::filesystem::current_path(_workingPath, chdirEc);
if (chdirEc) {
cout << "Error changing working directory to " << _workingPath
<< ": " << chdirEc.message() << endl;
}

//Parse .dissco File
pugi::xml_document disscoDoc;
Expand Down Expand Up @@ -366,7 +373,49 @@ Piece::Piece(string _workingPath, string _projectTitle){
score_file.close();

// execute lilypond to create pdf file
system(("lilypond " + projectName + ".ly").c_str());
{
std::string lilypondCmd = "lilypond " + projectName + ".ly";
#ifdef _WIN32
// Use the native CreateProcess API instead of system() so we avoid
// launching cmd.exe just to parse the command line.
//
// We resolve lilypond.exe via PATH ourselves rather than letting
// CreateProcess do it from a NULL lpApplicationName. With NULL,
// CreateProcess's search order includes the parent's current
// directory (which we just chdir'd to a user-controlled project
// path), so an attacker could drop a lilypond.exe next to the
// .dissco file and have it execute. SearchPath under safe-search
// mode moves CWD to the end of the lookup list, and passing the
// resolved absolute path as lpApplicationName removes the parsing
// ambiguity entirely. If the binary can't be found we fail closed.
SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE |
BASE_SEARCH_PATH_PERMANENT);
char resolvedPath[MAX_PATH];
DWORD resolvedLen = SearchPathA(nullptr, "lilypond", ".exe",
MAX_PATH, resolvedPath, nullptr);
if (resolvedLen == 0 || resolvedLen >= MAX_PATH) {
cout << "Could not locate lilypond.exe on PATH (Win32 error "
<< GetLastError() << ")" << endl;
} else {
STARTUPINFOA si{};
PROCESS_INFORMATION pi{};
si.cb = sizeof(si);
std::vector<char> mutableCmd(lilypondCmd.begin(), lilypondCmd.end());
mutableCmd.push_back('\0');
if (CreateProcessA(resolvedPath, mutableCmd.data(), nullptr, nullptr,
FALSE, 0, nullptr, nullptr, &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
cout << "Failed to launch " << resolvedPath << " (Win32 error "
<< GetLastError() << ")" << endl;
}
}
#else
std::system(lilypondCmd.c_str());
#endif
}

// generating score files with different suffixes instead of replacing the older files.
int suffix_rank = 0;
Expand All @@ -380,7 +429,14 @@ Piece::Piece(string _workingPath, string _projectTitle){
suffix_rank++;
}

system(("mv " + projectName + ".pdf " + "ScoreFiles/" + projectName + suffix + ".pdf").c_str());
std::error_code mvEc;
std::filesystem::rename(projectName + ".pdf",
"ScoreFiles/" + projectName + suffix + ".pdf",
mvEc);
if (mvEc) {
cout << "Error moving " << projectName << ".pdf into ScoreFiles/: "
<< mvEc.message() << endl;
}

}

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ option(BUILD_LASS_EXAMPLES "Build LASS examples" OFF)

# see Cross Compiling with CMake -- Mastering CMake for more on the eponymous subject
if(WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/toolchains/windows.cmake")
elseif(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-deprecated")
Expand Down
2 changes: 1 addition & 1 deletion LASS/src/DynamicVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ m_sample_count_type DynamicVariable::getSampleCount()
}

//----------------------------------------------------------------------------//
//DynamicVariable* DynamicVariable::create_dv_from_xml(XmlReader::xmltag *dvtag, hash_map<long,DynamicVariable*> dvHash)
//DynamicVariable* DynamicVariable::create_dv_from_xml(XmlReader::xmltag *dvtag, unordered_map<long,DynamicVariable*> dvHash)
DynamicVariable* DynamicVariable::create_dv_from_xml(XmlReader::xmltag *dvtag)
{
// First look up the dv_type which is always a child tag
Expand Down
2 changes: 1 addition & 1 deletion LASS/src/DynamicVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class DynamicVariable
virtual void xml_print( ofstream& xmlOutput ) = 0;


//static DynamicVariable* create_dv_from_xml(XmlReader::xmltag *dvtag, hash_map<long,DynamicVariable*> *dvHash);
//static DynamicVariable* create_dv_from_xml(XmlReader::xmltag *dvtag, unordered_map<long,DynamicVariable*> *dvHash);

/**
* \deprecated
Expand Down
4 changes: 4 additions & 0 deletions LASS/src/MultiPan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "Envelope.h"
#include "MultiPan.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

//----------------------------------------------------------------------------//

/** Simple constructor:
Expand Down
Loading