Refactored audiogen tool

This commit is contained in:
2023-02-13 17:07:24 -08:00
parent f1d13d2e45
commit 0794b8739c
21 changed files with 357 additions and 163 deletions

View File

@ -1,13 +1,13 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Check for build target, or default
if(WIN32)
set(DAWN_BUILD_HOST "build-host-win32")
elseif(UNIX AND NOT APPLE)
set(DAWN_BUILD_HOST "build-host-linux64")
endif()
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Check for build target, or default
if(WIN32)
set(DAWN_BUILD_HOST "build-host-win32")
elseif(UNIX AND NOT APPLE)
set(DAWN_BUILD_HOST "build-host-linux64")
endif()
add_subdirectory(${DAWN_BUILD_HOST})

View File

@ -1,6 +1,6 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
set(DAWN_BUILD_HOST_LIBS "m" CACHE INTERNAL ${DAWN_CACHE_TARGET})

View File

@ -91,4 +91,5 @@ const dirScan = directory => {
(() => {
dirScan(DIR_SOURCES);
console.log('Lint done');
})();

View File

@ -1,32 +1,32 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "DeathPrefab.hpp"
using namespace Dawn;
std::string DeathPrefab::getCharacterTexture() {
return "texture_penny";
}
std::string DeathPrefab::getCharacterTileset() {
return "tileset_penny";
}
std::string DeathPrefab::getLanguagePrefix() {
return "character.death";
}
struct VisualNovelCharacterEmotion DeathPrefab::defineAndGetInitialEmotion(
AssetManager *man
) {
this->emotionHappy.tile = 0;
this->emotionConcerned.tile = 1;
this->emotionSurprised.tile = 2;
return this->emotionHappy;
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "DeathPrefab.hpp"
using namespace Dawn;
std::string DeathPrefab::getCharacterTexture() {
return "texture_death";
}
std::string DeathPrefab::getCharacterTileset() {
return "tileset_death";
}
std::string DeathPrefab::getLanguagePrefix() {
return "character.death";
}
struct VisualNovelCharacterEmotion DeathPrefab::defineAndGetInitialEmotion(
AssetManager *man
) {
this->emotionHappy.tile = 0;
this->emotionConcerned.tile = 1;
this->emotionSurprised.tile = 2;
return this->emotionHappy;
}

View File

@ -12,6 +12,7 @@ set(
${DAWN_CACHE_TARGET}
)
# Sources
set(D ${CMAKE_CURRENT_LIST_DIR})
set(
DAWN_SHARED_SOURCES
@ -20,4 +21,10 @@ set(
CACHE INTERNAL
${DAWN_CACHE_TARGET}
)
# Compile Definitions
set(
DAWN_SHARED_DEFINITIONS
)

View File

@ -3,6 +3,24 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Tool Level Values
set(
DAWN_TOOL_INCLUDES
${CMAKE_CURRENT_LIST_DIR}
CACHE INTERNAL ${DAWN_CACHE_TARGET}
)
set(
DAWN_TOOL_SOURCES
CACHE INTERNAL
${DAWN_CACHE_TARGET}
)
# Tool-Utils
include(util/CMakeLists.txt)
# Tools
add_subdirectory(audio)
add_subdirectory(display)
add_subdirectory(file)

View File

@ -1,15 +1,15 @@
# Copyright (c) 2021 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(audiogen)
# Texture Tool
function(tool_audio target in)
add_custom_target(${target}
COMMAND audiogen "${DAWN_ASSETS_SOURCE_DIR}/${in}" "${DAWN_ASSETS_BUILD_DIR}/${target}"
COMMENT "Generating audio ${target} from ${in}"
DEPENDS audiogen
)
# Copyright (c) 2021 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(audiogen)
# Texture Tool
function(tool_audio target in)
add_custom_target(${target}
COMMAND audiogen --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_ASSETS_BUILD_DIR}/${target}"
COMMENT "Generating audio ${target} from ${in}"
DEPENDS audiogen
)
endfunction()

View File

@ -0,0 +1,60 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "AudioGen.hpp"
using namespace Dawn;
std::vector<std::string> AudioGen::getRequiredFlags() {
return std::vector<std::string>{ "input", "output" };
}
int32_t AudioGen::start() {
// Finished with XML data, now we can write data out.
File fileOut(flags["output"] + ".audio");
if(fileOut.exists()) return 0;
// Load input file
AudioFile<double> audioFile;
if(!audioFile.load(flags["input"])) {
printf("Failed to load audio file.\n");
return 1;
}
// Open Output File
fileOut.mkdirp();
if(!fileOut.open(FILE_MODE_WRITE)) {
std::cout << "Failed to open " << fileOut.filename << " output for writing" << std::endl;
return 1;
}
// Write Header
char buffer[FILE_BUFFER_SIZE];
sprintf(buffer, "%i|%i|%i|%i|",
audioFile.getNumChannels(),
audioFile.getSampleRate(),
audioFile.getNumSamplesPerChannel(),
audioFile.getNumSamplesPerChannel() * audioFile.getNumChannels()*(
sizeof(int16_t) / sizeof(uint8_t)
)
);
auto bufferLength = strlen(buffer);
fileOut.writeRaw(buffer, bufferLength);
// Convert Data to 16 bit audio
for (int32_t i = 0; i < audioFile.getNumSamplesPerChannel(); i++) {
for(int32_t y = 0; y < audioFile.getNumChannels(); y++) {
double sample = audioFile.samples[y][i];
sample = sample < -1 ? -1 : sample > 1 ? 1 : sample;
auto q = static_cast<int16_t> (sample * 32767.);
buffer[0] = (q >> 8) & 0xFF;
buffer[1] = q & 0xFF;
fileOut.writeRaw(buffer, 2);
}
}
return 0;
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/DawnTool.hpp"
#include "util/File.hpp"
#include "AudioFile.h"
namespace Dawn {
class AudioGen : public DawnTool {
protected:
std::vector<std::string> getRequiredFlags() override;
public:
int32_t start();
};
}

View File

@ -6,17 +6,32 @@
# Texture Build Tool
project(audiogen VERSION 1.0)
add_executable(audiogen)
# Sources
target_sources(audiogen
PRIVATE
main.cpp
../../util/file.cpp
${DAWN_SHARED_SOURCES}
${DAWN_TOOL_SOURCES}
AudioGen.cpp
)
# Includes
target_include_directories(audiogen
PUBLIC
${DAWN_SHARED_INCLUDES}
${CMAKE_CURRENT_LIST_DIR}/../../
${DAWN_TOOL_INCLUDES}
${CMAKE_CURRENT_LIST_DIR}
)
# Definitions
target_compile_definitions(audiogen
PUBLIC
${DAWN_SHARED_DEFINITIONS}
DAWN_TOOL_INSTANCE=AudioGen
DAWN_TOOL_HEADER="AudioGen.hpp"
)
# Libraries
target_link_libraries(audiogen
PUBLIC
${DAWN_BUILD_HOST_LIBS}

View File

@ -1,76 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "dawnsharedlibs.hpp"
#include "../../util/file.hpp"
#include "AudioFile.h"
int main(int argc, char *argv[]) {
FILE *fileOut;
char buffer[FILENAME_MAX];
size_t bufferLength = 0;
if(argc != 3) {
printf("Invalid number of arguments\n");
return 1;
}
fileNormalizeSlashes(argv[1]);
fileNormalizeSlashes(argv[2]);
std::string strFileIn = std::string(argv[1]);
std::string strFileOut = std::string(argv[2]);
buffer[0] = '\0';
sprintf(buffer, "%s.audio", strFileOut.c_str());
fileOut = fopen(buffer, "rb");
if(fileOut != NULL) {
fclose(fileOut);
return 0;
}
// Load input file
AudioFile<double> audioFile;
if(!audioFile.load(strFileIn)) {
printf("Failed to load audio file.\n");
return 1;
}
// Open Output File
fileMkdirp(buffer);
fileOut = fopen(buffer, "wb");
if(fileOut == NULL) {
printf("Failed to create output file\n");
return 1;
}
// Write header
buffer[0] = '\0';
sprintf(buffer, "%i|%i|%i|%i|",
audioFile.getNumChannels(),
audioFile.getSampleRate(),
audioFile.getNumSamplesPerChannel(),
audioFile.getNumSamplesPerChannel() * audioFile.getNumChannels()*(
sizeof(int16_t) / sizeof(uint8_t)
)
);
bufferLength = strlen(buffer);
fwrite(buffer, sizeof(char), bufferLength, fileOut);
// Convert Data to 16 bit audio
for (int32_t i = 0; i < audioFile.getNumSamplesPerChannel(); i++) {
for(int32_t y = 0; y < audioFile.getNumChannels(); y++) {
double sample = audioFile.samples[y][i];
sample = sample < -1 ? -1 : sample > 1 ? 1 : sample;
auto q = static_cast<int16_t> (sample * 32767.);
buffer[0] = (q >> 8) & 0xFF;
buffer[1] = q & 0xFF;
fwrite(buffer, sizeof(uint8_t), 2, fileOut);
}
}
fclose(fileOut);
return 0;
}

View File

@ -0,0 +1,12 @@
# Copyright (c) 2023 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
set(D ${CMAKE_CURRENT_LIST_DIR})
list(APPEND
DAWN_TOOL_SOURCES
${D}/DawnTool.cpp
${D}/File.cpp
)

View File

@ -17,9 +17,53 @@
using namespace Dawn;
std::vector<std::string> DawnTool::getRequiredFlags() {
return std::vector<std::string>();
}
std::map<std::string, std::string> DawnTool::getOptionalFlags() {
return std::map<std::string, std::string>();
}
int32_t DawnTool::exec(const int32_t argc, const char *argv[]) {
// Set up flags
flags = this->getOptionalFlags();
// Parse args
for(int32_t i = 0; i < argc; i++) {
this->args.push_back(std::string(argv[i]));
std::string a(argv[i]);
this->args.push_back(a);
// First arg is the path, we ignore it.
if(i == 0) continue;
// Is this a flag-like arg, as in has "--[name]=[value]"
if(a.size() < 5) continue;
if(a[0] != '-' || a[1] != '-') continue;
// Remove --
auto flag = a.erase(0, 2);
// Ensure = is present
auto equalPos = flag.find('=');
if(equalPos == std::string::npos) continue;
// Get prefix and val and store
auto prefix = flag.substr(0, equalPos);
auto val = flag.substr(equalPos + 1);
flags[prefix] = val;
}
// Now validate flags
auto required = this->getRequiredFlags();
auto itReq = required.begin();
while(itReq != required.end()) {
auto match = flags.find(*itReq);
if(match == flags.end()) {
std::cout << "Missing required flag \"" + *itReq + "\"!" << std::endl;
return 1;
}
++itReq;
}
return this->start();

View File

@ -18,6 +18,10 @@ namespace Dawn {
class DawnTool {
protected:
std::vector<std::string> args;
std::map<std::string, std::string> flags;
virtual std::vector<std::string> getRequiredFlags();
virtual std::map<std::string, std::string> getOptionalFlags();
public:
int32_t exec(const int32_t argc, const char *argv[]);

View File

@ -7,8 +7,50 @@
using namespace Dawn;
std::string File::normalizeSlashes(std::string str) {
size_t i = 0;
while(i < str.size()) {
auto c = str[i];
if(c == '\\' || c == '/') str[i] = FILE_PATH_SEP;
++i;
}
return str;
}
void File::mkdirp(std::string path) {
std::string buffer;
char c;
size_t i = 0;
bool inFile;
bool hasMore;
inFile = false;
hasMore = false;
while(c = path[i]) {
if((c == '\\' || c == '/') && i > 0) {
fileMkdir(buffer.c_str(), 0755);
inFile = false;
hasMore = false;
buffer += FILE_PATH_SEP;
i++;
continue;
}
if(c == '.') inFile = true;
hasMore = true;
buffer += c;
i++;
}
if(!inFile && hasMore) {
fileMkdir(buffer.c_str(), 0755);
}
}
//
File::File(std::string filename) {
this->filename = fileNormalizeSlashesNew(filename);
this->filename = File::normalizeSlashes(filename);
}
bool_t File::open(enum FileMode mode) {
@ -42,6 +84,14 @@ bool_t File::isOpen() {
return this->file != nullptr;
}
bool_t File::exists() {
if(this->file != nullptr) return true;
FILE *f = fopen(this->filename.c_str(), "rb");
if(f == NULL || f == nullptr) return false;
fclose(f);
return true;
}
void File::close() {
assertNotNull(this->file);
fclose(this->file);
@ -49,7 +99,7 @@ void File::close() {
}
bool_t File::mkdirp() {
fileMkdirp((char*)this->filename.c_str());
File::mkdirp(this->filename);
return true;
}
@ -81,10 +131,15 @@ bool_t File::writeString(std::string in) {
if(!this->open(FILE_MODE_WRITE)) return false;
}
assertTrue(this->mode == FILE_MODE_WRITE);
return this->writeRaw((char *)in.c_str(), in.size()) && this->length == in.size();
}
const char_t *strOut = in.c_str();
// TODO: Validate write length.
this->length = fwrite(strOut, sizeof(char_t), in.size(), this->file);
bool_t File::writeRaw(char *data, size_t len) {
if(!this->isOpen()) {
if(!this->open(FILE_MODE_WRITE)) return false;
}
assertTrue(this->mode == FILE_MODE_WRITE);
this->length = fwrite(data, sizeof(char_t), len, this->file);
return true;
}

View File

@ -5,9 +5,22 @@
#pragma once
#include "assert/assert.hpp"
#include "util/file.hpp"
#include "util/mathutils.hpp"
#if defined(_MSC_VER)
#include <direct.h>
#include <windows.h>
#define getcwd _getcwd
#define FILE_PATH_SEP '\\'
#define fileMkdir(path, perms) _mkdir(path)
#elif defined(__GNUC__)
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#define FILE_PATH_SEP '/'
#define fileMkdir(path, perms) mkdir(path, perms)
#endif
#define FILE_BUFFER_SIZE 512
namespace Dawn {
@ -23,6 +36,9 @@ namespace Dawn {
size_t length;
public:
static std::string normalizeSlashes(std::string str);
static void mkdirp(std::string path);
std::string filename;
/**
@ -47,6 +63,14 @@ namespace Dawn {
*/
bool_t isOpen();
/**
* Returns whether or not the file exists. Will open the connection if it
* does exist.
*
* @return True if exists, otherwsie if it doesn't.
*/
bool_t exists();
/**
* Closes the currently open interface to the file. Done automatically
* when this object is disposed.
@ -78,6 +102,14 @@ namespace Dawn {
*/
bool_t writeString(std::string in);
/**
* Write raw bytes to the file.
*
* @param data Data to write.
* @return True if written successfully, otherwise false.
*/
bool_t writeRaw(char *data, size_t );
~File();
};
}

View File

@ -74,4 +74,4 @@ int32_t skipAhead(
char *needles, int32_t needleCount
);
std::string fileNormalizeSlashesNew(std::string str);
static std::string fileNormalizeSlashesNew(std::string str);

View File

@ -8,7 +8,7 @@ add_subdirectory(vnscenegen)
# UI Tool
function(tool_vnscene target in)
add_custom_target(${target}
COMMAND vnscenegen "${DAWN_ASSETS_SOURCE_DIR}/${in}" "${DAWN_GENERATED_DIR}/scenes/${target}"
COMMAND vnscenegen --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_GENERATED_DIR}/scenes/${target}"
COMMENT "Generating VN Scene ${target} from ${in}"
DEPENDS vnscenegen
)

View File

@ -3,32 +3,35 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Texture Build Tool
# VN Scene Generator Tool
project(vnscenegen VERSION 1.1)
add_executable(vnscenegen)
# Sources
target_sources(vnscenegen
PRIVATE
${DAWN_SHARED_SOURCES}
${DAWN_TOOL_SOURCES}
VnSceneGen.cpp
../../util/DawnTool.cpp
../../util/File.cpp
../../util/file.cpp
../../util/xml.cpp
)
# Includes
target_include_directories(vnscenegen
PUBLIC
${DAWN_SHARED_INCLUDES}
${CMAKE_CURRENT_LIST_DIR}/../../
${DAWN_TOOL_INCLUDES}
${CMAKE_CURRENT_LIST_DIR}
)
# Definitions
target_compile_definitions(vnscenegen
PUBLIC
${DAWN_SHARED_DEFINITIONS}
DAWN_TOOL_INSTANCE=VnSceneGen
DAWN_TOOL_HEADER="VnSceneGen.hpp"
)
# Libraries
target_link_libraries(vnscenegen
PUBLIC
${DAWN_BUILD_HOST_LIBS}

View File

@ -7,14 +7,13 @@
using namespace Dawn;
int32_t VnSceneGen::start() {
if(this->args.size() != 3) {
std::cout << "Invalid number of args passed to VNScene Generator" << std::endl;
return 1;
}
std::vector<std::string> VnSceneGen::getRequiredFlags() {
return std::vector<std::string>{ "input", "output" };
}
int32_t VnSceneGen::start() {
// Open input file.
File file(this->args[1]);
File file(flags["input"]);
std::string buffer;
if(!file.readString(&buffer)) {
std::cout << "Failed to read scene " << file.filename << std::endl;
@ -43,7 +42,7 @@ int32_t VnSceneGen::start() {
}
// Finished with XML data, now we can write data out.
File fileOut(this->args[2] + ".hpp");
File fileOut(flags["output"] + ".hpp");
if(!fileOut.mkdirp()) {
std::cout << "Failed to make scene output dir" << std::endl;
return 1;

View File

@ -11,6 +11,7 @@
namespace Dawn {
class VnSceneGen : public DawnTool {
protected:
std::vector<std::string> getRequiredFlags() override;
public:
int32_t start();