mirror of
https://github.com/AxioDL/amuse.git
synced 2025-12-08 13:14:58 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e99dbc7e0a | ||
|
|
d6b9d4fca1 | ||
|
|
c7f093c5ee | ||
|
|
22a8534887 | ||
|
|
0c606fa9b7 | ||
|
|
a0241574ba | ||
|
|
bd10015024 | ||
|
|
a0bb35433a | ||
|
|
3bc47baa1d | ||
|
|
7666b51c50 | ||
|
|
ee29fb4b1e | ||
|
|
ad23f9d0c4 |
@@ -49,7 +49,7 @@ void RegisterAudioUnit();
|
||||
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
|
||||
error:(NSError * __nullable * __nonnull)outError
|
||||
viewController:(AudioUnitViewController* __nonnull)vc;
|
||||
- (void)requestAudioGroup:(AudioGroupToken*)group;
|
||||
- (void)requestAudioGroup:(AudioGroupToken* _Nonnull)group;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,12 +10,14 @@ set(SOURCES
|
||||
lib/AudioGroupPool.cpp
|
||||
lib/AudioGroupProject.cpp
|
||||
lib/AudioGroupSampleDirectory.cpp
|
||||
lib/DirectoryEnumerator.cpp
|
||||
lib/Emitter.cpp
|
||||
lib/Engine.cpp
|
||||
lib/Envelope.cpp
|
||||
lib/Listener.cpp
|
||||
lib/Sequencer.cpp
|
||||
lib/SoundMacroState.cpp
|
||||
lib/SongConverter.cpp
|
||||
lib/SongState.cpp
|
||||
lib/Voice.cpp
|
||||
lib/VolumeLUT.cpp
|
||||
@@ -35,6 +37,7 @@ set(HEADERS
|
||||
include/amuse/AudioGroupPool.hpp
|
||||
include/amuse/AudioGroupProject.hpp
|
||||
include/amuse/AudioGroupSampleDirectory.hpp
|
||||
include/amuse/DirectoryEnumerator.hpp
|
||||
include/amuse/Emitter.hpp
|
||||
include/amuse/Engine.hpp
|
||||
include/amuse/Entity.hpp
|
||||
@@ -42,6 +45,7 @@ set(HEADERS
|
||||
include/amuse/Listener.hpp
|
||||
include/amuse/Sequencer.hpp
|
||||
include/amuse/SoundMacroState.hpp
|
||||
include/amuse/SongConverter.hpp
|
||||
include/amuse/SongState.hpp
|
||||
include/amuse/Voice.hpp
|
||||
include/amuse/Submix.hpp
|
||||
@@ -81,7 +85,13 @@ if(TARGET boo)
|
||||
# VST Target
|
||||
add_subdirectory(VST)
|
||||
|
||||
# Multi-platform CLI tool
|
||||
add_executable(amuseplay WIN32 driver/main.cpp)
|
||||
# Multi-platform CLI tools
|
||||
|
||||
# Player
|
||||
add_executable(amuseplay WIN32 driver/amuseplay.cpp)
|
||||
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
|
||||
|
||||
# Converter
|
||||
add_executable(amuseconv driver/amuseconv.cpp)
|
||||
target_link_libraries(amuseconv amuse ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
@@ -385,7 +385,7 @@ void VSTEditor::addAction()
|
||||
if (dotpos != std::string::npos)
|
||||
name.assign(path.cbegin(), path.cbegin() + dotpos);
|
||||
size_t slashpos = name.rfind(L'\\');
|
||||
size_t fslashpos = name.rfind(L"/");
|
||||
size_t fslashpos = name.rfind(L'/');
|
||||
if (slashpos == std::string::npos)
|
||||
slashpos = fslashpos;
|
||||
else if (fslashpos != std::string::npos)
|
||||
|
||||
217
driver/amuseconv.cpp
Normal file
217
driver/amuseconv.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "amuse/amuse.hpp"
|
||||
#include "athena/FileReader.hpp"
|
||||
#include "athena/DNAYaml.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static logvisor::Module Log("amuseconv");
|
||||
|
||||
enum ConvType
|
||||
{
|
||||
ConvN64,
|
||||
ConvGCN,
|
||||
ConvPC
|
||||
};
|
||||
|
||||
static void ReportConvType(ConvType tp)
|
||||
{
|
||||
switch (tp)
|
||||
{
|
||||
case ConvN64:
|
||||
Log.report(logvisor::Info, _S("using N64 format"));
|
||||
break;
|
||||
case ConvPC:
|
||||
Log.report(logvisor::Info, _S("using PC format"));
|
||||
break;
|
||||
case ConvGCN:
|
||||
default:
|
||||
Log.report(logvisor::Info, _S("using GameCube format"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool BuildAudioGroup(const amuse::SystemString& groupBase, const amuse::SystemString& targetPath)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExtractAudioGroup(const amuse::SystemString& inPath, const amuse::SystemString& targetPath)
|
||||
{
|
||||
amuse::ContainerRegistry::Type type;
|
||||
auto groups = amuse::ContainerRegistry::LoadContainer(inPath.c_str(), type);
|
||||
|
||||
if (groups.size())
|
||||
{
|
||||
Log.report(logvisor::Info, _S("Found '%s'"), amuse::ContainerRegistry::TypeToName(type));
|
||||
|
||||
amuse::Mkdir(targetPath.c_str(), 0755);
|
||||
Log.report(logvisor::Info, _S("Established directory at %s"), targetPath.c_str());
|
||||
|
||||
for (auto& group : groups)
|
||||
{
|
||||
Log.report(logvisor::Info, _S("Extracting %s"), group.first.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.c_str());
|
||||
amuse::SystemString songsDir = targetPath + _S("/midifiles");
|
||||
bool madeDir = false;
|
||||
for (auto& pair : songs)
|
||||
{
|
||||
if (!madeDir)
|
||||
{
|
||||
amuse::Mkdir(targetPath.c_str(), 0755);
|
||||
amuse::Mkdir(songsDir.c_str(), 0755);
|
||||
madeDir = true;
|
||||
}
|
||||
|
||||
amuse::SystemString songPath = songsDir + _S('/') + pair.first + _S(".mid");
|
||||
FILE* fp = amuse::FOpen(songPath.c_str(), _S("wb"));
|
||||
if (fp)
|
||||
{
|
||||
Log.report(logvisor::Info, _S("Extracting %s"), pair.first.c_str());
|
||||
amuse::SongConverter::Target extractedTarget;
|
||||
std::vector<uint8_t> mid = amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedTarget);
|
||||
fwrite(mid.data(), 1, mid.size(), fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool BuildSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath, amuse::SongConverter::Target target)
|
||||
{
|
||||
FILE* fp = amuse::FOpen(inPath.c_str(), _S("rb"));
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long sz = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
std::vector<uint8_t> data(sz, 0);
|
||||
fread(&data[0], 1, sz, fp);
|
||||
fclose(fp);
|
||||
|
||||
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, target);
|
||||
if (out.empty())
|
||||
return false;
|
||||
|
||||
fp = amuse::FOpen(targetPath.c_str(), _S("wb"));
|
||||
fwrite(out.data(), 1, out.size(), fp);
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExtractSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath)
|
||||
{
|
||||
FILE* fp = amuse::FOpen(inPath.c_str(), _S("rb"));
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long sz = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
std::vector<uint8_t> data(sz, 0);
|
||||
fread(&data[0], 1, sz, fp);
|
||||
fclose(fp);
|
||||
|
||||
amuse::SongConverter::Target target;
|
||||
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), target);
|
||||
if (out.empty())
|
||||
return false;
|
||||
|
||||
ReportConvType(ConvType(target));
|
||||
|
||||
fp = amuse::FOpen(targetPath.c_str(), _S("wb"));
|
||||
fwrite(out.data(), 1, out.size(), fp);
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
int wmain(int argc, const amuse::SystemChar** argv)
|
||||
#else
|
||||
int main(int argc, const amuse::SystemChar** argv)
|
||||
#endif
|
||||
{
|
||||
logvisor::RegisterConsoleLogger();
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
printf("Usage: amuseconv <in-file> <out-file> [n64|pc|gcn]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ConvType type = ConvGCN;
|
||||
if (argc >= 4)
|
||||
{
|
||||
if (!amuse::CompareCaseInsensitive(argv[3], _S("n64")))
|
||||
type = ConvN64;
|
||||
else if (!amuse::CompareCaseInsensitive(argv[3], _S("gcn")))
|
||||
type = ConvGCN;
|
||||
else if (!amuse::CompareCaseInsensitive(argv[3], _S("pc")))
|
||||
type = ConvPC;
|
||||
else
|
||||
{
|
||||
Log.report(logvisor::Error, _S("unrecognized format: %s"), argv[3]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool good = false;
|
||||
FILE* fin = amuse::FOpen(argv[1], _S("rb"));
|
||||
if (fin)
|
||||
{
|
||||
fclose(fin);
|
||||
amuse::SystemString barePath(argv[1]);
|
||||
size_t dotPos = barePath.rfind(_S('.'));
|
||||
const amuse::SystemChar* dot = barePath.c_str() + dotPos;
|
||||
if (dotPos != amuse::SystemString::npos)
|
||||
{
|
||||
if (!amuse::CompareCaseInsensitive(dot, _S(".mid")) ||
|
||||
!amuse::CompareCaseInsensitive(dot, _S(".midi")))
|
||||
{
|
||||
ReportConvType(type);
|
||||
good = BuildSNG(barePath, argv[2], amuse::SongConverter::Target(type));
|
||||
}
|
||||
else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) ||
|
||||
!amuse::CompareCaseInsensitive(dot, _S(".sng")))
|
||||
{
|
||||
good = ExtractSNG(argv[1], argv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
good = ExtractAudioGroup(argv[1], argv[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
amuse::Sstat theStat;
|
||||
if (!amuse::Stat(argv[1], &theStat) && S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
amuse::SystemString projectPath(argv[1]);
|
||||
projectPath += _S("/project.yaml");
|
||||
fin = amuse::FOpen(projectPath.c_str(), _S("rb"));
|
||||
if (fin)
|
||||
{
|
||||
fclose(fin);
|
||||
ReportConvType(type);
|
||||
good = BuildAudioGroup(argv[1], argv[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!good)
|
||||
{
|
||||
Log.report(logvisor::Error, _S("unable to convert %s to %s"), argv[1], argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -782,9 +782,11 @@ struct AppCallback : boo::IApplicationCallback
|
||||
}
|
||||
for (const auto& pair : allSongGroups)
|
||||
{
|
||||
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n"),
|
||||
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize " MIDI-setups\n"),
|
||||
pair.first, pair.second.first->first.c_str(),
|
||||
pair.second.second->m_normPages.size(), pair.second.second->m_drumPages.size());
|
||||
pair.second.second->m_normPages.size(),
|
||||
pair.second.second->m_drumPages.size(),
|
||||
pair.second.second->m_midiSetups.size());
|
||||
}
|
||||
|
||||
int userSel = 0;
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <strings.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
namespace amuse
|
||||
@@ -30,12 +31,18 @@ namespace amuse
|
||||
# ifndef _S
|
||||
# define _S(val) L ## val
|
||||
# endif
|
||||
typedef struct _stat Sstat;
|
||||
static inline int Mkdir(const wchar_t* path, int) {return _wmkdir(path);}
|
||||
static inline int Stat(const wchar_t* path, Sstat* statout) {return _wstat(path, statout);}
|
||||
#else
|
||||
using SystemString = std::string;
|
||||
using SystemChar = char;
|
||||
# ifndef _S
|
||||
# define _S(val) val
|
||||
# endif
|
||||
typedef struct stat Sstat;
|
||||
static inline int Mkdir(const char* path, mode_t mode) {return mkdir(path, mode);}
|
||||
static inline int Stat(const char* path, Sstat* statout) {return stat(path, statout);}
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
|
||||
74
include/amuse/DirectoryEnumerator.hpp
Normal file
74
include/amuse/DirectoryEnumerator.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef __AMUSE_DIRECTORY_ENUMERATOR__
|
||||
#define __AMUSE_DIRECTORY_ENUMERATOR__
|
||||
|
||||
#include "Common.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
struct CaseInsensitiveCompare
|
||||
{
|
||||
bool operator()(const std::string& lhs, const std::string& rhs) const
|
||||
{
|
||||
#if _WIN32
|
||||
if (_stricmp(lhs.c_str(), rhs.c_str()) < 0)
|
||||
#else
|
||||
if (strcasecmp(lhs.c_str(), rhs.c_str()) < 0)
|
||||
#endif
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
bool operator()(const std::wstring& lhs, const std::wstring& rhs) const
|
||||
{
|
||||
if (_wcsicmp(lhs.c_str(), rhs.c_str()) < 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class DirectoryEnumerator
|
||||
{
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
Native,
|
||||
DirsSorted,
|
||||
FilesSorted,
|
||||
DirsThenFilesSorted
|
||||
};
|
||||
struct Entry
|
||||
{
|
||||
SystemString m_path;
|
||||
SystemString m_name;
|
||||
size_t m_fileSz;
|
||||
bool m_isDir;
|
||||
|
||||
private:
|
||||
friend class DirectoryEnumerator;
|
||||
Entry(SystemString&& path, const SystemChar* name, size_t sz, bool isDir)
|
||||
: m_path(std::move(path)), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Entry> m_entries;
|
||||
|
||||
public:
|
||||
DirectoryEnumerator(const SystemString& path, Mode mode=Mode::DirsThenFilesSorted,
|
||||
bool sizeSort=false, bool reverse=false, bool noHidden=false)
|
||||
: DirectoryEnumerator(path.c_str(), mode, sizeSort, reverse, noHidden) {}
|
||||
DirectoryEnumerator(const SystemChar* path, Mode mode=Mode::DirsThenFilesSorted,
|
||||
bool sizeSort=false, bool reverse=false, bool noHidden=false);
|
||||
|
||||
operator bool() const {return m_entries.size() != 0;}
|
||||
size_t size() const {return m_entries.size();}
|
||||
std::vector<Entry>::const_iterator begin() const {return m_entries.cbegin();}
|
||||
std::vector<Entry>::const_iterator end() const {return m_entries.cend();}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_DIRECTORY_ENUMERATOR__
|
||||
25
include/amuse/SongConverter.hpp
Normal file
25
include/amuse/SongConverter.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef __AMUSE_SONGCONVERTER_HPP__
|
||||
#define __AMUSE_SONGCONVERTER_HPP__
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
class SongConverter
|
||||
{
|
||||
public:
|
||||
enum class Target
|
||||
{
|
||||
N64,
|
||||
GCN,
|
||||
PC
|
||||
};
|
||||
static std::vector<uint8_t> SongToMIDI(const unsigned char* data, Target& targetOut);
|
||||
static std::vector<uint8_t> MIDIToSong(const std::vector<uint8_t>& data, Target target);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SONGCONVERTER_HPP__
|
||||
@@ -22,34 +22,30 @@ enum class SongPlayState
|
||||
class SongState
|
||||
{
|
||||
friend class Voice;
|
||||
friend class SongConverter;
|
||||
|
||||
/** Song header */
|
||||
struct Header
|
||||
{
|
||||
uint32_t m_version;
|
||||
uint32_t m_chanIdxOff;
|
||||
uint32_t m_trackIdxOff;
|
||||
uint32_t m_regionIdxOff;
|
||||
uint32_t m_chanMapOff;
|
||||
uint32_t m_tempoTableOff;
|
||||
uint32_t m_initialTempo;
|
||||
uint32_t m_unkOff;
|
||||
uint32_t m_chanOffs[64];
|
||||
void swapBig();
|
||||
} m_header;
|
||||
|
||||
/** Channel header */
|
||||
struct ChanHeader
|
||||
/** Track region ('clip' in an NLA representation) */
|
||||
struct TrackRegion
|
||||
{
|
||||
uint32_t m_startTick;
|
||||
uint16_t m_unk1;
|
||||
uint8_t m_progNum;
|
||||
uint8_t m_unk1;
|
||||
uint16_t m_unk2;
|
||||
uint16_t m_dataIndex;
|
||||
uint16_t m_unk3;
|
||||
uint32_t m_startTick2;
|
||||
uint16_t m_unk4;
|
||||
uint16_t m_unk5;
|
||||
uint16_t m_unk6;
|
||||
uint16_t m_unk7;
|
||||
void swapBig();
|
||||
int16_t m_regionIndex;
|
||||
int16_t m_unk3;
|
||||
bool indexValid(bool bigEndian) const;
|
||||
};
|
||||
|
||||
/** Tempo change entry */
|
||||
@@ -60,8 +56,11 @@ class SongState
|
||||
void swapBig();
|
||||
};
|
||||
|
||||
/** State of a single channel within arrangement */
|
||||
struct Channel
|
||||
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
|
||||
bool m_bigEndian; /**< True if loaded song is big-endian data */
|
||||
|
||||
/** State of a single track within arrangement */
|
||||
struct Track
|
||||
{
|
||||
struct Header
|
||||
{
|
||||
@@ -73,25 +72,28 @@ class SongState
|
||||
|
||||
SongState& m_parent;
|
||||
uint8_t m_midiChan; /**< MIDI channel number of song channel */
|
||||
uint32_t m_startTick; /**< Tick to start execution of channel commands */
|
||||
const TrackRegion* m_curRegion; /**< Pointer to currently-playing track region */
|
||||
const TrackRegion* m_nextRegion; /**< Pointer to next-queued track region */
|
||||
|
||||
const unsigned char* m_dataBase; /**< Base pointer to command data */
|
||||
const unsigned char* m_data; /**< Pointer to upcoming command data */
|
||||
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
|
||||
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
|
||||
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
|
||||
uint32_t m_lastPitchTick = 0; /**< Last position of pitch wheel change */
|
||||
int32_t m_lastPitchVal = 0; /**< Last value of pitch */
|
||||
uint32_t m_lastModTick = 0; /**< Last position of mod wheel change */
|
||||
int32_t m_lastModVal = 0; /**< Last value of mod */
|
||||
std::array<uint16_t, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
|
||||
std::array<int, 128> m_remNoteLengths; /**< Remaining ticks per note */
|
||||
|
||||
int32_t m_waitCountdown = 0; /**< Current wait in ticks */
|
||||
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
|
||||
int32_t m_lastN64EventTick = 0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */
|
||||
|
||||
Channel(SongState& parent, uint8_t midiChan, uint32_t startTick,
|
||||
const unsigned char* song, const unsigned char* chan);
|
||||
Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions);
|
||||
void setRegion(Sequencer* seq, const TrackRegion* region);
|
||||
void advanceRegion(Sequencer* seq);
|
||||
bool advance(Sequencer& seq, int32_t ticks);
|
||||
};
|
||||
std::array<std::experimental::optional<Channel>, 64> m_channels;
|
||||
std::array<std::experimental::optional<Track>, 64> m_tracks;
|
||||
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
|
||||
|
||||
/** Current pointer to tempo control, iterated over playback */
|
||||
const TempoChange* m_tempoPtr = nullptr;
|
||||
|
||||
@@ -159,6 +159,7 @@ class Voice : public Entity
|
||||
|
||||
void _setPan(float pan);
|
||||
void _setSurroundPan(float span);
|
||||
void _setPitchWheel(float pitchWheel);
|
||||
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
||||
public:
|
||||
~Voice();
|
||||
@@ -307,12 +308,6 @@ public:
|
||||
_notifyCtrlChange(ctrl, val);
|
||||
}
|
||||
|
||||
/** Get ModWheel value on voice */
|
||||
int8_t getModWheel() const
|
||||
{
|
||||
return m_state.m_modWheelSel ? m_state.m_modWheelSel.evaluate(*this, m_state) : getCtrlValue(1);
|
||||
}
|
||||
|
||||
/** 'install' external MIDI controller storage */
|
||||
void installCtrlValues(int8_t* cvs)
|
||||
{
|
||||
@@ -321,7 +316,7 @@ public:
|
||||
}
|
||||
|
||||
/** Get MIDI pitch wheel value on voice */
|
||||
int8_t getPitchWheel() const {return m_curPitchWheel * 127;}
|
||||
float getPitchWheel() const {return m_curPitchWheel;}
|
||||
|
||||
/** Get MIDI aftertouch value on voice */
|
||||
int8_t getAftertouch() const {return m_curAftertouch;}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "Listener.hpp"
|
||||
#include "Sequencer.hpp"
|
||||
#include "SoundMacroState.hpp"
|
||||
#include "SongConverter.hpp"
|
||||
#include "SongState.hpp"
|
||||
#include "Submix.hpp"
|
||||
#include "Voice.hpp"
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ IntrusiveAudioGroupData::~IntrusiveAudioGroupData()
|
||||
{
|
||||
if (m_owns)
|
||||
{
|
||||
delete m_pool;
|
||||
delete m_proj;
|
||||
delete m_sdir;
|
||||
delete m_samp;
|
||||
delete[] m_pool;
|
||||
delete[] m_proj;
|
||||
delete[] m_sdir;
|
||||
delete[] m_samp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupD
|
||||
{
|
||||
if (m_owns)
|
||||
{
|
||||
delete m_pool;
|
||||
delete m_proj;
|
||||
delete m_sdir;
|
||||
delete m_samp;
|
||||
delete[] m_pool;
|
||||
delete[] m_proj;
|
||||
delete[] m_sdir;
|
||||
delete[] m_samp;
|
||||
}
|
||||
|
||||
m_owns = other.m_owns;
|
||||
|
||||
@@ -35,7 +35,7 @@ static void *memmem(const void *haystack, size_t hlen, const void *needle, size_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
amuse::SystemString StrToSys(const std::string& str)
|
||||
static amuse::SystemString StrToSys(const std::string& str)
|
||||
{
|
||||
std::wstring ret;
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), nullptr, 0);
|
||||
@@ -45,7 +45,7 @@ amuse::SystemString StrToSys(const std::string& str)
|
||||
}
|
||||
|
||||
#else
|
||||
amuse::SystemString StrToSys(const std::string& str)
|
||||
static amuse::SystemString StrToSys(const std::string& str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
@@ -725,6 +725,41 @@ static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadRS1PC(F
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<SystemString, ContainerRegistry::SongData>> LoadRS1PCSongs(FILE* fp)
|
||||
{
|
||||
std::vector<std::pair<SystemString, ContainerRegistry::SongData>> ret;
|
||||
size_t endPos = FileLength(fp);
|
||||
|
||||
uint32_t fstOff;
|
||||
uint32_t fstSz;
|
||||
if (fread(&fstOff, 1, 4, fp) == 4 && fread(&fstSz, 1, 4, fp) == 4)
|
||||
{
|
||||
if (fstOff + fstSz <= endPos)
|
||||
{
|
||||
FSeek(fp, fstOff, SEEK_SET);
|
||||
uint32_t elemCount = fstSz / 32;
|
||||
std::unique_ptr<RS1FSTEntry[]> entries(new RS1FSTEntry[elemCount]);
|
||||
fread(entries.get(), fstSz, 1, fp);
|
||||
|
||||
for (uint32_t i=0 ; i<elemCount ; ++i)
|
||||
{
|
||||
RS1FSTEntry& entry = entries[i];
|
||||
if (strstr(entry.name, "SNG"))
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> song(new uint8_t[entry.decompSz]);
|
||||
FSeek(fp, entry.offset, SEEK_SET);
|
||||
fread(song.get(), 1, entry.decompSz, fp);
|
||||
|
||||
SystemString name = StrToSys(entry.name);
|
||||
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), entry.decompSz, -1, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool ValidateRS1N64(FILE* fp)
|
||||
{
|
||||
size_t endPos = FileLength(fp);
|
||||
@@ -890,6 +925,61 @@ static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadRS1N64(
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<SystemString, ContainerRegistry::SongData>> LoadRS1N64Songs(FILE* fp)
|
||||
{
|
||||
std::vector<std::pair<SystemString, ContainerRegistry::SongData>> ret;
|
||||
size_t endPos = FileLength(fp);
|
||||
|
||||
std::unique_ptr<uint8_t[]> data(new uint8_t[endPos]);
|
||||
fread(data.get(), 1, endPos, fp);
|
||||
|
||||
if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80)
|
||||
SwapN64Rom32(data.get(), endPos);
|
||||
else if ((data[0] & 0x80) != 0x80 && (data[1] & 0x80) == 0x80)
|
||||
SwapN64Rom16(data.get(), endPos);
|
||||
|
||||
const uint8_t* dataSeg = reinterpret_cast<const uint8_t*>(memmem(data.get(), endPos,
|
||||
"dbg_data\0\0\0\0\0\0\0\0", 16));
|
||||
if (dataSeg)
|
||||
{
|
||||
dataSeg += 28;
|
||||
size_t fstEnd = SBig(*reinterpret_cast<const uint32_t*>(dataSeg));
|
||||
dataSeg += 4;
|
||||
size_t fstOff = SBig(*reinterpret_cast<const uint32_t*>(dataSeg));
|
||||
if (endPos <= size_t(dataSeg - data.get()) + fstOff || endPos <= size_t(dataSeg - data.get()) + fstEnd)
|
||||
return ret;
|
||||
|
||||
const RS1FSTEntry* entry = reinterpret_cast<const RS1FSTEntry*>(dataSeg + fstOff);
|
||||
const RS1FSTEntry* lastEnt = reinterpret_cast<const RS1FSTEntry*>(dataSeg + fstEnd);
|
||||
|
||||
for (; entry != lastEnt ; ++entry)
|
||||
{
|
||||
RS1FSTEntry ent = *entry;
|
||||
ent.swapBig();
|
||||
|
||||
if (strstr(ent.name, "SNG"))
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> song(new uint8_t[ent.decompSz]);
|
||||
|
||||
if (ent.compSz == 0xffffffff)
|
||||
{
|
||||
memmove(song.get(), dataSeg + ent.offset, ent.decompSz);
|
||||
}
|
||||
else
|
||||
{
|
||||
uLongf outSz = ent.decompSz;
|
||||
uncompress(song.get(), &outSz, dataSeg + ent.offset, ent.compSz);
|
||||
}
|
||||
|
||||
SystemString name = StrToSys(ent.name);
|
||||
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), ent.decompSz, -1, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool ValidateBFNPC(FILE* fp)
|
||||
{
|
||||
size_t endPos = FileLength(fp);
|
||||
@@ -996,6 +1086,41 @@ static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadBFNPC(F
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<SystemString, ContainerRegistry::SongData>> LoadBFNPCSongs(FILE* fp)
|
||||
{
|
||||
std::vector<std::pair<SystemString, ContainerRegistry::SongData>> ret;
|
||||
size_t endPos = FileLength(fp);
|
||||
|
||||
uint32_t fstOff;
|
||||
uint32_t fstSz;
|
||||
if (fread(&fstOff, 1, 4, fp) == 4 && fread(&fstSz, 1, 4, fp) == 4)
|
||||
{
|
||||
if (fstOff + fstSz <= endPos)
|
||||
{
|
||||
FSeek(fp, fstOff, SEEK_SET);
|
||||
uint32_t elemCount = fstSz / 32;
|
||||
std::unique_ptr<RS1FSTEntry[]> entries(new RS1FSTEntry[elemCount]);
|
||||
fread(entries.get(), fstSz, 1, fp);
|
||||
|
||||
for (uint32_t i=0 ; i<elemCount ; ++i)
|
||||
{
|
||||
RS1FSTEntry& entry = entries[i];
|
||||
if (!strncmp(entry.name, "s_", 2))
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> song(new uint8_t[entry.decompSz]);
|
||||
FSeek(fp, entry.offset, SEEK_SET);
|
||||
fread(song.get(), 1, entry.decompSz, fp);
|
||||
|
||||
SystemString name = StrToSys(entry.name);
|
||||
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), entry.decompSz, -1, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool ValidateBFNN64(FILE* fp)
|
||||
{
|
||||
size_t endPos = FileLength(fp);
|
||||
@@ -1161,6 +1286,61 @@ static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadBFNN64(
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<SystemString, ContainerRegistry::SongData>> LoadBFNN64Songs(FILE* fp)
|
||||
{
|
||||
std::vector<std::pair<SystemString, ContainerRegistry::SongData>> ret;
|
||||
size_t endPos = FileLength(fp);
|
||||
|
||||
std::unique_ptr<uint8_t[]> data(new uint8_t[endPos]);
|
||||
fread(data.get(), 1, endPos, fp);
|
||||
|
||||
if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80)
|
||||
SwapN64Rom32(data.get(), endPos);
|
||||
else if ((data[0] & 0x80) != 0x80 && (data[1] & 0x80) == 0x80)
|
||||
SwapN64Rom16(data.get(), endPos);
|
||||
|
||||
const uint8_t* dataSeg = reinterpret_cast<const uint8_t*>(memmem(data.get(), endPos,
|
||||
"dbg_data\0\0\0\0\0\0\0\0", 16));
|
||||
if (dataSeg)
|
||||
{
|
||||
dataSeg += 28;
|
||||
size_t fstEnd = SBig(*reinterpret_cast<const uint32_t*>(dataSeg));
|
||||
dataSeg += 4;
|
||||
size_t fstOff = SBig(*reinterpret_cast<const uint32_t*>(dataSeg));
|
||||
if (endPos <= size_t(dataSeg - data.get()) + fstOff || endPos <= size_t(dataSeg - data.get()) + fstEnd)
|
||||
return ret;
|
||||
|
||||
const RS1FSTEntry* entry = reinterpret_cast<const RS1FSTEntry*>(dataSeg + fstOff);
|
||||
const RS1FSTEntry* lastEnt = reinterpret_cast<const RS1FSTEntry*>(dataSeg + fstEnd);
|
||||
|
||||
for (; entry != lastEnt ; ++entry)
|
||||
{
|
||||
RS1FSTEntry ent = *entry;
|
||||
ent.swapBig();
|
||||
|
||||
if (!strncmp(ent.name, "s_", 2))
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> song(new uint8_t[ent.decompSz]);
|
||||
|
||||
if (ent.compSz == 0xffffffff)
|
||||
{
|
||||
memmove(song.get(), dataSeg + ent.offset, ent.decompSz);
|
||||
}
|
||||
else
|
||||
{
|
||||
uLongf outSz = ent.decompSz;
|
||||
uncompress(song.get(), &outSz, dataSeg + ent.offset, ent.compSz);
|
||||
}
|
||||
|
||||
SystemString name = StrToSys(ent.name);
|
||||
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), ent.decompSz, -1, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct RS2FSTEntry
|
||||
{
|
||||
uint64_t offset;
|
||||
@@ -1847,35 +2027,33 @@ ContainerRegistry::LoadSongs(const SystemChar* path)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (ValidateRS1PCSongs(fp))
|
||||
if (ValidateRS1PC(fp))
|
||||
{
|
||||
auto ret = LoadRS1PCSongs(fp);
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ValidateRS1N64Songs(fp))
|
||||
if (ValidateRS1N64(fp))
|
||||
{
|
||||
auto ret = LoadRS1N64Songs(fp);
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ValidateBFNPCSongs(fp))
|
||||
if (ValidateBFNPC(fp))
|
||||
{
|
||||
auto ret = LoadBFNPCSongs(fp);
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ValidateBFNN64Songs(fp))
|
||||
if (ValidateBFNN64(fp))
|
||||
{
|
||||
auto ret = LoadBFNN64Songs(fp);
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ValidateRS2(fp))
|
||||
{
|
||||
|
||||
287
lib/DirectoryEnumerator.cpp
Normal file
287
lib/DirectoryEnumerator.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
|
||||
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "amuse/DirectoryEnumerator.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
DirectoryEnumerator::DirectoryEnumerator(const SystemChar* path, Mode mode,
|
||||
bool sizeSort, bool reverse, bool noHidden)
|
||||
{
|
||||
Sstat theStat;
|
||||
if (Stat(path, &theStat) || !S_ISDIR(theStat.st_mode))
|
||||
return;
|
||||
|
||||
#if _WIN32
|
||||
SystemString wc(path);
|
||||
wc += _S("/*");
|
||||
WIN32_FIND_DATAW d;
|
||||
HANDLE dir = FindFirstFileW(wc.c_str(), &d);
|
||||
if (dir == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
switch (mode)
|
||||
{
|
||||
case Mode::Native:
|
||||
do
|
||||
{
|
||||
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _S('/');
|
||||
fp += d.cFileName;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st))
|
||||
continue;
|
||||
|
||||
size_t sz = 0;
|
||||
bool isDir = false;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
isDir = true;
|
||||
else if (S_ISREG(st.st_mode))
|
||||
sz = st.st_size;
|
||||
else
|
||||
continue;
|
||||
|
||||
m_entries.push_back(std::move(Entry(std::move(fp), d.cFileName, sz, isDir)));
|
||||
} while (FindNextFileW(dir, &d));
|
||||
break;
|
||||
case Mode::DirsThenFilesSorted:
|
||||
case Mode::DirsSorted:
|
||||
{
|
||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||
do
|
||||
{
|
||||
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp +=_S('/');
|
||||
fp += d.cFileName;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
sort.emplace(std::make_pair(d.cFileName, Entry(std::move(fp), d.cFileName, 0, true)));
|
||||
} while (FindNextFileW(dir, &d));
|
||||
|
||||
if (reverse)
|
||||
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
|
||||
m_entries.push_back(std::move(it->second));
|
||||
else
|
||||
for (auto& e : sort)
|
||||
m_entries.push_back(std::move(e.second));
|
||||
|
||||
if (mode == Mode::DirsSorted)
|
||||
break;
|
||||
FindClose(dir);
|
||||
dir = FindFirstFileW(wc.c_str(), &d);
|
||||
}
|
||||
case Mode::FilesSorted:
|
||||
{
|
||||
if (mode == Mode::FilesSorted)
|
||||
m_entries.clear();
|
||||
|
||||
if (sizeSort)
|
||||
{
|
||||
std::multimap<size_t, Entry> sort;
|
||||
do
|
||||
{
|
||||
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _S('/');
|
||||
fp += d.cFileName;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||
continue;
|
||||
sort.emplace(std::make_pair(st.st_size, Entry(std::move(fp), d.cFileName, st.st_size, false)));
|
||||
} while (FindNextFileW(dir, &d));
|
||||
|
||||
if (reverse)
|
||||
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
|
||||
m_entries.push_back(std::move(it->second));
|
||||
else
|
||||
for (auto& e : sort)
|
||||
m_entries.push_back(std::move(e.second));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||
do
|
||||
{
|
||||
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _S('/');
|
||||
fp += d.cFileName;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||
continue;
|
||||
sort.emplace(std::make_pair(d.cFileName, Entry(std::move(fp), d.cFileName, st.st_size, false)));
|
||||
} while (FindNextFileW(dir, &d));
|
||||
|
||||
if (reverse)
|
||||
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
|
||||
m_entries.push_back(std::move(it->second));
|
||||
else
|
||||
for (auto& e : sort)
|
||||
m_entries.push_back(std::move(e.second));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
FindClose(dir);
|
||||
|
||||
#else
|
||||
|
||||
DIR* dir = opendir(path);
|
||||
if (!dir)
|
||||
return;
|
||||
const dirent* d;
|
||||
switch (mode)
|
||||
{
|
||||
case Mode::Native:
|
||||
while ((d = readdir(dir)))
|
||||
{
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
if (noHidden && d->d_name[0] == '.')
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += '/';
|
||||
fp += d->d_name;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st))
|
||||
continue;
|
||||
|
||||
size_t sz = 0;
|
||||
bool isDir = false;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
isDir = true;
|
||||
else if (S_ISREG(st.st_mode))
|
||||
sz = st.st_size;
|
||||
else
|
||||
continue;
|
||||
|
||||
m_entries.push_back(std::move(Entry(std::move(fp), d->d_name, sz, isDir)));
|
||||
}
|
||||
break;
|
||||
case Mode::DirsThenFilesSorted:
|
||||
case Mode::DirsSorted:
|
||||
{
|
||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||
while ((d = readdir(dir)))
|
||||
{
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
if (noHidden && d->d_name[0] == '.')
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += '/';
|
||||
fp += d->d_name;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
sort.emplace(std::make_pair(d->d_name, Entry(std::move(fp), d->d_name, 0, true)));
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
|
||||
m_entries.push_back(std::move(it->second));
|
||||
else
|
||||
for (auto& e : sort)
|
||||
m_entries.push_back(std::move(e.second));
|
||||
|
||||
if (mode == Mode::DirsSorted)
|
||||
break;
|
||||
rewinddir(dir);
|
||||
}
|
||||
case Mode::FilesSorted:
|
||||
{
|
||||
if (mode == Mode::FilesSorted)
|
||||
m_entries.clear();
|
||||
|
||||
if (sizeSort)
|
||||
{
|
||||
std::multimap<size_t, Entry> sort;
|
||||
while ((d = readdir(dir)))
|
||||
{
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
if (noHidden && d->d_name[0] == '.')
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += '/';
|
||||
fp += d->d_name;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||
continue;
|
||||
sort.emplace(std::make_pair(st.st_size, Entry(std::move(fp), d->d_name, st.st_size, false)));
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
|
||||
m_entries.push_back(std::move(it->second));
|
||||
else
|
||||
for (auto& e : sort)
|
||||
m_entries.push_back(std::move(e.second));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||
while ((d = readdir(dir)))
|
||||
{
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
if (noHidden && d->d_name[0] == '.')
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += '/';
|
||||
fp += d->d_name;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||
continue;
|
||||
sort.emplace(std::make_pair(d->d_name, Entry(std::move(fp), d->d_name, st.st_size, false)));
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
|
||||
m_entries.push_back(std::move(it->second));
|
||||
else
|
||||
for (auto& e : sort)
|
||||
m_entries.push_back(std::move(e.second));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -72,15 +72,15 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||
m_midiSetup = it->second->data();
|
||||
|
||||
m_submix = m_engine.addSubmix(smx);
|
||||
m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f);
|
||||
m_submix->makeReverbHi(0.2f, 0.3f, 1.f, 0.5f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||
const SFXGroupIndex* sfxGroup, Submix* smx)
|
||||
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup)
|
||||
{
|
||||
m_submix = m_engine.addSubmix(smx);
|
||||
m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f);
|
||||
//m_submix = m_engine.addSubmix(smx);
|
||||
//m_submix->makeReverbHi(0.2f, 0.3f, 1.f, 0.5f, 0.f, 0.f);
|
||||
|
||||
std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX;
|
||||
for (const auto& sfx : sfxGroup->m_sfxEntries)
|
||||
|
||||
1530
lib/SongConverter.cpp
Normal file
1530
lib/SongConverter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -68,28 +68,17 @@ static uint32_t DecodeTimeRLE(const unsigned char*& data)
|
||||
|
||||
void SongState::Header::swapBig()
|
||||
{
|
||||
m_version = SBig(m_version);
|
||||
m_chanIdxOff = SBig(m_chanIdxOff);
|
||||
m_trackIdxOff = SBig(m_trackIdxOff);
|
||||
m_regionIdxOff = SBig(m_regionIdxOff);
|
||||
m_chanMapOff = SBig(m_chanMapOff);
|
||||
m_tempoTableOff = SBig(m_tempoTableOff);
|
||||
m_initialTempo = SBig(m_initialTempo);
|
||||
m_unkOff = SBig(m_unkOff);
|
||||
for (int i=0 ; i<64 ; ++i)
|
||||
m_chanOffs[i] = SBig(m_chanOffs[i]);
|
||||
}
|
||||
|
||||
void SongState::ChanHeader::swapBig()
|
||||
bool SongState::TrackRegion::indexValid(bool bigEndian) const
|
||||
{
|
||||
m_startTick = SBig(m_startTick);
|
||||
m_unk1 = SBig(m_unk1);
|
||||
m_unk2 = SBig(m_unk2);
|
||||
m_dataIndex = SBig(m_dataIndex);
|
||||
m_unk3 = SBig(m_unk3);
|
||||
m_startTick2 = SBig(m_startTick2);
|
||||
m_unk4 = SBig(m_unk4);
|
||||
m_unk5 = SBig(m_unk5);
|
||||
m_unk6 = SBig(m_unk6);
|
||||
m_unk7 = SBig(m_unk7);
|
||||
return (bigEndian ? SBig(m_regionIndex) : m_regionIndex) >= 0;
|
||||
}
|
||||
|
||||
void SongState::TempoChange::swapBig()
|
||||
@@ -98,57 +87,88 @@ void SongState::TempoChange::swapBig()
|
||||
m_tempo = SBig(m_tempo);
|
||||
}
|
||||
|
||||
void SongState::Channel::Header::swapBig()
|
||||
void SongState::Track::Header::swapBig()
|
||||
{
|
||||
m_type = SBig(m_type);
|
||||
m_pitchOff = SBig(m_pitchOff);
|
||||
m_modOff = SBig(m_modOff);
|
||||
}
|
||||
|
||||
SongState::Channel::Channel(SongState& parent, uint8_t midiChan, uint32_t startTick,
|
||||
const unsigned char* song, const unsigned char* chan)
|
||||
: m_parent(parent), m_midiChan(midiChan), m_startTick(startTick), m_dataBase(chan + 12)
|
||||
SongState::Track::Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions)
|
||||
: m_parent(parent), m_midiChan(midiChan), m_curRegion(nullptr), m_nextRegion(regions)
|
||||
{
|
||||
m_data = m_dataBase;
|
||||
for (int i=0 ; i<128 ; ++i)
|
||||
m_remNoteLengths[i] = INT_MIN;
|
||||
}
|
||||
|
||||
Header header = *reinterpret_cast<const Header*>(chan);
|
||||
header.swapBig();
|
||||
if (header.m_type != 8)
|
||||
{
|
||||
m_data = nullptr;
|
||||
return;
|
||||
}
|
||||
void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
||||
{
|
||||
m_curRegion = region;
|
||||
uint32_t regionIdx = (m_parent.m_bigEndian ? SBig(m_curRegion->m_regionIndex) :
|
||||
m_curRegion->m_regionIndex);
|
||||
m_nextRegion = &m_curRegion[1];
|
||||
|
||||
m_data = m_parent.m_songData + (m_parent.m_bigEndian ? SBig(m_parent.m_regionIdx[regionIdx]) :
|
||||
m_parent.m_regionIdx[regionIdx]);
|
||||
|
||||
Header header = *reinterpret_cast<const Header*>(m_data);
|
||||
if (m_parent.m_bigEndian)
|
||||
header.swapBig();
|
||||
m_data += 12;
|
||||
|
||||
if (header.m_pitchOff)
|
||||
m_pitchWheelData = song + header.m_pitchOff;
|
||||
m_pitchWheelData = m_parent.m_songData + header.m_pitchOff;
|
||||
if (header.m_modOff)
|
||||
m_modWheelData = song + header.m_modOff;
|
||||
m_modWheelData = m_parent.m_songData + header.m_modOff;
|
||||
|
||||
m_waitCountdown = startTick;
|
||||
m_lastPitchTick = startTick;
|
||||
m_lastModTick = startTick;
|
||||
m_waitCountdown += int32_t(DecodeTimeRLE(m_data));
|
||||
m_eventWaitCountdown = 0;
|
||||
m_lastPitchTick = m_parent.m_curTick;
|
||||
m_lastPitchVal = 0;
|
||||
m_lastModTick = m_parent.m_curTick;
|
||||
m_lastModVal = 0;
|
||||
if (seq)
|
||||
{
|
||||
seq->setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f));
|
||||
seq->setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||
}
|
||||
if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58)
|
||||
m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data));
|
||||
else
|
||||
{
|
||||
int32_t absTick = (m_parent.m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data)) :
|
||||
*reinterpret_cast<const int32_t*>(m_data));
|
||||
m_eventWaitCountdown = absTick;
|
||||
m_lastN64EventTick = absTick;
|
||||
m_data += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void SongState::Track::advanceRegion(Sequencer* seq)
|
||||
{
|
||||
setRegion(seq, m_nextRegion);
|
||||
}
|
||||
|
||||
void SongState::initialize(const unsigned char* ptr)
|
||||
{
|
||||
m_bigEndian = ptr[0] == 0;
|
||||
m_songData = ptr;
|
||||
m_header = *reinterpret_cast<const Header*>(ptr);
|
||||
m_header.swapBig();
|
||||
if (m_bigEndian)
|
||||
m_header.swapBig();
|
||||
const uint32_t* trackIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_trackIdxOff);
|
||||
m_regionIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_regionIdxOff);
|
||||
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
|
||||
|
||||
/* Initialize all channels */
|
||||
/* Initialize all tracks */
|
||||
for (int i=0 ; i<64 ; ++i)
|
||||
{
|
||||
if (m_header.m_chanOffs[i])
|
||||
if (trackIdx[i])
|
||||
{
|
||||
ChanHeader cHeader = *reinterpret_cast<const ChanHeader*>(ptr + m_header.m_chanOffs[i]);
|
||||
cHeader.swapBig();
|
||||
const uint32_t* chanIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_chanIdxOff);
|
||||
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
|
||||
m_channels[i].emplace(*this, chanMap[i], cHeader.m_startTick, ptr,
|
||||
ptr + SBig(chanIdx[cHeader.m_dataIndex]));
|
||||
const TrackRegion* region = reinterpret_cast<const TrackRegion*>(ptr + (m_bigEndian ? SBig(trackIdx[i]) : trackIdx[i]));
|
||||
m_tracks[i].emplace(*this, chanMap[i], region);
|
||||
}
|
||||
else
|
||||
m_channels[i] = std::experimental::nullopt;
|
||||
m_tracks[i] = std::experimental::nullopt;
|
||||
}
|
||||
|
||||
/* Initialize tempo */
|
||||
@@ -162,13 +182,38 @@ void SongState::initialize(const unsigned char* ptr)
|
||||
m_songState = SongPlayState::Playing;
|
||||
}
|
||||
|
||||
bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||
bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
|
||||
{
|
||||
if (!m_data)
|
||||
return true;
|
||||
|
||||
int32_t endTick = m_parent.m_curTick + ticks;
|
||||
|
||||
/* Advance region if needed */
|
||||
while (m_nextRegion->indexValid(m_parent.m_bigEndian))
|
||||
{
|
||||
uint32_t nextRegTick = (m_parent.m_bigEndian ? SBig(m_nextRegion->m_startTick) :
|
||||
m_nextRegion->m_startTick);
|
||||
if (endTick > nextRegTick)
|
||||
advanceRegion(&seq);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop finished notes */
|
||||
for (int i=0 ; i<128 ; ++i)
|
||||
{
|
||||
if (m_remNoteLengths[i] != INT_MIN)
|
||||
{
|
||||
m_remNoteLengths[i] -= ticks;
|
||||
if (m_remNoteLengths[i] <= 0)
|
||||
{
|
||||
seq.keyOff(m_midiChan, i, 0);
|
||||
m_remNoteLengths[i] = INT_MIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_data)
|
||||
return !m_nextRegion->indexValid(m_parent.m_bigEndian);
|
||||
|
||||
/* Update continuous pitch data */
|
||||
if (m_pitchWheelData)
|
||||
{
|
||||
@@ -224,7 +269,7 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||
m_lastModTick = nextTick;
|
||||
remModTicks -= (nextTick - modTick);
|
||||
modTick = nextTick;
|
||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, (m_lastModVal + 8192) * 128 / 16384, 127));
|
||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||
continue;
|
||||
}
|
||||
remModTicks -= (nextTick - modTick);
|
||||
@@ -235,61 +280,115 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop finished notes */
|
||||
for (int i=0 ; i<128 ; ++i)
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58)
|
||||
{
|
||||
if (m_remNoteLengths[i])
|
||||
/* GameCube */
|
||||
while (true)
|
||||
{
|
||||
if (m_remNoteLengths[i] <= ticks)
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
seq.keyOff(m_midiChan, i, 0);
|
||||
m_remNoteLengths[i] = 0;
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(m_data) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return !m_nextRegion->indexValid(m_parent.m_bigEndian);
|
||||
}
|
||||
else if (m_data[0] & 0x80 && m_data[1] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[0] & 0x7f;
|
||||
uint8_t ctrl = m_data[1] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
m_data += 2;
|
||||
}
|
||||
else if (m_data[0] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[0] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
m_data += 2;
|
||||
}
|
||||
else
|
||||
m_remNoteLengths[i] -= ticks;
|
||||
{
|
||||
/* Note */
|
||||
uint8_t note = m_data[0] & 0x7f;
|
||||
uint8_t vel = m_data[1] & 0x7f;
|
||||
uint16_t length = (m_parent.m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data + 2)) :
|
||||
*reinterpret_cast<const uint16_t*>(m_data + 2));
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
m_remNoteLengths[note] = length;
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
m_eventWaitCountdown += int32_t(DecodeTimeRLE(m_data));
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
while (true)
|
||||
else
|
||||
{
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_waitCountdown)
|
||||
/* N64 */
|
||||
while (true)
|
||||
{
|
||||
m_waitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_waitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(m_data) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return true;
|
||||
}
|
||||
else if (m_data[0] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[0] & 0x7f;
|
||||
uint8_t ctrl = m_data[1] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
m_data += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note */
|
||||
uint8_t note = m_data[0] & 0x7f;
|
||||
uint8_t vel = m_data[1] & 0x7f;
|
||||
uint16_t length = SBig(*reinterpret_cast<const uint16_t*>(m_data + 2));
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
m_remNoteLengths[note] = length;
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(&m_data[2]) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return !m_nextRegion->indexValid(m_parent.m_bigEndian);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_data[2] & 0x80) != 0x80)
|
||||
{
|
||||
/* Note */
|
||||
uint16_t length = (m_parent.m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data)) :
|
||||
*reinterpret_cast<const uint16_t*>(m_data));
|
||||
uint8_t note = m_data[2] & 0x7f;
|
||||
uint8_t vel = m_data[3] & 0x7f;
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
m_remNoteLengths[note] = length;
|
||||
}
|
||||
else if (m_data[2] & 0x80 && m_data[3] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[2] & 0x7f;
|
||||
uint8_t ctrl = m_data[3] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
}
|
||||
else if (m_data[2] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[2] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
}
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
int32_t absTick = (m_parent.m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data)) :
|
||||
*reinterpret_cast<const int32_t*>(m_data));
|
||||
m_eventWaitCountdown += absTick - m_lastN64EventTick;
|
||||
m_lastN64EventTick = absTick;
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
m_waitCountdown += int32_t(DecodeTimeRLE(m_data));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -317,7 +416,8 @@ bool SongState::advance(Sequencer& seq, double dt)
|
||||
if (m_tempoPtr && m_tempoPtr->m_tick != 0xffffffff)
|
||||
{
|
||||
TempoChange change = *m_tempoPtr;
|
||||
change.swapBig();
|
||||
if (m_bigEndian)
|
||||
change.swapBig();
|
||||
|
||||
if (m_curTick + remTicks > change.m_tick)
|
||||
remTicks = change.m_tick - m_curTick;
|
||||
@@ -332,10 +432,10 @@ bool SongState::advance(Sequencer& seq, double dt)
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance all channels */
|
||||
for (std::experimental::optional<Channel>& chan : m_channels)
|
||||
if (chan)
|
||||
done &= chan->advance(seq, remTicks);
|
||||
/* Advance all tracks */
|
||||
for (std::experimental::optional<Track>& trk : m_tracks)
|
||||
if (trk)
|
||||
done &= trk->advance(seq, remTicks);
|
||||
|
||||
m_curTick += remTicks;
|
||||
|
||||
|
||||
@@ -52,21 +52,21 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
|
||||
break;
|
||||
case 129:
|
||||
/* Aftertouch */
|
||||
thisValue = vox.getAftertouch();
|
||||
thisValue = vox.getAftertouch() * (2.f / 127.f);
|
||||
break;
|
||||
case 130:
|
||||
/* LFO1 */
|
||||
if (vox.m_lfoPeriods[0])
|
||||
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF) / 2.f + 1.f) * 127.f;
|
||||
thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF);
|
||||
break;
|
||||
case 131:
|
||||
/* LFO2 */
|
||||
if (vox.m_lfoPeriods[1])
|
||||
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF) / 2.f + 1.f) * 127.f;
|
||||
thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF);
|
||||
break;
|
||||
case 132:
|
||||
/* Surround panning */
|
||||
thisValue = vox.m_curSpan * 64.f + 64.f;
|
||||
thisValue = vox.m_curSpan;
|
||||
break;
|
||||
case 133:
|
||||
/* Macro-starting key */
|
||||
@@ -78,10 +78,13 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
|
||||
break;
|
||||
case 135:
|
||||
/* Time since macro-start (ms) */
|
||||
thisValue = st.m_execTime * 1000.f;
|
||||
thisValue = clamp(0.f, float(st.m_execTime * 1000.f), 16383.f);
|
||||
break;
|
||||
default:
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl);
|
||||
if (comp.m_midiCtrl == 10) /* Centered pan computation */
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f) - 1.f;
|
||||
else
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,12 +253,12 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||
}
|
||||
|
||||
/* Dynamically evaluate per-sample SoundMacro parameters */
|
||||
float evalVol = m_state.m_volumeSel ? ((m_state.m_volumeSel.evaluate(*this, m_state) / 127.f) * m_curVol) : m_curVol;
|
||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(*this, m_state) / 2.f * m_curVol) : m_curVol;
|
||||
|
||||
bool panDirty = false;
|
||||
if (m_state.m_panSel)
|
||||
{
|
||||
float evalPan = (m_state.m_panSel.evaluate(*this, m_state) - 64.f) / 64.f;
|
||||
float evalPan = m_state.m_panSel.evaluate(*this, m_state);
|
||||
if (evalPan != m_curPan)
|
||||
{
|
||||
m_curPan = evalPan;
|
||||
@@ -267,7 +267,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||
}
|
||||
if (m_state.m_spanSel)
|
||||
{
|
||||
float evalSpan = (m_state.m_spanSel.evaluate(*this, m_state) - 64.f) / 64.f;
|
||||
float evalSpan = m_state.m_spanSel.evaluate(*this, m_state);
|
||||
if (evalSpan != m_curSpan)
|
||||
{
|
||||
m_curSpan = evalSpan;
|
||||
@@ -276,7 +276,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||
}
|
||||
if (m_state.m_reverbSel)
|
||||
{
|
||||
float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 127.f;
|
||||
float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 2.f;
|
||||
if (evalRev != m_curReverbVol)
|
||||
{
|
||||
m_curReverbVol = evalRev;
|
||||
@@ -287,7 +287,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||
_setPan(m_curPan);
|
||||
|
||||
if (m_state.m_pitchWheelSel)
|
||||
setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state) / 127.f);
|
||||
_setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state));
|
||||
|
||||
/* Process user volume slew */
|
||||
if (m_engine.m_ampMode == AmplitudeMode::PerSample)
|
||||
@@ -326,11 +326,11 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||
/* Apply tremolo */
|
||||
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
|
||||
{
|
||||
float t = m_state.m_tremoloSel.evaluate(*this, m_state);
|
||||
float t = m_state.m_tremoloSel.evaluate(*this, m_state) / 2.f;
|
||||
if (m_tremoloScale && m_tremoloModScale)
|
||||
{
|
||||
float fac = (1.0f - t) + (m_tremoloScale * t);
|
||||
float modT = getModWheel() / 127.f;
|
||||
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(*this, m_state) / 2.f) : (getCtrlValue(1) / 127.f);
|
||||
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
||||
m_nextLevel *= fac * modFac;
|
||||
}
|
||||
@@ -341,7 +341,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||
}
|
||||
else if (m_tremoloModScale)
|
||||
{
|
||||
float modT = getModWheel() / 127.f;
|
||||
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(*this, m_state) / 2.f) : (getCtrlValue(1) / 127.f);
|
||||
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
||||
m_nextLevel *= modFac;
|
||||
}
|
||||
@@ -451,7 +451,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||
/* Process per-block evaluators here */
|
||||
if (m_state.m_pedalSel)
|
||||
{
|
||||
bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 64;
|
||||
bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 1.f;
|
||||
if (pedal != m_sustained)
|
||||
setPedal(pedal);
|
||||
}
|
||||
@@ -793,7 +793,7 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
||||
m_sampleRate = m_curSample->first.m_sampleRate;
|
||||
m_curPitch = m_curSample->first.m_pitch;
|
||||
m_pitchDirty = true;
|
||||
setPitchWheel(m_curPitchWheel);
|
||||
_setPitchWheel(m_curPitchWheel);
|
||||
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
||||
|
||||
int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff;
|
||||
@@ -1063,9 +1063,8 @@ void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
||||
}
|
||||
}
|
||||
|
||||
void Voice::setPitchWheel(float pitchWheel)
|
||||
void Voice::_setPitchWheel(float pitchWheel)
|
||||
{
|
||||
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
|
||||
if (pitchWheel > 0.f)
|
||||
m_pitchWheelVal = m_pitchWheelUp * m_curPitchWheel;
|
||||
else if (pitchWheel < 0.f)
|
||||
@@ -1073,6 +1072,12 @@ void Voice::setPitchWheel(float pitchWheel)
|
||||
else
|
||||
m_pitchWheelVal = 0;
|
||||
m_pitchDirty = true;
|
||||
}
|
||||
|
||||
void Voice::setPitchWheel(float pitchWheel)
|
||||
{
|
||||
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
|
||||
_setPitchWheel(m_curPitchWheel);
|
||||
|
||||
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||
vox->setPitchWheel(pitchWheel);
|
||||
@@ -1082,7 +1087,7 @@ void Voice::setPitchWheelRange(int8_t up, int8_t down)
|
||||
{
|
||||
m_pitchWheelUp = up * 100;
|
||||
m_pitchWheelDown = down * 100;
|
||||
setPitchWheel(m_curPitchWheel);
|
||||
_setPitchWheel(m_curPitchWheel);
|
||||
}
|
||||
|
||||
void Voice::setAftertouch(uint8_t aftertouch)
|
||||
@@ -1105,7 +1110,7 @@ bool Voice::doPortamento(uint8_t newNote)
|
||||
pState = true;
|
||||
break;
|
||||
case 2:
|
||||
pState = (m_state.m_portamentoSel ? m_state.m_portamentoSel.evaluate(*this, m_state) : getCtrlValue(65)) >= 64;
|
||||
pState = m_state.m_portamentoSel ? (m_state.m_portamentoSel.evaluate(*this, m_state) >= 1.f) : (getCtrlValue(65) >= 64);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user