Updated libopenmpt to version 0.3.9.

CQTexperiment
Chris Moeller 2018-04-30 20:48:06 -07:00
parent 186d432108
commit ae22f79bad
13 changed files with 113 additions and 63 deletions

View File

@ -629,6 +629,13 @@ mpt::ustring GetFullCreditsString()
"http://www.hermannseib.com/english/vsthost.htm\n" "http://www.hermannseib.com/english/vsthost.htm\n"
"\n" "\n"
#endif #endif
"Storlek for all the IT compatibility hints and testcases\n"
"as well as the IMF, MDL, OKT and ULT loaders\n"
"http://schismtracker.org/\n"
"\n"
"Sergei \"x0r\" Kolzun for various hints on Scream Tracker 2 compatibility\n"
"https://github.com/viiri/st2play\n"
"\n"
"Laurent Cl\xc3\xA9vy for unofficial MO3 documentation and decompression code\n" "Laurent Cl\xc3\xA9vy for unofficial MO3 documentation and decompression code\n"
"https://github.com/lclevy/unmo3\n" "https://github.com/lclevy/unmo3\n"
"\n" "\n"
@ -716,10 +723,6 @@ mpt::ustring GetFullCreditsString()
"https://github.com/kazuho/picojson\n" "https://github.com/kazuho/picojson\n"
"\n" "\n"
#endif #endif
"Storlek for all the IT compatibility hints and testcases\n"
"as well as the IMF, MDL, OKT and ULT loaders\n"
"http://schismtracker.org/\n"
"\n"
#ifdef MODPLUG_TRACKER #ifdef MODPLUG_TRACKER
"Lennart Poettering and David Henningsson for RealtimeKit\n" "Lennart Poettering and David Henningsson for RealtimeKit\n"
"http://git.0pointer.net/rtkit.git/\n" "http://git.0pointer.net/rtkit.git/\n"

View File

@ -19,7 +19,7 @@ OPENMPT_NAMESPACE_BEGIN
#define VER_MAJORMAJOR 1 #define VER_MAJORMAJOR 1
#define VER_MAJOR 27 #define VER_MAJOR 27
#define VER_MINOR 07 #define VER_MINOR 07
#define VER_MINORMINOR 00 #define VER_MINORMINOR 02
//Version string. For example "1.17.02.28" //Version string. For example "1.17.02.28"
#define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR) #define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR)

View File

@ -5,10 +5,26 @@ 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.3.9 (2018-04-29)
* [**Sec**] Possible write near address 0 in out-of-memory situations when
reading AMS files (r10149).
* [**Bug**] openmpt123: Fixed build failure in C++17 due to use of removed
feature `std::random_shuffle`.
* STM: Having both Bxx and Cxx commands in a pattern imported the Bxx command
incorrectly.
* STM: Last character of sample name was missing.
* Speed up reading of truncated ULT files.
* ULT: Portamento import was sometimes broken.
* The resonant filter was sometimes unstable when combining low-volume
samples, low cutoff and high mixing rates.
### libopenmpt 0.3.8 (2018-04-08) ### libopenmpt 0.3.8 (2018-04-08)
* [**Sec**] Possible out-of-bounds memory read with IT / ITP / MO3 files * [**Sec**] Possible out-of-bounds memory read with IT and MO3 files
containing pattern loops (r10028). containing many nested pattern loops (r10028).
* Keep track of active SFx macro during seeking. * Keep track of active SFx macro during seeking.
* The "note cut" duplicate note action did not volume-ramp the previously * The "note cut" duplicate note action did not volume-ramp the previously
@ -60,7 +76,6 @@ is just a high-level summary.
* Tighten M15 and MOD file rejection heuristics. * Tighten M15 and MOD file rejection heuristics.
* J2B: Ignore frequency limits from file header. Fixes Medivo.j2b, broken * J2B: Ignore frequency limits from file header. Fixes Medivo.j2b, broken
since libopenmpt-0.2.6401-beta17. since libopenmpt-0.2.6401-beta17.
* STM: Last character of sample name was missing.
* ParamEq plugin center frequency was not limited correctly. * ParamEq plugin center frequency was not limited correctly.
* libopenmpt_ext C API was not included in the documentation. * libopenmpt_ext C API was not included in the documentation.

View File

@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */ /*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 3 #define OPENMPT_API_VERSION_MINOR 3
/*! \brief libopenmpt patch version number */ /*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 8 #define OPENMPT_API_VERSION_PATCH 9
/*! \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 */

View File

@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=3 LIBOPENMPT_VERSION_MINOR=3
LIBOPENMPT_VERSION_PATCH=8 LIBOPENMPT_VERSION_PATCH=9
LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=1 LIBOPENMPT_LTVER_CURRENT=1
LIBOPENMPT_LTVER_REVISION=8 LIBOPENMPT_LTVER_REVISION=9
LIBOPENMPT_LTVER_AGE=1 LIBOPENMPT_LTVER_AGE=1

View File

@ -290,7 +290,7 @@ static void load_settings_from_map( libopenmpt::plugin::settings & s, const std:
static void load_settings_from_xml( libopenmpt::plugin::settings & s, const std::string & xml ) { static void load_settings_from_xml( libopenmpt::plugin::settings & s, const std::string & xml ) {
pugi::xml_document doc; pugi::xml_document doc;
doc.load( xml.c_str() ); doc.load_string( xml.c_str() );
pugi::xml_node settings_node = doc.child( "settings" ); pugi::xml_node settings_node = doc.child( "settings" );
std::map<std::string,int> map; std::map<std::string,int> map;
for ( pugi::xml_attribute_iterator it = settings_node.attributes_begin(); it != settings_node.attributes_end(); ++it ) { for ( pugi::xml_attribute_iterator it = settings_node.attributes_begin(); it != settings_node.attributes_end(); ++it ) {

View File

@ -47,6 +47,7 @@ static const char * const license =
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <map> #include <map>
#include <random>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -1754,18 +1755,17 @@ static void render_file( commandlineflags & flags, const std::string & filename,
} }
static std::string get_random_filename(std::set<std::string> & filenames) { static std::string get_random_filename( std::set<std::string> & filenames, std::default_random_engine & prng ) {
// TODO: actually use a useful random distribution std::size_t index = std::uniform_int_distribution<std::size_t>( 0, filenames.size() - 1 )( prng );
std::size_t index = std::rand() % filenames.size();
std::set<std::string>::iterator it = filenames.begin(); std::set<std::string>::iterator it = filenames.begin();
std::advance( it, index ); std::advance( it, index );
return *it; return *it;
} }
static void render_files( commandlineflags & flags, textout & log, write_buffers_interface & audio_stream ) { static void render_files( commandlineflags & flags, textout & log, write_buffers_interface & audio_stream, std::default_random_engine & prng ) {
if ( flags.randomize ) { if ( flags.randomize ) {
std::random_shuffle( flags.filenames.begin(), flags.filenames.end() ); std::shuffle( flags.filenames.begin(), flags.filenames.end(), prng );
} }
try { try {
while ( true ) { while ( true ) {
@ -1777,7 +1777,7 @@ static void render_files( commandlineflags & flags, textout & log, write_buffers
if ( shuffle_set.empty() ) { if ( shuffle_set.empty() ) {
break; break;
} }
std::string filename = get_random_filename( shuffle_set ); std::string filename = get_random_filename( shuffle_set, prng );
try { try {
flags.playlist_index = std::find( flags.filenames.begin(), flags.filenames.end(), filename ) - flags.filenames.begin(); flags.playlist_index = std::find( flags.filenames.begin(), flags.filenames.end(), filename ) - flags.filenames.begin();
render_file( flags, filename, log, audio_stream ); render_file( flags, filename, log, audio_stream );
@ -2393,7 +2393,10 @@ static int main( int argc, char * argv [] ) {
log.writeout(); log.writeout();
std::srand( static_cast<unsigned int>( std::time( NULL ) ) ); std::random_device rd;
std::seed_seq seq{ rd(), static_cast<unsigned int>( std::time( NULL ) ) };
std::default_random_engine prng( seq );
std::srand( std::uniform_int_distribution<unsigned int>()( prng ) );
switch ( flags.mode ) { switch ( flags.mode ) {
case ModeProbe: { case ModeProbe: {
@ -2404,42 +2407,42 @@ static int main( int argc, char * argv [] ) {
} break; } break;
case ModeInfo: { case ModeInfo: {
void_audio_stream dummy; void_audio_stream dummy;
render_files( flags, log, dummy ); render_files( flags, log, dummy, prng );
} break; } break;
case ModeUI: case ModeUI:
case ModeBatch: { case ModeBatch: {
if ( flags.use_stdout ) { if ( flags.use_stdout ) {
flags.apply_default_buffer_sizes(); flags.apply_default_buffer_sizes();
stdout_stream_raii stdout_audio_stream; stdout_stream_raii stdout_audio_stream;
render_files( flags, log, stdout_audio_stream ); render_files( flags, log, stdout_audio_stream, prng );
} else if ( !flags.output_filename.empty() ) { } else if ( !flags.output_filename.empty() ) {
flags.apply_default_buffer_sizes(); flags.apply_default_buffer_sizes();
file_audio_stream_raii file_audio_stream( flags, flags.output_filename, log ); file_audio_stream_raii file_audio_stream( flags, flags.output_filename, log );
render_files( flags, log, file_audio_stream ); render_files( flags, log, file_audio_stream, prng );
#if defined( MPT_WITH_PULSEAUDIO ) #if defined( MPT_WITH_PULSEAUDIO )
} else if ( flags.driver == "pulseaudio" || flags.driver.empty() ) { } else if ( flags.driver == "pulseaudio" || flags.driver.empty() ) {
pulseaudio_stream_raii pulseaudio_stream( flags, log ); pulseaudio_stream_raii pulseaudio_stream( flags, log );
render_files( flags, log, pulseaudio_stream ); render_files( flags, log, pulseaudio_stream, prng );
#endif #endif
#if defined( MPT_WITH_SDL2 ) #if defined( MPT_WITH_SDL2 )
} else if ( flags.driver == "sdl2" || flags.driver.empty() ) { } else if ( flags.driver == "sdl2" || flags.driver.empty() ) {
sdl2_stream_raii sdl2_stream( flags, log ); sdl2_stream_raii sdl2_stream( flags, log );
render_files( flags, log, sdl2_stream ); render_files( flags, log, sdl2_stream, prng );
#endif #endif
#if defined( MPT_WITH_SDL ) #if defined( MPT_WITH_SDL )
} else if ( flags.driver == "sdl" || flags.driver.empty() ) { } else if ( flags.driver == "sdl" || flags.driver.empty() ) {
sdl_stream_raii sdl_stream( flags, log ); sdl_stream_raii sdl_stream( flags, log );
render_files( flags, log, sdl_stream ); render_files( flags, log, sdl_stream, prng );
#endif #endif
#if defined( MPT_WITH_PORTAUDIO ) #if defined( MPT_WITH_PORTAUDIO )
} else if ( flags.driver == "portaudio" || flags.driver.empty() ) { } else if ( flags.driver == "portaudio" || flags.driver.empty() ) {
portaudio_stream_raii portaudio_stream( flags, log ); portaudio_stream_raii portaudio_stream( flags, log );
render_files( flags, log, portaudio_stream ); render_files( flags, log, portaudio_stream, prng );
#endif #endif
#if defined( WIN32 ) #if defined( WIN32 )
} else if ( flags.driver == "waveout" || flags.driver.empty() ) { } else if ( flags.driver == "waveout" || flags.driver.empty() ) {
waveout_stream_raii waveout_stream( flags ); waveout_stream_raii waveout_stream( flags );
render_files( flags, log, waveout_stream ); render_files( flags, log, waveout_stream, prng );
#endif #endif
} else { } else {
if ( flags.driver.empty() ) { if ( flags.driver.empty() ) {

View File

@ -361,8 +361,10 @@ struct ResonantFilter
} }
} }
// To avoid a precision loss in the state variables especially with quiet samples at low cutoff and high mix rate, we pre-amplify the sample.
#define MIXING_FILTER_PREAMP 256
// Filter values are clipped to double the input range // Filter values are clipped to double the input range
#define ClipFilter(x) Clamp<typename Traits::output_t, typename Traits::output_t>(x, int16_min * 2, int16_max * 2) #define ClipFilter(x) Clamp<typename Traits::output_t, typename Traits::output_t>(x, int16_min * 2 * MIXING_FILTER_PREAMP, int16_max * 2 * MIXING_FILTER_PREAMP)
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn) MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn)
{ {
@ -370,14 +372,15 @@ struct ResonantFilter
for(int i = 0; i < Traits::numChannelsIn; i++) for(int i = 0; i < Traits::numChannelsIn; i++)
{ {
typename Traits::output_t val = static_cast<typename Traits::output_t>(( const auto inputAmp = outSample[i] * MIXING_FILTER_PREAMP;
Util::mul32to64(outSample[i], chn.nFilter_A0) + typename Traits::output_t val = static_cast<typename Traits::output_t>(mpt::rshift_signed(
Util::mul32to64(inputAmp, chn.nFilter_A0) +
Util::mul32to64(ClipFilter(fy[i][0]), chn.nFilter_B0) + Util::mul32to64(ClipFilter(fy[i][0]), chn.nFilter_B0) +
Util::mul32to64(ClipFilter(fy[i][1]), chn.nFilter_B1) + Util::mul32to64(ClipFilter(fy[i][1]), chn.nFilter_B1) +
(1 << (MIXING_FILTER_PRECISION - 1))) / (1 << MIXING_FILTER_PRECISION)); (1 << (MIXING_FILTER_PRECISION - 1)), MIXING_FILTER_PRECISION));
fy[i][1] = fy[i][0]; fy[i][1] = fy[i][0];
fy[i][0] = val - (outSample[i] & chn.nFilter_HP); fy[i][0] = val - (inputAmp & chn.nFilter_HP);
outSample[i] = val; outSample[i] = val / MIXING_FILTER_PREAMP;
} }
} }

View File

@ -179,7 +179,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
if(sampleHeader.zero != 0 && sampleHeader.zero != 46) // putup10.stm has zero = 46 if(sampleHeader.zero != 0 && sampleHeader.zero != 46) // putup10.stm has zero = 46
return false; return false;
sampleHeader.ConvertToMPT(Samples[smp]); sampleHeader.ConvertToMPT(Samples[smp]);
mpt::String::Read<mpt::String::nullTerminated>(m_szNames[smp], sampleHeader.filename); mpt::String::Read<mpt::String::maybeNullTerminated>(m_szNames[smp], sampleHeader.filename);
sampleOffsets[smp - 1] = sampleHeader.offset; sampleOffsets[smp - 1] = sampleHeader.offset;
} }
@ -212,7 +212,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
ORDERINDEX breakPos = ORDERINDEX_INVALID; ORDERINDEX breakPos = ORDERINDEX_INVALID;
ROWINDEX breakRow = 63; // Candidate row for inserting pattern break ROWINDEX breakRow = 63; // Candidate row for inserting pattern break
for(int i = 0; i < 64 * 4; i++, m++) for(unsigned int i = 0; i < 64 * 4; i++, m++)
{ {
uint8 note = file.ReadUint8(), insvol, volcmd, cmdinf; uint8 note = file.ReadUint8(), insvol, volcmd, cmdinf;
switch(note) switch(note)
@ -226,9 +226,13 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
m->note = NOTE_NOTECUT; m->note = NOTE_NOTECUT;
continue; continue;
default: default:
insvol = file.ReadUint8(); {
volcmd = file.ReadUint8(); uint8 patData[3];
cmdinf = file.ReadUint8(); file.ReadArray(patData);
insvol = patData[0];
volcmd = patData[1];
cmdinf = patData[2];
}
break; break;
} }
@ -266,16 +270,22 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
{ {
case CMD_VOLUMESLIDE: case CMD_VOLUMESLIDE:
// Lower nibble always has precedence, and there are no fine slides. // Lower nibble always has precedence, and there are no fine slides.
if(m->param & 0x0F) m->param &= 0x0F; if(m->param & 0x0F)
else m->param &= 0xF0; m->param &= 0x0F;
else
m->param &= 0xF0;
break; break;
case CMD_PATTERNBREAK: case CMD_PATTERNBREAK:
m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F);
if(breakRow > m->param) if(breakPos != ORDERINDEX_INVALID && m->param == 0)
{ {
breakRow = m->param; // Merge Bxx + C00 into just Bxx
m->command = CMD_POSITIONJUMP;
m->param = static_cast<ModCommand::PARAM>(breakPos);
breakPos = ORDERINDEX_INVALID;
} }
LimitMax(breakRow, i / 4u);
break; break;
case CMD_POSITIONJUMP: case CMD_POSITIONJUMP:

View File

@ -85,6 +85,17 @@ struct UltSample
MPT_BINARY_STRUCT(UltSample, 66) MPT_BINARY_STRUCT(UltSample, 66)
struct UltPatternCommand
{
uint8 instr;
uint8 cmd;
uint8 param1;
uint8 param2;
};
MPT_BINARY_STRUCT(UltPatternCommand, 4)
/* Unhandled effects: /* Unhandled effects:
5x1 - do not loop sample (x is unused) 5x1 - do not loop sample (x is unused)
9xx - set sample offset to xx * 1024 9xx - set sample offset to xx * 1024
@ -215,13 +226,14 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
b = file.ReadUint8(); b = file.ReadUint8();
} }
m.note = (b > 0 && b < 61) ? b + 36 : NOTE_NONE; m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE;
m.instr = file.ReadUint8(); UltPatternCommand patCmd;
b = file.ReadUint8(); file.ReadStruct(patCmd);
cmd1 = b & 0x0F; m.instr = patCmd.instr;
cmd2 = b >> 4; cmd1 = patCmd.cmd & 0x0F;
param1 = file.ReadUint8(); cmd2 = patCmd.cmd >> 4;
param2 = file.ReadUint8(); param1 = patCmd.param1;
param2 = patCmd.param2;
TranslateULTCommands(cmd1, param1, version); TranslateULTCommands(cmd1, param1, version);
TranslateULTCommands(cmd2, param2, version); TranslateULTCommands(cmd2, param2, version);
@ -295,7 +307,7 @@ struct PostFixUltCommands
// Apply porta? // Apply porta?
if(m.note == NOTE_NONE && isPortaActive[curChannel]) if(m.note == NOTE_NONE && isPortaActive[curChannel])
{ {
if(m.command == CMD_NONE && m.vol != VOLCMD_TONEPORTAMENTO) if(m.command == CMD_NONE && m.volcmd != VOLCMD_TONEPORTAMENTO)
{ {
m.command = CMD_TONEPORTAMENTO; m.command = CMD_TONEPORTAMENTO;
m.param = 0; m.param = 0;
@ -345,6 +357,10 @@ static bool ValidateHeader(const UltFileHeader &fileHeader)
return true; return true;
} }
static uint64 GetHeaderMinimumAdditionalSize(const UltFileHeader &fileHeader)
{
return fileHeader.messageLength * 32u;
}
CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize)
{ {
@ -357,8 +373,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, co
{ {
return ProbeFailure; return ProbeFailure;
} }
MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
return ProbeSuccess;
} }
@ -379,6 +394,10 @@ bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags)
{ {
return true; return true;
} }
if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
{
return false;
}
InitializeGlobals(MOD_TYPE_ULT); InitializeGlobals(MOD_TYPE_ULT);
mpt::String::Read<mpt::String::maybeNullTerminated>(m_songName, fileHeader.songName); mpt::String::Read<mpt::String::maybeNullTerminated>(m_songName, fileHeader.songName);
@ -442,12 +461,9 @@ bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags)
for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
{ {
ModCommand evnote; ModCommand evnote;
ModCommand *note; for(PATTERNINDEX pat = 0; pat < numPats && file.CanRead(5); pat++)
evnote.Clear();
for(PATTERNINDEX pat = 0; pat < numPats; pat++)
{ {
note = Patterns[pat].GetpModCommand(0, chn); ModCommand *note = Patterns[pat].GetpModCommand(0, chn);
ROWINDEX row = 0; ROWINDEX row = 0;
while(row < 64) while(row < 64)
{ {

View File

@ -16,7 +16,7 @@ OPENMPT_NAMESPACE_BEGIN
#ifdef MPT_INTMIXER #ifdef MPT_INTMIXER
typedef int32 mixsample_t; typedef int32 mixsample_t;
enum { MIXING_FILTER_PRECISION = 16 }; // Fixed point resonant filter bits enum { MIXING_FILTER_PRECISION = 24 }; // Fixed point resonant filter bits
#else #else
typedef float mixsample_t; typedef float mixsample_t;
#endif #endif

View File

@ -1665,7 +1665,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
} }
if(!region.name.empty()) if(!region.name.empty())
{ {
mpt::String::Copy(m_szNames[smp], region.name); mpt::String::Copy(m_szNames[smp], mpt::ToCharset(GetCharsetInternal(), mpt::CharsetUTF8, region.name));
} }
if(!m_szNames[smp][0]) if(!m_szNames[smp][0])
{ {

View File

@ -133,7 +133,7 @@ public:
const_iterator end() const { return m_ModCommands.end(); } const_iterator end() const { return m_ModCommands.end(); }
const_iterator cend() const { return m_ModCommands.cend(); } const_iterator cend() const { return m_ModCommands.cend(); }
CPattern(CPatternContainer& patCont) : m_ModCommands(0), m_Rows(64), m_RowsPerBeat(0), m_RowsPerMeasure(0), m_rPatternContainer(patCont) {}; CPattern(CPatternContainer& patCont) : m_rPatternContainer(patCont) {};
CPattern(const CPattern &) = default; CPattern(const CPattern &) = default;
CPattern(CPattern &&) noexcept = default; CPattern(CPattern &&) noexcept = default;
@ -148,9 +148,9 @@ protected:
//BEGIN: DATA //BEGIN: DATA
protected: protected:
std::vector<ModCommand> m_ModCommands; std::vector<ModCommand> m_ModCommands;
ROWINDEX m_Rows; ROWINDEX m_Rows = 0;
ROWINDEX m_RowsPerBeat; // patterns-specific time signature. if != 0, this is implicitely set. ROWINDEX m_RowsPerBeat = 0; // patterns-specific time signature. if != 0, this is implicitely set.
ROWINDEX m_RowsPerMeasure; // ditto ROWINDEX m_RowsPerMeasure = 0; // ditto
TempoSwing m_tempoSwing; TempoSwing m_tempoSwing;
std::string m_PatternName; std::string m_PatternName;
CPatternContainer& m_rPatternContainer; CPatternContainer& m_rPatternContainer;