Updated libopenmpt to version 0.5.3
parent
4eb7a3eebf
commit
ee0782efbf
|
@ -1,4 +1,4 @@
|
||||||
|
|
||||||
MPT_SVNVERSION=13555
|
MPT_SVNVERSION=13775
|
||||||
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.2
|
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.3
|
||||||
MPT_SVNDATE=2020-08-30T13:42:32.941871Z
|
MPT_SVNDATE=2020-10-25T14:02:16.624929Z
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#define OPENMPT_VERSION_SVNVERSION "13555"
|
#define OPENMPT_VERSION_SVNVERSION "13775"
|
||||||
#define OPENMPT_VERSION_REVISION 13555
|
#define OPENMPT_VERSION_REVISION 13775
|
||||||
#define OPENMPT_VERSION_DIRTY 0
|
#define OPENMPT_VERSION_DIRTY 0
|
||||||
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
||||||
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.2"
|
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.3"
|
||||||
#define OPENMPT_VERSION_DATE "2020-08-30T13:42:32.941871Z"
|
#define OPENMPT_VERSION_DATE "2020-10-25T14:02:16.624929Z"
|
||||||
#define OPENMPT_VERSION_IS_PACKAGE 1
|
#define OPENMPT_VERSION_IS_PACKAGE 1
|
||||||
|
|
||||||
|
|
|
@ -563,10 +563,7 @@ namespace FileReader
|
||||||
static_assert(mpt::is_binary_safe<T>::value);
|
static_assert(mpt::is_binary_safe<T>::value);
|
||||||
if(f.CanRead(sizeof(destArray)))
|
if(f.CanRead(sizeof(destArray)))
|
||||||
{
|
{
|
||||||
for(auto &element : destArray)
|
f.ReadRaw(mpt::as_raw_memory(destArray));
|
||||||
{
|
|
||||||
Read(f, element);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
|
@ -584,10 +581,7 @@ namespace FileReader
|
||||||
static_assert(mpt::is_binary_safe<T>::value);
|
static_assert(mpt::is_binary_safe<T>::value);
|
||||||
if(f.CanRead(sizeof(destArray)))
|
if(f.CanRead(sizeof(destArray)))
|
||||||
{
|
{
|
||||||
for(auto &element : destArray)
|
f.ReadRaw(mpt::as_raw_memory(destArray));
|
||||||
{
|
|
||||||
Read(f, element);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
|
@ -606,10 +600,7 @@ namespace FileReader
|
||||||
destVector.resize(destSize);
|
destVector.resize(destSize);
|
||||||
if(f.CanRead(sizeof(T) * destSize))
|
if(f.CanRead(sizeof(T) * destSize))
|
||||||
{
|
{
|
||||||
for(auto &element : destVector)
|
f.ReadRaw(mpt::as_raw_memory(destVector));
|
||||||
{
|
|
||||||
Read(f, element);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
|
@ -679,18 +670,9 @@ namespace FileReader
|
||||||
typename TFileCursor::off_t avail = f.GetRaw(bytes, sizeof(bytes));
|
typename TFileCursor::off_t avail = f.GetRaw(bytes, sizeof(bytes));
|
||||||
typename TFileCursor::off_t readPos = 1;
|
typename TFileCursor::off_t readPos = 1;
|
||||||
|
|
||||||
size_t writtenBits = 0;
|
|
||||||
uint8 b = mpt::byte_cast<uint8>(bytes[0]);
|
uint8 b = mpt::byte_cast<uint8>(bytes[0]);
|
||||||
target = (b & 0x7F);
|
target = (b & 0x7F);
|
||||||
|
size_t writtenBits = static_cast<size_t>(mpt::bit_width(target)); // Bits used in the most significant byte
|
||||||
// Count actual bits used in most significant byte (i.e. this one)
|
|
||||||
for(size_t bit = 0; bit < 7; bit++)
|
|
||||||
{
|
|
||||||
if((b & (1u << bit)) != 0)
|
|
||||||
{
|
|
||||||
writtenBits = bit + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(readPos < avail && (b & 0x80) != 0)
|
while(readPos < avail && (b & 0x80) != 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -388,8 +388,11 @@ void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite)
|
||||||
{
|
{
|
||||||
const Offtype nRawEntrySize = oStrm.tellp() - posBeforeWrite;
|
const Offtype nRawEntrySize = oStrm.tellp() - posBeforeWrite;
|
||||||
|
|
||||||
if (nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > std::numeric_limits<DataSize>::max())
|
MPT_MAYBE_CONSTANT_IF(nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > std::numeric_limits<DataSize>::max())
|
||||||
{ AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE); return; }
|
{
|
||||||
|
AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(GetFlag(RwfRMapHasSize) && (nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > (std::numeric_limits<DataSize>::max() >> 2)))
|
if(GetFlag(RwfRMapHasSize) && (nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > (std::numeric_limits<DataSize>::max() >> 2)))
|
||||||
{ AddWriteNote(SNW_DATASIZETYPE_OVERFLOW); return; }
|
{ AddWriteNote(SNW_DATASIZETYPE_OVERFLOW); return; }
|
||||||
|
@ -557,8 +560,11 @@ void SsbRead::BeginRead(const ID &id, const uint64& nVersion)
|
||||||
|
|
||||||
const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart;
|
const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart;
|
||||||
|
|
||||||
if (rawEndOfHdrData < 0 || static_cast<uint64>(rawEndOfHdrData) > std::numeric_limits<RposType>::max())
|
MPT_MAYBE_CONSTANT_IF(rawEndOfHdrData < 0 || static_cast<uint64>(rawEndOfHdrData) > std::numeric_limits<RposType>::max())
|
||||||
{ AddReadNote(SNR_INSUFFICIENT_RPOSTYPE); return; }
|
{
|
||||||
|
AddReadNote(SNR_INSUFFICIENT_RPOSTYPE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_rposEndofHdrData = static_cast<RposType>(rawEndOfHdrData);
|
m_rposEndofHdrData = static_cast<RposType>(rawEndOfHdrData);
|
||||||
m_rposMapBegin = (GetFlag(RwfRwHasMap)) ? static_cast<RposType>(tempU64) : m_rposEndofHdrData;
|
m_rposMapBegin = (GetFlag(RwfRwHasMap)) ? static_cast<RposType>(tempU64) : m_rposEndofHdrData;
|
||||||
|
|
|
@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN
|
||||||
// Version definitions. The only thing that needs to be changed when changing version number.
|
// Version definitions. The only thing that needs to be changed when changing version number.
|
||||||
#define VER_MAJORMAJOR 1
|
#define VER_MAJORMAJOR 1
|
||||||
#define VER_MAJOR 29
|
#define VER_MAJOR 29
|
||||||
#define VER_MINOR 03
|
#define VER_MINOR 05
|
||||||
#define VER_MINORMINOR 00
|
#define VER_MINORMINOR 00
|
||||||
|
|
||||||
OPENMPT_NAMESPACE_END
|
OPENMPT_NAMESPACE_END
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
cd "${0%/*}"
|
cd "${0%/*}"
|
||||||
|
|
||||||
AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/vanhauser-thc/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')"
|
AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')"
|
||||||
AFL_FILENAME="$AFL_VERSION.tar.gz"
|
AFL_FILENAME="$AFL_VERSION.tar.gz"
|
||||||
AFL_URL="https://github.com/vanhauser-thc/AFLplusplus/archive/$AFL_FILENAME"
|
AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME"
|
||||||
|
|
||||||
rm $AFL_FILENAME
|
rm $AFL_FILENAME
|
||||||
wget $AFL_URL || exit
|
wget $AFL_URL || exit
|
||||||
|
|
|
@ -5,6 +5,31 @@ Changelog {#changelog}
|
||||||
For fully detailed change log, please see the source repository directly. This
|
For fully detailed change log, please see the source repository directly. This
|
||||||
is just a high-level summary.
|
is just a high-level summary.
|
||||||
|
|
||||||
|
### libopenmpt 0.5.3 (2020-10-25)
|
||||||
|
|
||||||
|
* [**Sec**] Possible hang if a MED file claimed to contain 256 songs. (r13704)
|
||||||
|
|
||||||
|
* [**Bug**] libopenmpt: `openmpt::is_extension_supported2()` exported symbol
|
||||||
|
was missing (C++).
|
||||||
|
* [**Bug**] `openmpt::module::set_position_seconds` sometimes behaved as if
|
||||||
|
the song end was reached when seeking into a pattern loop and in some other
|
||||||
|
corner cases.
|
||||||
|
|
||||||
|
* Increase threshold for ignoring panning commands from 820 to 830.
|
||||||
|
* Subsong names now fall back to the first pattern's name if empty.
|
||||||
|
* MO3: Avoid certain ModPlug hacks from being fixed up twice, which could lead
|
||||||
|
to e.g. very narrow pan swing range for old OpenMPT IT files saved with a
|
||||||
|
recent MO3 encoder version.
|
||||||
|
* MO3: Some files with corrupted envelope data could be rejected completely
|
||||||
|
(normally libopenmpt should fix up the envelope data).
|
||||||
|
* MO3: Song metadata didn't correctly identify MPTM as source format (it
|
||||||
|
appeared as IT instead).
|
||||||
|
* STM: Change tempo computation to behave like Scream Tracker 2.3 instead of
|
||||||
|
Scream Tracker 2.2, as the playback frequencies we use for sample playback
|
||||||
|
are closer to those of Scream Tracker 2.3.
|
||||||
|
* PLM: Percentage offset (Mxx) was slightly off.
|
||||||
|
* WOW: Fix loading of several files and harden WOW detection.
|
||||||
|
|
||||||
### libopenmpt 0.5.2 (2020-08-30)
|
### libopenmpt 0.5.2 (2020-08-30)
|
||||||
|
|
||||||
* [**Change**] `Makefile` `CONFIG=emscripten` now supports
|
* [**Change**] `Makefile` `CONFIG=emscripten` now supports
|
||||||
|
|
|
@ -1567,7 +1567,7 @@ static int get_pattern_row_channel_effect_type( openmpt_module_ext * mod_ext, in
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int set_current_speed( openmpt_module_ext * mod_ext, int32_t speed ) {
|
static int set_current_speed( openmpt_module_ext * mod_ext, int32_t speed ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_current_speed( speed );
|
mod_ext->impl->set_current_speed( speed );
|
||||||
|
@ -1577,7 +1577,7 @@ int set_current_speed( openmpt_module_ext * mod_ext, int32_t speed ) {
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int set_current_tempo( openmpt_module_ext * mod_ext, int32_t tempo ) {
|
static int set_current_tempo( openmpt_module_ext * mod_ext, int32_t tempo ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_current_tempo( tempo );
|
mod_ext->impl->set_current_tempo( tempo );
|
||||||
|
@ -1587,7 +1587,7 @@ int set_current_tempo( openmpt_module_ext * mod_ext, int32_t tempo ) {
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int set_tempo_factor( openmpt_module_ext * mod_ext, double factor ) {
|
static int set_tempo_factor( openmpt_module_ext * mod_ext, double factor ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_tempo_factor( factor );
|
mod_ext->impl->set_tempo_factor( factor );
|
||||||
|
@ -1597,7 +1597,7 @@ int set_tempo_factor( openmpt_module_ext * mod_ext, double factor ) {
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
double get_tempo_factor( openmpt_module_ext * mod_ext ) {
|
static double get_tempo_factor( openmpt_module_ext * mod_ext ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
return mod_ext->impl->get_tempo_factor();
|
return mod_ext->impl->get_tempo_factor();
|
||||||
|
@ -1606,7 +1606,7 @@ double get_tempo_factor( openmpt_module_ext * mod_ext ) {
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
int set_pitch_factor( openmpt_module_ext * mod_ext, double factor ) {
|
static int set_pitch_factor( openmpt_module_ext * mod_ext, double factor ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_pitch_factor( factor );
|
mod_ext->impl->set_pitch_factor( factor );
|
||||||
|
@ -1616,7 +1616,7 @@ int set_pitch_factor( openmpt_module_ext * mod_ext, double factor ) {
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
double get_pitch_factor( openmpt_module_ext * mod_ext ) {
|
static double get_pitch_factor( openmpt_module_ext * mod_ext ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
return mod_ext->impl->get_pitch_factor();
|
return mod_ext->impl->get_pitch_factor();
|
||||||
|
@ -1625,7 +1625,7 @@ double get_pitch_factor( openmpt_module_ext * mod_ext ) {
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
int set_global_volume( openmpt_module_ext * mod_ext, double volume ) {
|
static int set_global_volume( openmpt_module_ext * mod_ext, double volume ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_global_volume( volume );
|
mod_ext->impl->set_global_volume( volume );
|
||||||
|
@ -1635,7 +1635,7 @@ int set_global_volume( openmpt_module_ext * mod_ext, double volume ) {
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
double get_global_volume( openmpt_module_ext * mod_ext ) {
|
static double get_global_volume( openmpt_module_ext * mod_ext ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
return mod_ext->impl->get_global_volume();
|
return mod_ext->impl->get_global_volume();
|
||||||
|
@ -1644,7 +1644,7 @@ double get_global_volume( openmpt_module_ext * mod_ext ) {
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
int set_channel_volume( openmpt_module_ext * mod_ext, int32_t channel, double volume ) {
|
static int set_channel_volume( openmpt_module_ext * mod_ext, int32_t channel, double volume ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_channel_volume( channel, volume );
|
mod_ext->impl->set_channel_volume( channel, volume );
|
||||||
|
@ -1654,7 +1654,7 @@ int set_channel_volume( openmpt_module_ext * mod_ext, int32_t channel, double vo
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
double get_channel_volume( openmpt_module_ext * mod_ext, int32_t channel ) {
|
static double get_channel_volume( openmpt_module_ext * mod_ext, int32_t channel ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
return mod_ext->impl->get_channel_volume( channel );
|
return mod_ext->impl->get_channel_volume( channel );
|
||||||
|
@ -1663,7 +1663,7 @@ double get_channel_volume( openmpt_module_ext * mod_ext, int32_t channel ) {
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
int set_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel, int mute ) {
|
static int set_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel, int mute ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_channel_mute_status( channel, mute ? true : false );
|
mod_ext->impl->set_channel_mute_status( channel, mute ? true : false );
|
||||||
|
@ -1673,7 +1673,7 @@ int set_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel, int
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int get_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel ) {
|
static int get_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
return mod_ext->impl->get_channel_mute_status( channel ) ? 1 : 0;
|
return mod_ext->impl->get_channel_mute_status( channel ) ? 1 : 0;
|
||||||
|
@ -1682,7 +1682,7 @@ int get_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel ) {
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int set_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument, int mute ) {
|
static int set_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument, int mute ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->set_instrument_mute_status( instrument, mute ? true : false );
|
mod_ext->impl->set_instrument_mute_status( instrument, mute ? true : false );
|
||||||
|
@ -1692,7 +1692,7 @@ int set_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int get_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument ) {
|
static int get_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
return mod_ext->impl->get_instrument_mute_status( instrument ) ? 1 : 0;
|
return mod_ext->impl->get_instrument_mute_status( instrument ) ? 1 : 0;
|
||||||
|
@ -1701,7 +1701,7 @@ int get_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int32_t play_note( openmpt_module_ext * mod_ext, int32_t instrument, int32_t note, double volume, double panning ) {
|
static int32_t play_note( openmpt_module_ext * mod_ext, int32_t instrument, int32_t note, double volume, double panning ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
return mod_ext->impl->play_note( instrument, note, volume, panning );
|
return mod_ext->impl->play_note( instrument, note, volume, panning );
|
||||||
|
@ -1710,7 +1710,7 @@ int32_t play_note( openmpt_module_ext * mod_ext, int32_t instrument, int32_t not
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int stop_note( openmpt_module_ext * mod_ext, int32_t channel ) {
|
static int stop_note( openmpt_module_ext * mod_ext, int32_t channel ) {
|
||||||
try {
|
try {
|
||||||
openmpt::interface::check_soundfile( mod_ext );
|
openmpt::interface::check_soundfile( mod_ext );
|
||||||
mod_ext->impl->stop_note( channel );
|
mod_ext->impl->stop_note( channel );
|
||||||
|
|
|
@ -126,7 +126,7 @@ std::vector<std::string> get_supported_extensions() {
|
||||||
bool is_extension_supported( const std::string & extension ) {
|
bool is_extension_supported( const std::string & extension ) {
|
||||||
return openmpt::module_impl::is_extension_supported( extension );
|
return openmpt::module_impl::is_extension_supported( extension );
|
||||||
}
|
}
|
||||||
bool is_extension_supported( std::string_view extension ) {
|
bool is_extension_supported2( std::string_view extension ) {
|
||||||
return openmpt::module_impl::is_extension_supported( extension );
|
return openmpt::module_impl::is_extension_supported( extension );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1106,11 +1106,11 @@ double module_impl::set_position_seconds( double seconds ) {
|
||||||
} else {
|
} else {
|
||||||
subsong = &subsongs[m_current_subsong];
|
subsong = &subsongs[m_current_subsong];
|
||||||
}
|
}
|
||||||
GetLengthType t = m_sndFile->GetLength( eNoAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back();
|
GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back();
|
||||||
m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder;
|
m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder;
|
||||||
m_sndFile->SetCurrentOrder( t.lastOrder );
|
m_sndFile->SetCurrentOrder( t.lastOrder );
|
||||||
m_sndFile->m_PlayState.m_nNextRow = t.lastRow;
|
m_sndFile->m_PlayState.m_nNextRow = t.lastRow;
|
||||||
m_currentPositionSeconds = base_seconds + m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( t.lastOrder, t.lastRow ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back().duration;
|
m_currentPositionSeconds = base_seconds + t.duration;
|
||||||
return m_currentPositionSeconds;
|
return m_currentPositionSeconds;
|
||||||
}
|
}
|
||||||
double module_impl::set_position_order_row( std::int32_t order, std::int32_t row ) {
|
double module_impl::set_position_order_row( std::int32_t order, std::int32_t row ) {
|
||||||
|
@ -1367,8 +1367,15 @@ std::vector<std::string> module_impl::get_subsong_names() const {
|
||||||
std::vector<std::string> retval;
|
std::vector<std::string> retval;
|
||||||
std::unique_ptr<subsongs_type> subsongs_temp = has_subsongs_inited() ? std::unique_ptr<subsongs_type>() : std::make_unique<subsongs_type>( get_subsongs() );
|
std::unique_ptr<subsongs_type> subsongs_temp = has_subsongs_inited() ? std::unique_ptr<subsongs_type>() : std::make_unique<subsongs_type>( get_subsongs() );
|
||||||
const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp;
|
const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp;
|
||||||
|
retval.reserve( subsongs.size() );
|
||||||
for ( const auto & subsong : subsongs ) {
|
for ( const auto & subsong : subsongs ) {
|
||||||
retval.push_back( mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->Order( static_cast<SEQUENCEINDEX>( subsong.sequence ) ).GetName() ) );
|
const auto & order = m_sndFile->Order( static_cast<SEQUENCEINDEX>( subsong.sequence ) );
|
||||||
|
retval.push_back( mpt::ToCharset( mpt::Charset::UTF8, order.GetName() ) );
|
||||||
|
if ( retval.back().empty() ) {
|
||||||
|
// use first pattern name instead
|
||||||
|
if ( order.IsValidPat( static_cast<SEQUENCEINDEX>( subsong.start_order ) ) )
|
||||||
|
retval.back() = mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetCharsetInternal(), m_sndFile->Patterns[ order[ subsong.start_order ] ].GetName() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
/*! \brief libopenmpt minor version number */
|
/*! \brief libopenmpt minor version number */
|
||||||
#define OPENMPT_API_VERSION_MINOR 5
|
#define OPENMPT_API_VERSION_MINOR 5
|
||||||
/*! \brief libopenmpt patch version number */
|
/*! \brief libopenmpt patch version number */
|
||||||
#define OPENMPT_API_VERSION_PATCH 2
|
#define OPENMPT_API_VERSION_PATCH 3
|
||||||
/*! \brief libopenmpt pre-release tag */
|
/*! \brief libopenmpt pre-release tag */
|
||||||
#define OPENMPT_API_VERSION_PREREL ""
|
#define OPENMPT_API_VERSION_PREREL ""
|
||||||
/*! \brief libopenmpt pre-release flag */
|
/*! \brief libopenmpt pre-release flag */
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
LIBOPENMPT_VERSION_MAJOR=0
|
LIBOPENMPT_VERSION_MAJOR=0
|
||||||
LIBOPENMPT_VERSION_MINOR=5
|
LIBOPENMPT_VERSION_MINOR=5
|
||||||
LIBOPENMPT_VERSION_PATCH=2
|
LIBOPENMPT_VERSION_PATCH=3
|
||||||
LIBOPENMPT_VERSION_PREREL=
|
LIBOPENMPT_VERSION_PREREL=
|
||||||
|
|
||||||
LIBOPENMPT_LTVER_CURRENT=2
|
LIBOPENMPT_LTVER_CURRENT=2
|
||||||
LIBOPENMPT_LTVER_REVISION=2
|
LIBOPENMPT_LTVER_REVISION=3
|
||||||
LIBOPENMPT_LTVER_AGE=2
|
LIBOPENMPT_LTVER_AGE=2
|
||||||
|
|
|
@ -190,7 +190,7 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModCommand::COMMAND effTrans[] =
|
static constexpr ModCommand::COMMAND effTrans[] =
|
||||||
{
|
{
|
||||||
CMD_PORTAMENTOUP, // Slide up (param * 80) Hz on every tick
|
CMD_PORTAMENTOUP, // Slide up (param * 80) Hz on every tick
|
||||||
CMD_PORTAMENTODOWN, // Slide down (param * 80) Hz on every tick
|
CMD_PORTAMENTODOWN, // Slide down (param * 80) Hz on every tick
|
||||||
|
|
|
@ -313,7 +313,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
patNum++;
|
patNum++;
|
||||||
} else if(!memcmp(chunkHeader.magic, "INST", 4) && GetNumSamples() < SAMPLEINDEX(MAX_SAMPLES - 1))
|
} else if(!memcmp(chunkHeader.magic, "INST", 4) && CanAddMoreSamples())
|
||||||
{
|
{
|
||||||
// Read sample
|
// Read sample
|
||||||
m_nSamples++;
|
m_nSamples++;
|
||||||
|
|
|
@ -118,16 +118,12 @@ struct MMDSong
|
||||||
MMD0Song GetMMD0Song() const
|
MMD0Song GetMMD0Song() const
|
||||||
{
|
{
|
||||||
static_assert(sizeof(MMD0Song) == sizeof(song));
|
static_assert(sizeof(MMD0Song) == sizeof(song));
|
||||||
MMD0Song result;
|
return mpt::bit_cast<MMD0Song>(song);
|
||||||
std::memcpy(&result, song, sizeof(result));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
MMD2Song GetMMD2Song() const
|
MMD2Song GetMMD2Song() const
|
||||||
{
|
{
|
||||||
static_assert(sizeof(MMD2Song) == sizeof(song));
|
static_assert(sizeof(MMD2Song) == sizeof(song));
|
||||||
MMD2Song result;
|
return mpt::bit_cast<MMD2Song>(song);
|
||||||
std::memcpy(&result, song, sizeof(result));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
uint16be defaultTempo;
|
uint16be defaultTempo;
|
||||||
int8be playTranspose; // The global play transpose value for current song
|
int8be playTranspose; // The global play transpose value for current song
|
||||||
|
@ -575,24 +571,24 @@ static void MEDReadNextSong(FileReader &file, MMD0FileHeader &fileHeader, MMD0Ex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static CHANNELINDEX MEDScanNumChannels(FileReader &file, const uint8 version)
|
static std::pair<CHANNELINDEX, SEQUENCEINDEX> MEDScanNumChannels(FileReader &file, const uint8 version)
|
||||||
{
|
{
|
||||||
MMD0FileHeader fileHeader;
|
MMD0FileHeader fileHeader;
|
||||||
MMD0Exp expData;
|
MMD0Exp expData;
|
||||||
MMDSong songHeader;
|
MMDSong songHeader;
|
||||||
|
|
||||||
file.Rewind();
|
file.Rewind();
|
||||||
|
uint32 songOffset = 0;
|
||||||
MEDReadNextSong(file, fileHeader, expData, songHeader);
|
MEDReadNextSong(file, fileHeader, expData, songHeader);
|
||||||
|
|
||||||
auto numSongs = fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1;
|
SEQUENCEINDEX numSongs = std::min(MAX_SEQUENCES, mpt::saturate_cast<SEQUENCEINDEX>(fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1));
|
||||||
|
|
||||||
CHANNELINDEX numChannels = 4;
|
CHANNELINDEX numChannels = 4;
|
||||||
// Scan patterns for max number of channels
|
// Scan patterns for max number of channels
|
||||||
for(SEQUENCEINDEX song = 0; song < numSongs; song++)
|
for(SEQUENCEINDEX song = 0; song < numSongs; song++)
|
||||||
{
|
{
|
||||||
const PATTERNINDEX numPatterns = songHeader.numBlocks;
|
const PATTERNINDEX numPatterns = songHeader.numBlocks;
|
||||||
if(songHeader.numSamples > 63 || numPatterns > 0x7FFF)
|
if(songHeader.numSamples > 63 || numPatterns > 0x7FFF)
|
||||||
return 0;
|
return {};
|
||||||
|
|
||||||
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
||||||
{
|
{
|
||||||
|
@ -604,11 +600,16 @@ static CHANNELINDEX MEDScanNumChannels(FileReader &file, const uint8 version)
|
||||||
numChannels = std::max(numChannels, static_cast<CHANNELINDEX>(version < 1 ? file.ReadUint8() : file.ReadUint16BE()));
|
numChannels = std::max(numChannels, static_cast<CHANNELINDEX>(version < 1 ? file.ReadUint8() : file.ReadUint16BE()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!expData.nextModOffset || !file.Seek(expData.nextModOffset))
|
// If song offsets are going backwards, reject the file
|
||||||
|
if(expData.nextModOffset <= songOffset || !file.Seek(expData.nextModOffset))
|
||||||
|
{
|
||||||
|
numSongs = song + 1;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
songOffset = expData.nextModOffset;
|
||||||
MEDReadNextSong(file, fileHeader, expData, songHeader);
|
MEDReadNextSong(file, fileHeader, expData, songHeader);
|
||||||
}
|
}
|
||||||
return numChannels;
|
return {numChannels, numSongs};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -680,9 +681,10 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.ReadStruct(expData);
|
file.ReadStruct(expData);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nChannels = MEDScanNumChannels(file, version);
|
const auto [numChannels, numSongs] = MEDScanNumChannels(file, version);
|
||||||
if(m_nChannels < 1 || m_nChannels > MAX_BASECHANNELS)
|
if(numChannels < 1 || numChannels > MAX_BASECHANNELS)
|
||||||
return false;
|
return false;
|
||||||
|
m_nChannels = numChannels;
|
||||||
|
|
||||||
// Start with the instruments, as those are shared between songs
|
// Start with the instruments, as those are shared between songs
|
||||||
|
|
||||||
|
@ -993,8 +995,6 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto numSongs = std::min<SEQUENCEINDEX>(MAX_SEQUENCES, fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1);
|
|
||||||
|
|
||||||
file.Rewind();
|
file.Rewind();
|
||||||
PATTERNINDEX basePattern = 0;
|
PATTERNINDEX basePattern = 0;
|
||||||
for(SEQUENCEINDEX song = 0; song < numSongs; song++)
|
for(SEQUENCEINDEX song = 0; song < numSongs; song++)
|
||||||
|
|
|
@ -292,7 +292,8 @@ uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChann
|
||||||
if (program + 1 == p->nMidiProgram && bank + 1 == p->wMidiBank && p->nMidiDrumKey == 0) return i;
|
if (program + 1 == p->nMidiProgram && bank + 1 == p->wMidiBank && p->nMidiDrumKey == 0) return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((m_nInstruments + 1 >= MAX_INSTRUMENTS) || (m_nSamples + 1 >= MAX_SAMPLES)) return 0;
|
if(!CanAddMoreInstruments() || !CanAddMoreSamples())
|
||||||
|
return 0;
|
||||||
|
|
||||||
pIns = AllocateInstrument(m_nInstruments + 1);
|
pIns = AllocateInstrument(m_nInstruments + 1);
|
||||||
if(pIns == nullptr)
|
if(pIns == nullptr)
|
||||||
|
@ -347,7 +348,7 @@ struct MThd
|
||||||
uint16be division; // Delta timing value: positive = units/beat; negative = smpte compatible units
|
uint16be division; // Delta timing value: positive = units/beat; negative = smpte compatible units
|
||||||
};
|
};
|
||||||
|
|
||||||
MPT_BINARY_STRUCT(MThd, 10);
|
MPT_BINARY_STRUCT(MThd, 10)
|
||||||
|
|
||||||
|
|
||||||
using tick_t = uint32;
|
using tick_t = uint32;
|
||||||
|
|
|
@ -838,34 +838,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_nDefaultSpeed = fileHeader.defaultSpeed ? fileHeader.defaultSpeed : 6;
|
m_nDefaultSpeed = fileHeader.defaultSpeed ? fileHeader.defaultSpeed : 6;
|
||||||
m_nDefaultTempo.Set(fileHeader.defaultTempo ? fileHeader.defaultTempo : 125, 0);
|
m_nDefaultTempo.Set(fileHeader.defaultTempo ? fileHeader.defaultTempo : 125, 0);
|
||||||
|
|
||||||
mpt::ustring originalFormatType;
|
|
||||||
mpt::ustring originalFormatName;
|
|
||||||
if(fileHeader.flags & MO3FileHeader::isIT)
|
if(fileHeader.flags & MO3FileHeader::isIT)
|
||||||
{
|
|
||||||
SetType(MOD_TYPE_IT);
|
SetType(MOD_TYPE_IT);
|
||||||
originalFormatType = U_("it");
|
else if(fileHeader.flags & MO3FileHeader::isS3M)
|
||||||
originalFormatName = U_("Impulse Tracker");
|
|
||||||
} else if(fileHeader.flags & MO3FileHeader::isS3M)
|
|
||||||
{
|
|
||||||
SetType(MOD_TYPE_S3M);
|
SetType(MOD_TYPE_S3M);
|
||||||
originalFormatType = U_("s3m");
|
else if(fileHeader.flags & MO3FileHeader::isMOD)
|
||||||
originalFormatName = U_("ScreamTracker 3");
|
|
||||||
} else if(fileHeader.flags & MO3FileHeader::isMOD)
|
|
||||||
{
|
|
||||||
SetType(MOD_TYPE_MOD);
|
SetType(MOD_TYPE_MOD);
|
||||||
originalFormatType = U_("mod");
|
else if(fileHeader.flags & MO3FileHeader::isMTM)
|
||||||
originalFormatName = U_("Generic MOD");
|
|
||||||
} else if(fileHeader.flags & MO3FileHeader::isMTM)
|
|
||||||
{
|
|
||||||
SetType(MOD_TYPE_MTM);
|
SetType(MOD_TYPE_MTM);
|
||||||
originalFormatType = U_("mtm");
|
else
|
||||||
originalFormatName = U_("MultiTracker");
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
SetType(MOD_TYPE_XM);
|
SetType(MOD_TYPE_XM);
|
||||||
originalFormatType = U_("xm");
|
|
||||||
originalFormatName = U_("FastTracker 2");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fileHeader.flags & MO3FileHeader::linearSlides)
|
if(fileHeader.flags & MO3FileHeader::linearSlides)
|
||||||
m_SongFlags.set(SONG_LINEARSLIDES);
|
m_SongFlags.set(SONG_LINEARSLIDES);
|
||||||
|
@ -1828,7 +1810,6 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
mpt::ustring madeWithTracker;
|
mpt::ustring madeWithTracker;
|
||||||
uint16 cwtv = 0;
|
uint16 cwtv = 0;
|
||||||
uint16 cmwt = 0;
|
uint16 cmwt = 0;
|
||||||
MPT_UNUSED_VARIABLE(cmwt);
|
|
||||||
while(musicChunk.CanRead(8))
|
while(musicChunk.CanRead(8))
|
||||||
{
|
{
|
||||||
uint32 id = musicChunk.ReadUint32LE();
|
uint32 id = musicChunk.ReadUint32LE();
|
||||||
|
@ -1926,18 +1907,20 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(fileHeader.flags & MO3FileHeader::modplugMode)
|
if(fileHeader.flags & MO3FileHeader::modplugMode)
|
||||||
{
|
{
|
||||||
// Apply some old ModPlug (mis-)behaviour
|
// Apply some old ModPlug (mis-)behaviour
|
||||||
|
if(!m_dwLastSavedWithVersion)
|
||||||
|
{
|
||||||
|
// These fixes are only applied when the OpenMPT version number is not known, as otherwise the song upgrade feature will take care of it.
|
||||||
for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
|
for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
|
||||||
{
|
{
|
||||||
if(ModInstrument *ins = Instruments[i])
|
if(ModInstrument *ins = Instruments[i])
|
||||||
{
|
{
|
||||||
// Fix pitch / filter envelope being shortened by one tick
|
// Fix pitch / filter envelope being shortened by one tick (for files before v1.20)
|
||||||
if(m_dwLastSavedWithVersion < MPT_V("1.20.00.00"))
|
|
||||||
ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType());
|
ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType());
|
||||||
// Fix excessive pan swing range
|
// Fix excessive pan swing range (for files before v1.26)
|
||||||
if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00"))
|
|
||||||
ins->nPanSwing = (ins->nPanSwing + 3) / 4u;
|
ins->nPanSwing = (ins->nPanSwing + 3) / 4u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if(m_dwLastSavedWithVersion < MPT_V("1.18.00.00"))
|
if(m_dwLastSavedWithVersion < MPT_V("1.18.00.00"))
|
||||||
{
|
{
|
||||||
m_playBehaviour.reset(kITOffset);
|
m_playBehaviour.reset(kITOffset);
|
||||||
|
@ -1956,8 +1939,39 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
m_modFormat.formatName = mpt::format(U_("Un4seen MO3 v%1"))(version);
|
m_modFormat.formatName = mpt::format(U_("Un4seen MO3 v%1"))(version);
|
||||||
m_modFormat.type = U_("mo3");
|
m_modFormat.type = U_("mo3");
|
||||||
m_modFormat.originalType = std::move(originalFormatType);
|
|
||||||
m_modFormat.originalFormatName = std::move(originalFormatName);
|
switch(GetType())
|
||||||
|
{
|
||||||
|
case MOD_TYPE_MTM:
|
||||||
|
m_modFormat.originalType = U_("mtm");
|
||||||
|
m_modFormat.originalFormatName = U_("MultiTracker");
|
||||||
|
break;
|
||||||
|
case MOD_TYPE_MOD:
|
||||||
|
m_modFormat.originalType = U_("mod");
|
||||||
|
m_modFormat.originalFormatName = U_("Generic MOD");
|
||||||
|
break;
|
||||||
|
case MOD_TYPE_XM:
|
||||||
|
m_modFormat.originalType = U_("xm");
|
||||||
|
m_modFormat.originalFormatName = U_("FastTracker 2");
|
||||||
|
break;
|
||||||
|
case MOD_TYPE_S3M:
|
||||||
|
m_modFormat.originalType = U_("s3m");
|
||||||
|
m_modFormat.originalFormatName = U_("ScreamTracker 3");
|
||||||
|
break;
|
||||||
|
case MOD_TYPE_IT:
|
||||||
|
m_modFormat.originalType = U_("it");
|
||||||
|
if(cmwt)
|
||||||
|
m_modFormat.originalFormatName = mpt::format(U_("Impulse Tracker %1.%2"))(cmwt >> 8, mpt::ufmt::hex0<2>(cmwt & 0xFF));
|
||||||
|
else
|
||||||
|
m_modFormat.originalFormatName = U_("Impulse Tracker");
|
||||||
|
break;
|
||||||
|
case MOD_TYPE_MPT:
|
||||||
|
m_modFormat.originalType = U_("mptm");
|
||||||
|
m_modFormat.originalFormatName = U_("OpenMPT MPTM");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MPT_ASSERT_NOTREACHED();
|
||||||
|
}
|
||||||
m_modFormat.madeWithTracker = std::move(madeWithTracker);
|
m_modFormat.madeWithTracker = std::move(madeWithTracker);
|
||||||
if(m_dwLastSavedWithVersion)
|
if(m_dwLastSavedWithVersion)
|
||||||
m_modFormat.charset = mpt::Charset::Windows1252;
|
m_modFormat.charset = mpt::Charset::Windows1252;
|
||||||
|
|
|
@ -61,6 +61,7 @@ void CSoundFile::ConvertModCommand(ModCommand &m)
|
||||||
case 'P' - 55: m.command = CMD_PANNINGSLIDE; break;
|
case 'P' - 55: m.command = CMD_PANNINGSLIDE; break;
|
||||||
case 'R' - 55: m.command = CMD_RETRIG; break;
|
case 'R' - 55: m.command = CMD_RETRIG; break;
|
||||||
case 'T' - 55: m.command = CMD_TREMOR; break;
|
case 'T' - 55: m.command = CMD_TREMOR; break;
|
||||||
|
case 'W' - 55: m.command = CMD_DUMMY; break;
|
||||||
case 'X' - 55: m.command = CMD_XFINEPORTAUPDOWN; break;
|
case 'X' - 55: m.command = CMD_XFINEPORTAUPDOWN; break;
|
||||||
case 'Y' - 55: m.command = CMD_PANBRELLO; break; //34
|
case 'Y' - 55: m.command = CMD_PANBRELLO; break; //34
|
||||||
case 'Z' - 55: m.command = CMD_MIDI; break; //35
|
case 'Z' - 55: m.command = CMD_MIDI; break; //35
|
||||||
|
@ -137,6 +138,7 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co
|
||||||
case CMD_PANNINGSLIDE: command = 'P' - 55; break;
|
case CMD_PANNINGSLIDE: command = 'P' - 55; break;
|
||||||
case CMD_RETRIG: command = 'R' - 55; break;
|
case CMD_RETRIG: command = 'R' - 55; break;
|
||||||
case CMD_TREMOR: command = 'T' - 55; break;
|
case CMD_TREMOR: command = 'T' - 55; break;
|
||||||
|
case CMD_DUMMY: command = 'W' - 55; break;
|
||||||
case CMD_XFINEPORTAUPDOWN: command = 'X' - 55;
|
case CMD_XFINEPORTAUPDOWN: command = 'X' - 55;
|
||||||
if(compatibilityExport && param >= 0x30) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
|
if(compatibilityExport && param >= 0x30) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
|
||||||
param = 0; // Don't set command to 0 to indicate that there *was* some X command here...
|
param = 0; // Don't set command to 0 to indicate that there *was* some X command here...
|
||||||
|
@ -342,6 +344,9 @@ struct MODSampleHeader
|
||||||
|
|
||||||
MPT_BINARY_STRUCT(MODSampleHeader, 30)
|
MPT_BINARY_STRUCT(MODSampleHeader, 30)
|
||||||
|
|
||||||
|
// Pattern data of a 4-channel MOD file
|
||||||
|
using MODPatternData = std::array<std::array<std::array<uint8, 4>, 4>, 64>;
|
||||||
|
|
||||||
// Synthesized StarTrekker instruments
|
// Synthesized StarTrekker instruments
|
||||||
struct AMInstrument
|
struct AMInstrument
|
||||||
{
|
{
|
||||||
|
@ -518,8 +523,36 @@ static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSam
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Count malformed bytes in MOD pattern data
|
||||||
|
static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, const bool allow31Samples)
|
||||||
|
{
|
||||||
|
const uint8 mask = allow31Samples ? 0xE0 : 0xF0;
|
||||||
|
uint32 malformedBytes = 0;
|
||||||
|
for(const auto &row : patternData)
|
||||||
|
{
|
||||||
|
for(const auto &data : row)
|
||||||
|
{
|
||||||
|
if(data[0] & mask)
|
||||||
|
malformedBytes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return malformedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if number of malformed bytes in MOD pattern data exceeds some threshold
|
||||||
|
template <typename TFileReader>
|
||||||
|
static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, const bool allow31Samples)
|
||||||
|
{
|
||||||
|
MODPatternData patternData;
|
||||||
|
if(!file.Read(patternData))
|
||||||
|
return false;
|
||||||
|
return CountMalformedMODPatternData(patternData, allow31Samples) <= threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Parse the order list to determine how many patterns are used in the file.
|
// Parse the order list to determine how many patterns are used in the file.
|
||||||
static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERINDEX numOrders, SmpLength totalSampleLen, CHANNELINDEX &numChannels, bool checkForWOW)
|
static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERINDEX numOrders, SmpLength totalSampleLen, CHANNELINDEX &numChannels, SmpLength wowSampleLen = 0)
|
||||||
{
|
{
|
||||||
PATTERNINDEX numPatterns = 0; // Total number of patterns in file (determined by going through the whole order list) with pattern number < 128
|
PATTERNINDEX numPatterns = 0; // Total number of patterns in file (determined by going through the whole order list) with pattern number < 128
|
||||||
PATTERNINDEX officialPatterns = 0; // Number of patterns only found in the "official" part of the order list (i.e. order positions < claimed order length)
|
PATTERNINDEX officialPatterns = 0; // Number of patterns only found in the "official" part of the order list (i.e. order positions < claimed order length)
|
||||||
|
@ -548,12 +581,16 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN
|
||||||
const size_t patternStartOffset = file.GetPosition();
|
const size_t patternStartOffset = file.GetPosition();
|
||||||
const size_t sizeWithoutPatterns = totalSampleLen + patternStartOffset;
|
const size_t sizeWithoutPatterns = totalSampleLen + patternStartOffset;
|
||||||
|
|
||||||
if(checkForWOW && sizeWithoutPatterns + numPatterns * 8 * 256 == file.GetLength())
|
if(wowSampleLen && (wowSampleLen + patternStartOffset) + numPatterns * 8 * 256 == (file.GetLength() & ~1))
|
||||||
{
|
{
|
||||||
// Check if this is a Mod's Grave WOW file... Never seen one of those, but apparently they *do* exist.
|
// Check if this is a Mod's Grave WOW file... WOW files use the M.K. magic but are actually 8CHN files.
|
||||||
// WOW files should use the M.K. magic but are actually 8CHN files.
|
// We do a simple pattern validation as well for regular MOD files that have non-module data attached at the end
|
||||||
|
// (e.g. ponylips.mod, MD5 c039af363b1d99a492dafc5b5f9dd949, SHA1 1bee1941c47bc6f913735ce0cf1880b248b8fc93)
|
||||||
|
file.Seek(patternStartOffset + numPatterns * 4 * 256);
|
||||||
|
if(ValidateMODPatternData(file, 16, true))
|
||||||
numChannels = 8;
|
numChannels = 8;
|
||||||
} else if(numPatterns != officialPatterns && numChannels == 4 && !checkForWOW)
|
file.Seek(patternStartOffset);
|
||||||
|
} else if(numPatterns != officialPatterns && numChannels == 4 && !wowSampleLen)
|
||||||
{
|
{
|
||||||
// Fix SoundTracker modules where "hidden" patterns should be ignored.
|
// Fix SoundTracker modules where "hidden" patterns should be ignored.
|
||||||
// razor-1911.mod (MD5 b75f0f471b0ae400185585ca05bf7fe8, SHA1 4de31af234229faec00f1e85e1e8f78f405d454b)
|
// razor-1911.mod (MD5 b75f0f471b0ae400185585ca05bf7fe8, SHA1 4de31af234229faec00f1e85e1e8f78f405d454b)
|
||||||
|
@ -568,20 +605,8 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN
|
||||||
// Hence, we have a peek at the first hidden pattern and check if it contains a lot of illegal data.
|
// Hence, we have a peek at the first hidden pattern and check if it contains a lot of illegal data.
|
||||||
// If that is the case, we assume it's part of the sample data and only consider the "official" patterns.
|
// If that is the case, we assume it's part of the sample data and only consider the "official" patterns.
|
||||||
file.Seek(patternStartOffset + officialPatterns * 1024);
|
file.Seek(patternStartOffset + officialPatterns * 1024);
|
||||||
int illegalBytes = 0;
|
if(!ValidateMODPatternData(file, 64, true))
|
||||||
for(int i = 0; i < 256; i++)
|
|
||||||
{
|
|
||||||
const auto data = file.ReadArray<uint8, 4>();
|
|
||||||
if(data[0] & 0xE0)
|
|
||||||
{
|
|
||||||
illegalBytes++;
|
|
||||||
if(illegalBytes > 64)
|
|
||||||
{
|
|
||||||
numPatterns = officialPatterns;
|
numPatterns = officialPatterns;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.Seek(patternStartOffset);
|
file.Seek(patternStartOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,7 +749,7 @@ static bool CheckMODMagic(const char magic[4], MODMagicResult &result)
|
||||||
result.madeWithTracker = UL_("Generic MOD-compatible Tracker");
|
result.madeWithTracker = UL_("Generic MOD-compatible Tracker");
|
||||||
result.isGenericMultiChannel = true;
|
result.isGenericMultiChannel = true;
|
||||||
result.numChannels = magic[0] - '0';
|
result.numChannels = magic[0] - '0';
|
||||||
} else if(magic[0] >= '1' && magic[0] <= '9' && magic[1]>='0' && magic[1] <= '9'
|
} else if(magic[0] >= '1' && magic[0] <= '9' && magic[1] >= '0' && magic[1] <= '9'
|
||||||
&& (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2)))
|
&& (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2)))
|
||||||
{
|
{
|
||||||
// xxCN / xxCH - Many trackers
|
// xxCN / xxCH - Many trackers
|
||||||
|
@ -819,13 +844,14 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
const bool isMdKd = IsMagic(magic, "M.K.");
|
const bool isMdKd = IsMagic(magic, "M.K.");
|
||||||
// Adjust finetune values for modules saved with "His Master's Noisetracker"
|
// Adjust finetune values for modules saved with "His Master's Noisetracker"
|
||||||
const bool isHMNT = IsMagic(magic, "M&K!") || IsMagic(magic, "FEST");
|
const bool isHMNT = IsMagic(magic, "M&K!") || IsMagic(magic, "FEST");
|
||||||
|
bool maybeWOW = isMdKd;
|
||||||
|
|
||||||
// Reading song title
|
// Reading song title
|
||||||
file.Seek(0);
|
file.Seek(0);
|
||||||
file.ReadString<mpt::String::spacePadded>(m_songName, 20);
|
file.ReadString<mpt::String::spacePadded>(m_songName, 20);
|
||||||
|
|
||||||
// Load Sample Headers
|
// Load Sample Headers
|
||||||
SmpLength totalSampleLen = 0;
|
SmpLength totalSampleLen = 0, wowSampleLen = 0;
|
||||||
m_nSamples = 31;
|
m_nSamples = 31;
|
||||||
uint32 invalidBytes = 0;
|
uint32 invalidBytes = 0;
|
||||||
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
||||||
|
@ -835,15 +861,22 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
totalSampleLen += Samples[smp].nLength;
|
totalSampleLen += Samples[smp].nLength;
|
||||||
|
|
||||||
if(isHMNT)
|
if(isHMNT)
|
||||||
{
|
|
||||||
Samples[smp].nFineTune = -static_cast<int8>(sampleHeader.finetune << 3);
|
Samples[smp].nFineTune = -static_cast<int8>(sampleHeader.finetune << 3);
|
||||||
} else if(Samples[smp].nLength > 65535)
|
else if(Samples[smp].nLength > 65535)
|
||||||
{
|
|
||||||
isNoiseTracker = false;
|
isNoiseTracker = false;
|
||||||
}
|
|
||||||
if(sampleHeader.length && !sampleHeader.loopLength)
|
if(sampleHeader.length && !sampleHeader.loopLength)
|
||||||
{
|
|
||||||
hasRepLen0 = true;
|
hasRepLen0 = true;
|
||||||
|
|
||||||
|
if(maybeWOW)
|
||||||
|
{
|
||||||
|
// Some WOW files rely on sample length 1 being counted as well
|
||||||
|
wowSampleLen += sampleHeader.length * 2;
|
||||||
|
// WOW files are converted 669 files, which don't support finetune or default volume
|
||||||
|
if(sampleHeader.finetune)
|
||||||
|
maybeWOW = false;
|
||||||
|
else if(sampleHeader.length > 0 && sampleHeader.volume != 64)
|
||||||
|
maybeWOW = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If there is too much binary garbage in the sample headers, reject the file.
|
// If there is too much binary garbage in the sample headers, reject the file.
|
||||||
|
@ -857,6 +890,11 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.ReadStruct(fileHeader);
|
file.ReadStruct(fileHeader);
|
||||||
file.Skip(4); // Magic bytes (we already parsed these)
|
file.Skip(4); // Magic bytes (we already parsed these)
|
||||||
|
|
||||||
|
if(fileHeader.restartPos > 0)
|
||||||
|
maybeWOW = false;
|
||||||
|
if(!maybeWOW)
|
||||||
|
wowSampleLen = 0;
|
||||||
|
|
||||||
ReadOrderFromArray(Order(), fileHeader.orderList);
|
ReadOrderFromArray(Order(), fileHeader.orderList);
|
||||||
|
|
||||||
ORDERINDEX realOrders = fileHeader.numOrders;
|
ORDERINDEX realOrders = fileHeader.numOrders;
|
||||||
|
@ -875,11 +913,12 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get number of patterns (including some order list sanity checks)
|
// Get number of patterns (including some order list sanity checks)
|
||||||
PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), realOrders, totalSampleLen, m_nChannels, isMdKd);
|
PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), realOrders, totalSampleLen, m_nChannels, wowSampleLen);
|
||||||
if(isMdKd && GetNumChannels() == 8)
|
if(maybeWOW && GetNumChannels() == 8)
|
||||||
{
|
{
|
||||||
// M.K. with 8 channels = Grave Composer
|
// M.K. with 8 channels = Mod's Grave
|
||||||
modMagicResult.madeWithTracker = UL_("Mod's Grave");
|
modMagicResult.madeWithTracker = UL_("Mod's Grave");
|
||||||
|
isGenericMultiChannel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isFLT8)
|
if(isFLT8)
|
||||||
|
@ -1086,7 +1125,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
lastInstrument[chn] = m.instr;
|
lastInstrument[chn] = m.instr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(hasSpeedOnRow && hasTempoOnRow) definitelyCIA = true;
|
if(hasSpeedOnRow && hasTempoOnRow)
|
||||||
|
definitelyCIA = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1100,7 +1140,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_playBehaviour.set(kMODOutOfRangeNoteDelay);
|
m_playBehaviour.set(kMODOutOfRangeNoteDelay);
|
||||||
m_playBehaviour.set(kMODTempoOnSecondTick);
|
m_playBehaviour.set(kMODTempoOnSecondTick);
|
||||||
// Arbitrary threshold for deciding that 8xx effects are only used as sync markers
|
// Arbitrary threshold for deciding that 8xx effects are only used as sync markers
|
||||||
if(maxPanning < 0x20)
|
if(maxPanning < 0x30)
|
||||||
{
|
{
|
||||||
m_playBehaviour.set(kMODIgnorePanning);
|
m_playBehaviour.set(kMODIgnorePanning);
|
||||||
if(fileHeader.restartPos != 0x7F)
|
if(fileHeader.restartPos != 0x7F)
|
||||||
|
@ -1265,7 +1305,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
|
|
||||||
// Check if a name string is valid (i.e. doesn't contain binary garbage data)
|
// Check if a name string is valid (i.e. doesn't contain binary garbage data)
|
||||||
template<size_t N>
|
template <size_t N>
|
||||||
static uint32 CountInvalidChars(const char (&name)[N])
|
static uint32 CountInvalidChars(const char (&name)[N])
|
||||||
{
|
{
|
||||||
uint32 invalidChars = 0;
|
uint32 invalidChars = 0;
|
||||||
|
@ -1304,8 +1344,6 @@ struct M15FileHeaders
|
||||||
|
|
||||||
MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130)
|
MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130)
|
||||||
|
|
||||||
typedef std::array<uint8, 4> M15PatternData[64][4];
|
|
||||||
|
|
||||||
|
|
||||||
static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
||||||
{
|
{
|
||||||
|
@ -1372,39 +1410,11 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint32 CountIllegalM15PatternBytes(const M15PatternData &patternData)
|
|
||||||
{
|
|
||||||
uint32 illegalBytes = 0;
|
|
||||||
for(uint8 row = 0; row < 64; ++row)
|
|
||||||
{
|
|
||||||
for(uint8 channel = 0; channel < 4; ++channel)
|
|
||||||
{
|
|
||||||
if(patternData[row][channel][0] & 0xF0u)
|
|
||||||
{
|
|
||||||
illegalBytes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return illegalBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename TFileReader>
|
template <typename TFileReader>
|
||||||
static bool ValidateFirstM15Pattern(TFileReader &file)
|
static bool ValidateFirstM15Pattern(TFileReader &file)
|
||||||
{
|
{
|
||||||
M15PatternData patternData;
|
// threshold is chosen as: [threshold for all patterns combined] / [max patterns] * [margin, do not reject too much]
|
||||||
if(!file.ReadArray(patternData))
|
return ValidateMODPatternData(file, 512 / 64 * 2, false);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
file.SkipBack(sizeof(patternData));
|
|
||||||
uint32 invalidBytes = CountIllegalM15PatternBytes(patternData);
|
|
||||||
// [threshold for all patterns combined] / [max patterns] * [margin, do not reject too much]
|
|
||||||
if(invalidBytes > 512 / 64 * 2)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1419,7 +1429,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderM15(MemoryFileReader file, co
|
||||||
{
|
{
|
||||||
return ProbeFailure;
|
return ProbeFailure;
|
||||||
}
|
}
|
||||||
if(!file.CanRead(sizeof(M15PatternData)))
|
if(!file.CanRead(sizeof(MODPatternData)))
|
||||||
{
|
{
|
||||||
return ProbeWantMoreData;
|
return ProbeWantMoreData;
|
||||||
}
|
}
|
||||||
|
@ -1496,7 +1506,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.ReadStruct(fileHeader);
|
file.ReadStruct(fileHeader);
|
||||||
|
|
||||||
ReadOrderFromArray(Order(), fileHeader.orderList);
|
ReadOrderFromArray(Order(), fileHeader.orderList);
|
||||||
PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false);
|
PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels);
|
||||||
|
|
||||||
// Most likely just a file with lots of NULs at the start
|
// Most likely just a file with lots of NULs at the start
|
||||||
if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1)
|
if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1)
|
||||||
|
@ -1552,11 +1562,11 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
bool patternInUse = std::find(Order().cbegin(), Order().cend(), pat) != Order().cend();
|
bool patternInUse = std::find(Order().cbegin(), Order().cend(), pat) != Order().cend();
|
||||||
uint8 numDxx = 0;
|
uint8 numDxx = 0;
|
||||||
uint8 emptyCmds = 0;
|
uint8 emptyCmds = 0;
|
||||||
M15PatternData patternData;
|
MODPatternData patternData;
|
||||||
file.ReadArray(patternData);
|
file.ReadArray(patternData);
|
||||||
if(patternInUse)
|
if(patternInUse)
|
||||||
{
|
{
|
||||||
illegalBytes += CountIllegalM15PatternBytes(patternData);
|
illegalBytes += CountMalformedMODPatternData(patternData, false);
|
||||||
// Reject files that contain a lot of illegal pattern data.
|
// Reject files that contain a lot of illegal pattern data.
|
||||||
// STK.the final remix (MD5 5ff13cdbd77211d1103be7051a7d89c9, SHA1 e94dba82a5da00a4758ba0c207eb17e3a89c3aa3)
|
// STK.the final remix (MD5 5ff13cdbd77211d1103be7051a7d89c9, SHA1 e94dba82a5da00a4758ba0c207eb17e3a89c3aa3)
|
||||||
// has one illegal byte, so we only reject after an arbitrary threshold has been passed.
|
// has one illegal byte, so we only reject after an arbitrary threshold has been passed.
|
||||||
|
@ -1655,7 +1665,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
Patterns.ResizeArray(numPatterns);
|
Patterns.ResizeArray(numPatterns);
|
||||||
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
||||||
{
|
{
|
||||||
M15PatternData patternData;
|
MODPatternData patternData;
|
||||||
file.ReadArray(patternData);
|
file.ReadArray(patternData);
|
||||||
|
|
||||||
if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64))
|
if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64))
|
||||||
|
|
|
@ -627,7 +627,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m.param--;
|
m.param--;
|
||||||
if(m.param < loopList.size())
|
if(m.param < loopList.size())
|
||||||
{
|
{
|
||||||
if(!loopList[m.param].looped && m_nSamples < MAX_SAMPLES - 1)
|
if(!loopList[m.param].looped && CanAddMoreSamples())
|
||||||
loopList[m.param].looped = ++m_nSamples;
|
loopList[m.param].looped = ++m_nSamples;
|
||||||
m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
|
m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
|
||||||
}
|
}
|
||||||
|
@ -648,7 +648,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m.vol = m.param;
|
m.vol = m.param;
|
||||||
}
|
}
|
||||||
// switch to non-looped version of sample and create it if needed
|
// switch to non-looped version of sample and create it if needed
|
||||||
if(!nonLooped[m.instr - 1] && m_nSamples < MAX_SAMPLES - 1)
|
if(!nonLooped[m.instr - 1] && CanAddMoreSamples())
|
||||||
nonLooped[m.instr - 1] = ++m_nSamples;
|
nonLooped[m.instr - 1] = ++m_nSamples;
|
||||||
m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
|
m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
|
||||||
}
|
}
|
||||||
|
@ -664,7 +664,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m.param--;
|
m.param--;
|
||||||
if(m.param < loopList.size())
|
if(m.param < loopList.size())
|
||||||
{
|
{
|
||||||
if(!loopList[m.param].nonLooped && m_nSamples < MAX_SAMPLES-1)
|
if(!loopList[m.param].nonLooped && CanAddMoreSamples())
|
||||||
loopList[m.param].nonLooped = ++m_nSamples;
|
loopList[m.param].nonLooped = ++m_nSamples;
|
||||||
m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
|
m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
FileReader fileChunk = chunk.ReadChunk(size);
|
FileReader fileChunk = chunk.ReadChunk(size);
|
||||||
|
|
||||||
if(GetNumSamples() < MAX_SAMPLES - 1)
|
if(CanAddMoreSamples())
|
||||||
{
|
{
|
||||||
// Read as sample
|
// Read as sample
|
||||||
if(ReadSampleFromFile(GetNumSamples() + 1, fileChunk, true))
|
if(ReadSampleFromFile(GetNumSamples() + 1, fileChunk, true))
|
||||||
|
|
|
@ -395,11 +395,12 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT.
|
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT.
|
||||||
|
|
||||||
// read "messageLength" lines, each containing 32 characters.
|
// Read "messageLength" lines, each containing 32 characters.
|
||||||
m_songMessage.ReadFixedLineLength(file, fileHeader.messageLength * 32, 32, 0);
|
m_songMessage.ReadFixedLineLength(file, fileHeader.messageLength * 32, 32, 0);
|
||||||
|
|
||||||
m_nSamples = static_cast<SAMPLEINDEX>(file.ReadUint8());
|
if(SAMPLEINDEX numSamples = file.ReadUint8(); numSamples < MAX_SAMPLES)
|
||||||
if(GetNumSamples() >= MAX_SAMPLES)
|
m_nSamples = numSamples;
|
||||||
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
|
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
|
||||||
|
@ -423,12 +424,13 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
ReadOrderFromFile<uint8>(Order(), file, 256, 0xFF, 0xFE);
|
ReadOrderFromFile<uint8>(Order(), file, 256, 0xFF, 0xFE);
|
||||||
|
|
||||||
m_nChannels = file.ReadUint8() + 1;
|
if(CHANNELINDEX numChannels = file.ReadUint8() + 1u; numChannels <= MAX_BASECHANNELS)
|
||||||
PATTERNINDEX numPats = file.ReadUint8() + 1;
|
m_nChannels = numChannels;
|
||||||
|
else
|
||||||
if(GetNumChannels() > MAX_BASECHANNELS)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
PATTERNINDEX numPats = file.ReadUint8() + 1;
|
||||||
|
|
||||||
for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
|
for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
|
||||||
{
|
{
|
||||||
ChnSettings[chn].Reset();
|
ChnSettings[chn].Reset();
|
||||||
|
|
|
@ -43,7 +43,7 @@ void InstrumentEnvelope::Convert(MODTYPE fromType, MODTYPE toType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XM -> IT / MPTM: Shorten loop by one tick by inserting bogus point
|
// XM -> IT / MPTM: Shorten loop by one tick by inserting bogus point
|
||||||
if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP])
|
if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP] && nLoopEnd < size())
|
||||||
{
|
{
|
||||||
if(at(nLoopEnd).tick - 1 > at(nLoopEnd - 1).tick)
|
if(at(nLoopEnd).tick - 1 > at(nLoopEnd - 1).tick)
|
||||||
{
|
{
|
||||||
|
|
|
@ -469,11 +469,10 @@ int32 ModSample::FrequencyToTranspose(uint32 freq)
|
||||||
|
|
||||||
void ModSample::FrequencyToTranspose()
|
void ModSample::FrequencyToTranspose()
|
||||||
{
|
{
|
||||||
int f2t = 0;
|
const int f2t = Clamp(FrequencyToTranspose(nC5Speed), -16384, 16383);
|
||||||
if(nC5Speed)
|
const auto fine = std::div(f2t, 128);
|
||||||
f2t = Clamp(FrequencyToTranspose(nC5Speed), -16384, 16383);
|
RelativeTone = static_cast<int8>(fine.quot);
|
||||||
RelativeTone = static_cast<int8>(f2t / 128);
|
nFineTune = static_cast<int8>(fine.rem);
|
||||||
nFineTune = static_cast<int8>(f2t & 0x7F);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -207,6 +207,8 @@ bool ModSequence::IsValidPat(ORDERINDEX ord) const
|
||||||
ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bool searchForward) const
|
ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bool searchForward) const
|
||||||
{
|
{
|
||||||
const ORDERINDEX length = GetLength();
|
const ORDERINDEX length = GetLength();
|
||||||
|
if(startSearchAt >= length)
|
||||||
|
return ORDERINDEX_INVALID;
|
||||||
ORDERINDEX ord = startSearchAt;
|
ORDERINDEX ord = startSearchAt;
|
||||||
for(ORDERINDEX p = 0; p < length; p++)
|
for(ORDERINDEX p = 0; p < length; p++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,8 +26,8 @@ class ModSequence: public std::vector<PATTERNINDEX>
|
||||||
friend class ModSequenceSet;
|
friend class ModSequenceSet;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
mpt::ustring m_name; // Sequence name.
|
mpt::ustring m_name; // Sequence name
|
||||||
CSoundFile &m_sndFile; // Associated CSoundFile.
|
CSoundFile &m_sndFile; // Associated CSoundFile
|
||||||
ORDERINDEX m_restartPos = 0; // Restart position when playback of this order ended
|
ORDERINDEX m_restartPos = 0; // Restart position when playback of this order ended
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -128,7 +128,7 @@ class ModSequenceSet
|
||||||
friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset);
|
friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<ModSequence> m_Sequences; // Array of sequences.
|
std::vector<ModSequence> m_Sequences; // Array of sequences
|
||||||
CSoundFile &m_sndFile;
|
CSoundFile &m_sndFile;
|
||||||
SEQUENCEINDEX m_currentSeq = 0; // Index of current sequence.
|
SEQUENCEINDEX m_currentSeq = 0; // Index of current sequence.
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,11 @@ OPENMPT_NAMESPACE_BEGIN
|
||||||
|
|
||||||
// Formats which have 7-bit (0...128) instead of 6-bit (0...64) global volume commands, or which are imported to this range (mostly formats which are converted to IT internally)
|
// Formats which have 7-bit (0...128) instead of 6-bit (0...64) global volume commands, or which are imported to this range (mostly formats which are converted to IT internally)
|
||||||
#ifdef MODPLUG_TRACKER
|
#ifdef MODPLUG_TRACKER
|
||||||
#define GLOBALVOL_7BIT_FORMATS_EXT (MOD_TYPE_MT2)
|
static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_MT2;
|
||||||
#else
|
#else
|
||||||
#define GLOBALVOL_7BIT_FORMATS_EXT (MOD_TYPE_NONE)
|
static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_NONE;
|
||||||
#endif // MODPLUG_TRACKER
|
#endif // MODPLUG_TRACKER
|
||||||
#define GLOBALVOL_7BIT_FORMATS (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT)
|
static constexpr auto GLOBALVOL_7BIT_FORMATS = MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT;
|
||||||
|
|
||||||
|
|
||||||
// Compensate frequency slide LUTs depending on whether we are handling periods or frequency - "up" and "down" in function name are seen from frequency perspective.
|
// Compensate frequency slide LUTs depending on whether we are handling periods or frequency - "up" and "down" in function name are seen from frequency perspective.
|
||||||
|
@ -244,7 +244,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
retval.startRow = target.startRow;
|
retval.startRow = target.startRow;
|
||||||
|
|
||||||
// Are we trying to reach a certain pattern position?
|
// Are we trying to reach a certain pattern position?
|
||||||
const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget;
|
const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs;
|
||||||
const bool adjustSamplePos = (adjustMode & eAdjustSamplePositions) == eAdjustSamplePositions;
|
const bool adjustSamplePos = (adjustMode & eAdjustSamplePositions) == eAdjustSamplePositions;
|
||||||
|
|
||||||
SEQUENCEINDEX sequence = target.sequence;
|
SEQUENCEINDEX sequence = target.sequence;
|
||||||
|
@ -300,14 +300,6 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
// Time target reached.
|
|
||||||
if(target.mode == GetLengthTarget::SeekSeconds && memory.elapsedTime >= target.time)
|
|
||||||
{
|
|
||||||
retval.targetReached = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 rowDelay = 0, tickDelay = 0;
|
|
||||||
playState.m_nRow = playState.m_nNextRow;
|
playState.m_nRow = playState.m_nNextRow;
|
||||||
playState.m_nCurrentOrder = playState.m_nNextOrder;
|
playState.m_nCurrentOrder = playState.m_nNextOrder;
|
||||||
|
|
||||||
|
@ -322,6 +314,13 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
playState.m_nCurrentOrder = ++playState.m_nNextOrder;
|
playState.m_nCurrentOrder = ++playState.m_nNextOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Time target reached.
|
||||||
|
if(target.mode == GetLengthTarget::SeekSeconds && memory.elapsedTime >= target.time)
|
||||||
|
{
|
||||||
|
retval.targetReached = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if pattern is valid
|
// Check if pattern is valid
|
||||||
playState.m_nPattern = playState.m_nCurrentOrder < orderList.size() ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex();
|
playState.m_nPattern = playState.m_nCurrentOrder < orderList.size() ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex();
|
||||||
bool positionJumpOnThisRow = false, positionJumpRightOfPatternLoop = false;
|
bool positionJumpOnThisRow = false, positionJumpRightOfPatternLoop = false;
|
||||||
|
@ -352,7 +351,12 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
playState.m_nNextOrder = playState.m_nCurrentOrder;
|
playState.m_nNextOrder = playState.m_nCurrentOrder;
|
||||||
if((!Patterns.IsValidPat(playState.m_nPattern)) && visitedRows.IsVisited(playState.m_nCurrentOrder, 0, true))
|
if((!Patterns.IsValidPat(playState.m_nPattern)) && visitedRows.IsVisited(playState.m_nCurrentOrder, 0, true))
|
||||||
{
|
{
|
||||||
if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
|
if(!hasSearchTarget)
|
||||||
|
{
|
||||||
|
retval.lastOrder = playState.m_nCurrentOrder;
|
||||||
|
retval.lastRow = 0;
|
||||||
|
}
|
||||||
|
if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
|
||||||
{
|
{
|
||||||
// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
|
// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
|
||||||
break;
|
break;
|
||||||
|
@ -384,7 +388,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
// If there isn't even a tune, we should probably stop here.
|
// If there isn't even a tune, we should probably stop here.
|
||||||
if(playState.m_nCurrentOrder == orderList.GetRestartPos())
|
if(playState.m_nCurrentOrder == orderList.GetRestartPos())
|
||||||
{
|
{
|
||||||
if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
|
if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
|
||||||
{
|
{
|
||||||
// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
|
// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
|
||||||
break;
|
break;
|
||||||
|
@ -416,7 +420,12 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
|
|
||||||
if(visitedRows.IsVisited(playState.m_nCurrentOrder, playState.m_nRow, true))
|
if(visitedRows.IsVisited(playState.m_nCurrentOrder, playState.m_nRow, true))
|
||||||
{
|
{
|
||||||
if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
|
if(!hasSearchTarget)
|
||||||
|
{
|
||||||
|
retval.lastOrder = playState.m_nCurrentOrder;
|
||||||
|
retval.lastRow = playState.m_nRow;
|
||||||
|
}
|
||||||
|
if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
|
||||||
{
|
{
|
||||||
// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
|
// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
|
||||||
break;
|
break;
|
||||||
|
@ -455,6 +464,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
}
|
}
|
||||||
|
|
||||||
// For various effects, we need to know first how many ticks there are in this row.
|
// For various effects, we need to know first how many ticks there are in this row.
|
||||||
|
uint32 rowDelay = 0, tickDelay = 0;
|
||||||
const ModCommand *p = Patterns[playState.m_nPattern].GetpModCommand(playState.m_nRow, 0);
|
const ModCommand *p = Patterns[playState.m_nPattern].GetpModCommand(playState.m_nRow, 0);
|
||||||
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, p++)
|
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, p++)
|
||||||
{
|
{
|
||||||
|
@ -1033,7 +1043,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
SampleOffset(chn, offset);
|
SampleOffset(chn, offset);
|
||||||
} else if(m.command == CMD_OFFSETPERCENTAGE)
|
} else if(m.command == CMD_OFFSETPERCENTAGE)
|
||||||
{
|
{
|
||||||
SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 255));
|
SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 256));
|
||||||
} else if(m.command == CMD_REVERSEOFFSET && chn.pModSample != nullptr)
|
} else if(m.command == CMD_REVERSEOFFSET && chn.pModSample != nullptr)
|
||||||
{
|
{
|
||||||
memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far
|
memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far
|
||||||
|
@ -1224,14 +1234,15 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
startTimes[start] = std::lcm(startTimes[start], 1 + (param & 0x0F));
|
startTimes[start] = std::lcm(startTimes[start], 1 + (param & 0x0F));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(const auto &i : startTimes)
|
for(const auto &[startTime, loopCount] : startTimes)
|
||||||
{
|
{
|
||||||
memory.elapsedTime += (memory.elapsedTime - i.first) * (double)(i.second - 1);
|
memory.elapsedTime += (memory.elapsedTime - startTime) * (loopCount - 1);
|
||||||
|
//memory.elapsedBeats += 1.0 / playState.m_nCurrentRowsPerBeat;
|
||||||
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
|
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
|
||||||
{
|
{
|
||||||
if(memory.chnSettings[nChn].patLoop == i.first)
|
if(memory.chnSettings[nChn].patLoop == startTime)
|
||||||
{
|
{
|
||||||
playState.m_lTotalSampleCount += (playState.m_lTotalSampleCount - memory.chnSettings[nChn].patLoopSmp) * (i.second - 1);
|
playState.m_lTotalSampleCount += (playState.m_lTotalSampleCount - memory.chnSettings[nChn].patLoopSmp) * (loopCount - 1);
|
||||||
if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M))
|
if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M))
|
||||||
{
|
{
|
||||||
memory.chnSettings[nChn].patLoop = memory.elapsedTime;
|
memory.chnSettings[nChn].patLoop = memory.elapsedTime;
|
||||||
|
@ -1270,7 +1281,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(retval.targetReached || target.mode == GetLengthTarget::NoTarget)
|
if(retval.targetReached)
|
||||||
{
|
{
|
||||||
retval.lastOrder = playState.m_nCurrentOrder;
|
retval.lastOrder = playState.m_nCurrentOrder;
|
||||||
retval.lastRow = playState.m_nRow;
|
retval.lastRow = playState.m_nRow;
|
||||||
|
@ -1344,11 +1355,11 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
{
|
{
|
||||||
Order.SetSequence(sequence);
|
Order.SetSequence(sequence);
|
||||||
}
|
}
|
||||||
visitedSongRows.MoveVisitedRowsFrom(visitedRows);
|
|
||||||
}
|
}
|
||||||
|
if(adjustMode & (eAdjust | eAdjustOnlyVisitedRows))
|
||||||
|
visitedSongRows.MoveVisitedRowsFrom(visitedRows);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3281,7 +3292,7 @@ bool CSoundFile::ProcessEffects()
|
||||||
case CMD_OFFSETPERCENTAGE:
|
case CMD_OFFSETPERCENTAGE:
|
||||||
if(triggerNote)
|
if(triggerNote)
|
||||||
{
|
{
|
||||||
SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, param, 255));
|
SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, param, 256));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -5812,7 +5823,8 @@ TEMPO CSoundFile::ConvertST2Tempo(uint8 tempo)
|
||||||
static constexpr uint32 st2MixingRate = 23863; // Highest possible setting in ST2
|
static constexpr uint32 st2MixingRate = 23863; // Highest possible setting in ST2
|
||||||
|
|
||||||
// This underflows at tempo 06...0F, and the resulting tick lengths depend on the mixing rate.
|
// This underflows at tempo 06...0F, and the resulting tick lengths depend on the mixing rate.
|
||||||
int32 samplesPerTick = st2MixingRate / (49 - ((ST2TempoFactor[tempo >> 4u] * (tempo & 0x0F)) >> 4u));
|
// Note: ST2.3 uses the constant 50 below, earlier versions use 49 but they also play samples at a different speed.
|
||||||
|
int32 samplesPerTick = st2MixingRate / (50 - ((ST2TempoFactor[tempo >> 4u] * (tempo & 0x0F)) >> 4u));
|
||||||
if(samplesPerTick <= 0)
|
if(samplesPerTick <= 0)
|
||||||
samplesPerTick += 65536;
|
samplesPerTick += 65536;
|
||||||
return TEMPO().SetRaw(Util::muldivrfloor(st2MixingRate, 5 * TEMPO::fractFact, samplesPerTick * 2));
|
return TEMPO().SetRaw(Util::muldivrfloor(st2MixingRate, 5 * TEMPO::fractFact, samplesPerTick * 2));
|
||||||
|
@ -6176,7 +6188,7 @@ PLUGINDEX CSoundFile::GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority res
|
||||||
const ModChannel &channel = m_PlayState.Chn[nChn];
|
const ModChannel &channel = m_PlayState.Chn[nChn];
|
||||||
|
|
||||||
PLUGINDEX plugin;
|
PLUGINDEX plugin;
|
||||||
if((respectMutes == RespectMutes && channel.dwFlags[CHN_MUTE]) || channel.dwFlags[CHN_NOFX])
|
if((respectMutes == RespectMutes && channel.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) || channel.dwFlags[CHN_NOFX])
|
||||||
{
|
{
|
||||||
plugin = 0;
|
plugin = 0;
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -197,6 +197,8 @@ enum enmGetLengthResetMode
|
||||||
eAdjustOnSuccess = 0x02 | eAdjust,
|
eAdjustOnSuccess = 0x02 | eAdjust,
|
||||||
// Same as previous option, but will also try to emulate sample playback so that voices from previous patterns will sound when continuing playback at the target position.
|
// Same as previous option, but will also try to emulate sample playback so that voices from previous patterns will sound when continuing playback at the target position.
|
||||||
eAdjustSamplePositions = 0x04 | eAdjustOnSuccess,
|
eAdjustSamplePositions = 0x04 | eAdjustOnSuccess,
|
||||||
|
// Only adjust the visited rows state
|
||||||
|
eAdjustOnlyVisitedRows = 0x08,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -707,6 +709,9 @@ public:
|
||||||
ORDERINDEX GetCurrentOrder() const { return m_PlayState.m_nCurrentOrder; }
|
ORDERINDEX GetCurrentOrder() const { return m_PlayState.m_nCurrentOrder; }
|
||||||
CHANNELINDEX GetNumChannels() const { return m_nChannels; }
|
CHANNELINDEX GetNumChannels() const { return m_nChannels; }
|
||||||
|
|
||||||
|
constexpr bool CanAddMoreSamples(SAMPLEINDEX amount = 1) const noexcept { return (amount < MAX_SAMPLES) && m_nSamples < (MAX_SAMPLES - amount); }
|
||||||
|
constexpr bool CanAddMoreInstruments(INSTRUMENTINDEX amount = 1) const noexcept { return (amount < MAX_INSTRUMENTS) && m_nInstruments < (MAX_INSTRUMENTS - amount); }
|
||||||
|
|
||||||
#ifndef NO_PLUGINS
|
#ifndef NO_PLUGINS
|
||||||
IMixPlugin* GetInstrumentPlugin(INSTRUMENTINDEX instr) const;
|
IMixPlugin* GetInstrumentPlugin(INSTRUMENTINDEX instr) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -608,8 +608,23 @@ bool CSoundFile::ProcessRow()
|
||||||
// Let's check again if this really is the end of the song.
|
// Let's check again if this really is the end of the song.
|
||||||
// The visited rows vector might have been screwed up while editing...
|
// The visited rows vector might have been screwed up while editing...
|
||||||
// This is of course not possible during rendering to WAV, so we ignore that case.
|
// This is of course not possible during rendering to WAV, so we ignore that case.
|
||||||
GetLengthType t = GetLength(eNoAdjust).back();
|
bool isReallyAtEnd = false;
|
||||||
if(IsRenderingToDisc() || (t.lastOrder == m_PlayState.m_nCurrentOrder && t.lastRow == m_PlayState.m_nRow))
|
if(IsRenderingToDisc())
|
||||||
|
{
|
||||||
|
isReallyAtEnd = true;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
for(const auto &t : GetLength(eNoAdjust, GetLengthTarget(true)))
|
||||||
|
{
|
||||||
|
if(t.lastOrder == m_PlayState.m_nCurrentOrder && t.lastRow == m_PlayState.m_nRow)
|
||||||
|
{
|
||||||
|
isReallyAtEnd = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isReallyAtEnd)
|
||||||
{
|
{
|
||||||
// This is really the song's end!
|
// This is really the song's end!
|
||||||
visitedSongRows.Initialize(true);
|
visitedSongRows.Initialize(true);
|
||||||
|
@ -617,7 +632,7 @@ bool CSoundFile::ProcessRow()
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// Ok, this is really dirty, but we have to update the visited rows vector...
|
// Ok, this is really dirty, but we have to update the visited rows vector...
|
||||||
GetLength(eAdjustOnSuccess, GetLengthTarget(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow));
|
GetLength(eAdjustOnlyVisitedRows, GetLengthTarget(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if(m_SongFlags[SONG_PLAYALLSONGS])
|
if(m_SongFlags[SONG_PLAYALLSONGS])
|
||||||
|
|
|
@ -93,7 +93,7 @@ static constexpr ModFormatInfo modFormatInfo[] =
|
||||||
{ MOD_TYPE_STM, UL_("ScreamTracker 2"), "stm" },
|
{ MOD_TYPE_STM, UL_("ScreamTracker 2"), "stm" },
|
||||||
{ MOD_TYPE_STP, UL_("Soundtracker Pro II"), "stp" },
|
{ MOD_TYPE_STP, UL_("Soundtracker Pro II"), "stp" },
|
||||||
{ MOD_TYPE_ULT, UL_("UltraTracker"), "ult" },
|
{ MOD_TYPE_ULT, UL_("UltraTracker"), "ult" },
|
||||||
{ MOD_TYPE_MOD, UL_("Grave Composer"), "wow" },
|
{ MOD_TYPE_MOD, UL_("Mod's Grave"), "wow" },
|
||||||
// converted formats (no MODTYPE)
|
// converted formats (no MODTYPE)
|
||||||
{ MOD_TYPE_NONE, UL_("General Digital Music"), "gdm" },
|
{ MOD_TYPE_NONE, UL_("General Digital Music"), "gdm" },
|
||||||
{ MOD_TYPE_NONE, UL_("Un4seen MO3"), "mo3" },
|
{ MOD_TYPE_NONE, UL_("Un4seen MO3"), "mo3" },
|
||||||
|
|
|
@ -255,6 +255,7 @@ void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharse
|
||||||
cueChunk.ReadStruct(cuePoint);
|
cueChunk.ReadStruct(cuePoint);
|
||||||
sample.cues[i] = cuePoint.position;
|
sample.cues[i] = cuePoint.position;
|
||||||
}
|
}
|
||||||
|
std::fill(std::begin(sample.cues) + numPoints, std::end(sample.cues), MAX_SAMPLE_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read MPT extra info
|
// Read MPT extra info
|
||||||
|
@ -598,16 +599,25 @@ void WAVWriter::WriteLoopInformation(const ModSample &sample)
|
||||||
// Write a sample's cue points to the file.
|
// Write a sample's cue points to the file.
|
||||||
void WAVWriter::WriteCueInformation(const ModSample &sample)
|
void WAVWriter::WriteCueInformation(const ModSample &sample)
|
||||||
{
|
{
|
||||||
StartChunk(RIFFChunk::idcue_);
|
uint32 numMarkers = 0;
|
||||||
|
for(const auto cue : sample.cues)
|
||||||
{
|
{
|
||||||
Write(mpt::as_le(static_cast<uint32>(CountOf(sample.cues))));
|
if(cue < sample.nLength)
|
||||||
|
numMarkers++;
|
||||||
}
|
}
|
||||||
for(uint32 i = 0; i < CountOf(sample.cues); i++)
|
|
||||||
|
StartChunk(RIFFChunk::idcue_);
|
||||||
|
Write(mpt::as_le(numMarkers));
|
||||||
|
uint32 i = 0;
|
||||||
|
for(const auto cue : sample.cues)
|
||||||
|
{
|
||||||
|
if(cue < sample.nLength)
|
||||||
{
|
{
|
||||||
WAVCuePoint cuePoint;
|
WAVCuePoint cuePoint;
|
||||||
cuePoint.ConvertToWAV(i, sample.cues[i]);
|
cuePoint.ConvertToWAV(i++, cue);
|
||||||
Write(cuePoint);
|
Write(cuePoint);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,7 @@ struct AMEnvelope
|
||||||
struct EnvPoint
|
struct EnvPoint
|
||||||
{
|
{
|
||||||
uint16le tick;
|
uint16le tick;
|
||||||
uint16le value;
|
int16le value;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16le flags;
|
uint16le flags;
|
||||||
|
@ -326,6 +326,23 @@ struct AMEnvelope
|
||||||
mptEnv.nLoopStart = loopStart;
|
mptEnv.nLoopStart = loopStart;
|
||||||
mptEnv.nLoopEnd = loopEnd;
|
mptEnv.nLoopEnd = loopEnd;
|
||||||
|
|
||||||
|
int32 scale = 0, offset = 0;
|
||||||
|
switch(envType)
|
||||||
|
{
|
||||||
|
case ENV_VOLUME: // 0....32767
|
||||||
|
default:
|
||||||
|
scale = 32767 / ENVELOPE_MAX;
|
||||||
|
break;
|
||||||
|
case ENV_PITCH: // -4096....4096
|
||||||
|
scale = 8192 / ENVELOPE_MAX;
|
||||||
|
offset = 4096;
|
||||||
|
break;
|
||||||
|
case ENV_PANNING: // -32768...32767
|
||||||
|
scale = 65536 / ENVELOPE_MAX;
|
||||||
|
offset = 32768;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
for(uint32 i = 0; i < mptEnv.size(); i++)
|
for(uint32 i = 0; i < mptEnv.size(); i++)
|
||||||
{
|
{
|
||||||
mptEnv[i].tick = values[i].tick >> 4;
|
mptEnv[i].tick = values[i].tick >> 4;
|
||||||
|
@ -334,21 +351,9 @@ struct AMEnvelope
|
||||||
else if(mptEnv[i].tick < mptEnv[i - 1].tick)
|
else if(mptEnv[i].tick < mptEnv[i - 1].tick)
|
||||||
mptEnv[i].tick = mptEnv[i - 1].tick + 1;
|
mptEnv[i].tick = mptEnv[i - 1].tick + 1;
|
||||||
|
|
||||||
const uint16 val = values[i].value;
|
int32 val = values[i].value + offset;
|
||||||
switch(envType)
|
val = (val + scale / 2) / scale;
|
||||||
{
|
mptEnv[i].value = static_cast<EnvelopeNode::value_t>(std::clamp(val, int32(ENVELOPE_MIN), int32(ENVELOPE_MAX)));
|
||||||
case ENV_VOLUME: // 0....32767
|
|
||||||
default:
|
|
||||||
mptEnv[i].value = (uint8)((val + 1) >> 9);
|
|
||||||
break;
|
|
||||||
case ENV_PITCH: // -4096....4096
|
|
||||||
mptEnv[i].value = (uint8)((((int16)val) + 0x1001) >> 7);
|
|
||||||
break;
|
|
||||||
case ENV_PANNING: // -32768...32767
|
|
||||||
mptEnv[i].value = (uint8)((((int16)val) + 0x8001) >> 10);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Limit(mptEnv[i].value, uint8(ENVELOPE_MIN), uint8(ENVELOPE_MAX));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mptEnv.dwFlags.set(ENV_ENABLED, (flags & AMFFEnvelope::envEnabled) != 0);
|
mptEnv.dwFlags.set(ENV_ENABLED, (flags & AMFFEnvelope::envEnabled) != 0);
|
||||||
|
@ -825,7 +830,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
{
|
{
|
||||||
AMFFSampleHeader sampleHeader;
|
AMFFSampleHeader sampleHeader;
|
||||||
|
|
||||||
if(m_nSamples + 1 >= MAX_SAMPLES || !chunk.ReadStruct(sampleHeader))
|
if(!CanAddMoreSamples() || !chunk.ReadStruct(sampleHeader))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -887,7 +892,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
for(auto sampleChunk : sampleChunks)
|
for(auto sampleChunk : sampleChunks)
|
||||||
{
|
{
|
||||||
if(sampleChunk.ReadUint32LE() != AMFFRiffChunk::idAS__ || m_nSamples + 1 >= MAX_SAMPLES)
|
if(sampleChunk.ReadUint32LE() != AMFFRiffChunk::idAS__ || !CanAddMoreSamples())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ constexpr CModSpecifications xm_ =
|
||||||
false, // Doesn't have artist name
|
false, // Doesn't have artist name
|
||||||
false, // Doesn't have default resampling
|
false, // Doesn't have default resampling
|
||||||
false, // Integer tempo
|
false, // Integer tempo
|
||||||
" 0123456789ABCDRFFTE???GHK??XPL????????????", // Supported Effects
|
" 0123456789ABCDRFFTE???GHK??XPL???????????W", // Supported Effects
|
||||||
" vpcdabuhlrg????", // Supported Volume Column commands
|
" vpcdabuhlrg????", // Supported Volume Column commands
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ constexpr CModSpecifications xmEx_ =
|
||||||
true, // Has artist name
|
true, // Has artist name
|
||||||
false, // Doesn't have default resampling
|
false, // Doesn't have default resampling
|
||||||
false, // Integer tempo
|
false, // Integer tempo
|
||||||
" 0123456789ABCDRFFTE???GHK?YXPLZ\\?#????????", // Supported Effects
|
" 0123456789ABCDRFFTE???GHK?YXPLZ\\?#???????W", // Supported Effects
|
||||||
" vpcdabuhlrg????", // Supported Volume Column commands
|
" vpcdabuhlrg????", // Supported Volume Column commands
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -112,15 +112,7 @@ bool CPatternContainer::IsPatternEmpty(const PATTERNINDEX nPat) const
|
||||||
|
|
||||||
void CPatternContainer::ResizeArray(const PATTERNINDEX newSize)
|
void CPatternContainer::ResizeArray(const PATTERNINDEX newSize)
|
||||||
{
|
{
|
||||||
if(Size() <= newSize)
|
|
||||||
{
|
|
||||||
m_Patterns.resize(newSize, CPattern(*this));
|
m_Patterns.resize(newSize, CPattern(*this));
|
||||||
} else
|
|
||||||
{
|
|
||||||
for(PATTERNINDEX i = Size(); i > newSize; i--)
|
|
||||||
Remove(i - 1);
|
|
||||||
m_Patterns.resize(newSize, CPattern(*this));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2000,7 +2000,7 @@ static MPT_NOINLINE void TestMisc2()
|
||||||
VERIFY_EQUAL(mpt::crc32_ogg(std::string("123456789")), 0x89a1897fu);
|
VERIFY_EQUAL(mpt::crc32_ogg(std::string("123456789")), 0x89a1897fu);
|
||||||
|
|
||||||
// Check floating-point accuracy in TransposeToFrequency
|
// Check floating-point accuracy in TransposeToFrequency
|
||||||
int32 transposeToFrequency[] =
|
static constexpr int32 transposeToFrequency[] =
|
||||||
{
|
{
|
||||||
5, 5, 5, 5,
|
5, 5, 5, 5,
|
||||||
31, 32, 33, 34,
|
31, 32, 33, 34,
|
||||||
|
@ -2014,8 +2014,23 @@ static MPT_NOINLINE void TestMisc2()
|
||||||
|
|
||||||
int freqIndex = 0;
|
int freqIndex = 0;
|
||||||
for(int32 transpose = -128; transpose < 128; transpose += 32)
|
for(int32 transpose = -128; transpose < 128; transpose += 32)
|
||||||
|
{
|
||||||
for(int32 finetune = -128; finetune < 128; finetune += 64, freqIndex++)
|
for(int32 finetune = -128; finetune < 128; finetune += 64, freqIndex++)
|
||||||
VERIFY_EQUAL_EPS(transposeToFrequency[freqIndex], static_cast<int32>(ModSample::TransposeToFrequency(transpose, finetune)), 1);
|
{
|
||||||
|
const auto freq = ModSample::TransposeToFrequency(transpose, finetune);
|
||||||
|
VERIFY_EQUAL_EPS(transposeToFrequency[freqIndex], static_cast<int32>(freq), 1);
|
||||||
|
if(transpose >= -96)
|
||||||
|
{
|
||||||
|
// Verify transpose+finetune <-> frequency roundtrip
|
||||||
|
// (not for transpose = -128 because it would require fractional precision that we don't have here)
|
||||||
|
ModSample smp;
|
||||||
|
smp.nC5Speed = freq;
|
||||||
|
smp.FrequencyToTranspose();
|
||||||
|
smp.TransposeToFrequency();
|
||||||
|
VERIFY_EQUAL(freq, smp.nC5Speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ModSample smp;
|
ModSample smp;
|
||||||
|
@ -3043,7 +3058,7 @@ static void TestLoadXMFile(const CSoundFile &sndFile)
|
||||||
|
|
||||||
// Global Variables
|
// Global Variables
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module");
|
VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.at(0), 'O');
|
VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 0));
|
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 0));
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5);
|
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5);
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128);
|
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128);
|
||||||
|
@ -3243,7 +3258,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile)
|
||||||
|
|
||||||
// Global Variables
|
// Global Variables
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module_____________X");
|
VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module_____________X");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.at(0), 'O');
|
VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 999));
|
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 999));
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5);
|
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5);
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128);
|
VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128);
|
||||||
|
@ -3271,7 +3286,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile)
|
||||||
|
|
||||||
// Edit history
|
// Edit history
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.GetFileHistory().size() > 0, true);
|
VERIFY_EQUAL_NONCONT(sndFile.GetFileHistory().size() > 0, true);
|
||||||
const FileHistory &fh = sndFile.GetFileHistory().at(0);
|
const FileHistory &fh = sndFile.GetFileHistory().front();
|
||||||
VERIFY_EQUAL_NONCONT(fh.loadDate.tm_year, 111);
|
VERIFY_EQUAL_NONCONT(fh.loadDate.tm_year, 111);
|
||||||
VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mon, 5);
|
VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mon, 5);
|
||||||
VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mday, 14);
|
VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mday, 14);
|
||||||
|
@ -4078,21 +4093,21 @@ static MPT_NOINLINE void TestEditing()
|
||||||
sndFile.GetSample(2).AllocateSample();
|
sndFile.GetSample(2).AllocateSample();
|
||||||
modDoc->ReArrangeSamples({ 2, SAMPLEINDEX_INVALID, 1 });
|
modDoc->ReArrangeSamples({ 2, SAMPLEINDEX_INVALID, 1 });
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).HasSampleData(), true);
|
VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).HasSampleData(), true);
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).filename, std::string("2"));
|
VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).filename, "2");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], std::string("2"));
|
VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "2");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.GetSample(2).filename, std::string());
|
VERIFY_EQUAL_NONCONT(sndFile.GetSample(2).filename, "");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], std::string());
|
VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], "");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.GetSample(3).filename, std::string("1"));
|
VERIFY_EQUAL_NONCONT(sndFile.GetSample(3).filename, "1");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], std::string("1"));
|
VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "1");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3);
|
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3);
|
||||||
|
|
||||||
// Convert / rearrange instruments
|
// Convert / rearrange instruments
|
||||||
modDoc->ConvertSamplesToInstruments();
|
modDoc->ConvertSamplesToInstruments();
|
||||||
modDoc->ReArrangeInstruments({ INSTRUMENTINDEX_INVALID, 2, 1, 3 });
|
modDoc->ReArrangeInstruments({ INSTRUMENTINDEX_INVALID, 2, 1, 3 });
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.Instruments[1]->name, std::string());
|
VERIFY_EQUAL_NONCONT(sndFile.Instruments[1]->name, "");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.Instruments[2]->name, std::string());
|
VERIFY_EQUAL_NONCONT(sndFile.Instruments[2]->name, "");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.Instruments[3]->name, std::string("2"));
|
VERIFY_EQUAL_NONCONT(sndFile.Instruments[3]->name, "2");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.Instruments[4]->name, std::string("1"));
|
VERIFY_EQUAL_NONCONT(sndFile.Instruments[4]->name, "1");
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 4);
|
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 4);
|
||||||
modDoc->ConvertInstrumentsToSamples();
|
modDoc->ConvertInstrumentsToSamples();
|
||||||
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3);
|
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3);
|
||||||
|
|
Loading…
Reference in New Issue