From ae22f79bad67f2f5a58c8392445e85ad128d5013 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Mon, 30 Apr 2018 20:48:06 -0700 Subject: [PATCH] Updated libopenmpt to version 0.3.9. --- Frameworks/OpenMPT/OpenMPT/common/version.cpp | 11 +++-- .../OpenMPT/OpenMPT/common/versionNumber.h | 2 +- .../OpenMPT/libopenmpt/dox/changelog.md | 21 +++++++-- .../OpenMPT/libopenmpt/libopenmpt_version.h | 2 +- .../OpenMPT/libopenmpt/libopenmpt_version.mk | 4 +- .../OpenMPT/libopenmpt/xmp-openmpt.cpp | 2 +- .../OpenMPT/OpenMPT/openmpt123/openmpt123.cpp | 33 +++++++------ .../OpenMPT/OpenMPT/soundlib/IntMixer.h | 15 +++--- .../OpenMPT/OpenMPT/soundlib/Load_stm.cpp | 28 +++++++---- .../OpenMPT/OpenMPT/soundlib/Load_ult.cpp | 46 +++++++++++++------ Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h | 2 +- .../OpenMPT/soundlib/SampleFormats.cpp | 2 +- Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h | 8 ++-- 13 files changed, 113 insertions(+), 63 deletions(-) diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.cpp b/Frameworks/OpenMPT/OpenMPT/common/version.cpp index a47f2c401..c3406a919 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/version.cpp @@ -629,6 +629,13 @@ mpt::ustring GetFullCreditsString() "http://www.hermannseib.com/english/vsthost.htm\n" "\n" #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" "https://github.com/lclevy/unmo3\n" "\n" @@ -716,10 +723,6 @@ mpt::ustring GetFullCreditsString() "https://github.com/kazuho/picojson\n" "\n" #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 "Lennart Poettering and David Henningsson for RealtimeKit\n" "http://git.0pointer.net/rtkit.git/\n" diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 797811e72..7912a0230 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -19,7 +19,7 @@ OPENMPT_NAMESPACE_BEGIN #define VER_MAJORMAJOR 1 #define VER_MAJOR 27 #define VER_MINOR 07 -#define VER_MINORMINOR 00 +#define VER_MINORMINOR 02 //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) diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index e37bd70ad..6ec40f7e6 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,10 +5,26 @@ Changelog {#changelog} For fully detailed change log, please see the source repository directly. This 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) - * [**Sec**] Possible out-of-bounds memory read with IT / ITP / MO3 files - containing pattern loops (r10028). + * [**Sec**] Possible out-of-bounds memory read with IT and MO3 files + containing many nested pattern loops (r10028). * Keep track of active SFx macro during seeking. * 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. * J2B: Ignore frequency limits from file header. Fixes Medivo.j2b, broken since libopenmpt-0.2.6401-beta17. - * STM: Last character of sample name was missing. * ParamEq plugin center frequency was not limited correctly. * libopenmpt_ext C API was not included in the documentation. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index 833258a94..bc19d8ad8 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -19,7 +19,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 3 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 8 +#define OPENMPT_API_VERSION_PATCH 9 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk index 70e881fd0..c3cf17391 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=3 -LIBOPENMPT_VERSION_PATCH=8 +LIBOPENMPT_VERSION_PATCH=9 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=1 -LIBOPENMPT_LTVER_REVISION=8 +LIBOPENMPT_LTVER_REVISION=9 LIBOPENMPT_LTVER_AGE=1 diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp index 1bf010804..30a8e1eb4 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp @@ -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 ) { pugi::xml_document doc; - doc.load( xml.c_str() ); + doc.load_string( xml.c_str() ); pugi::xml_node settings_node = doc.child( "settings" ); std::map map; for ( pugi::xml_attribute_iterator it = settings_node.attributes_begin(); it != settings_node.attributes_end(); ++it ) { diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index c6da3205f..c4d216006 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -47,6 +47,7 @@ static const char * const license = #include #include #include +#include #include #include #include @@ -1754,18 +1755,17 @@ static void render_file( commandlineflags & flags, const std::string & filename, } -static std::string get_random_filename(std::set & filenames) { - // TODO: actually use a useful random distribution - std::size_t index = std::rand() % filenames.size(); +static std::string get_random_filename( std::set & filenames, std::default_random_engine & prng ) { + std::size_t index = std::uniform_int_distribution( 0, filenames.size() - 1 )( prng ); std::set::iterator it = filenames.begin(); std::advance( it, index ); 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 ) { - std::random_shuffle( flags.filenames.begin(), flags.filenames.end() ); + std::shuffle( flags.filenames.begin(), flags.filenames.end(), prng ); } try { while ( true ) { @@ -1777,7 +1777,7 @@ static void render_files( commandlineflags & flags, textout & log, write_buffers if ( shuffle_set.empty() ) { break; } - std::string filename = get_random_filename( shuffle_set ); + std::string filename = get_random_filename( shuffle_set, prng ); try { flags.playlist_index = std::find( flags.filenames.begin(), flags.filenames.end(), filename ) - flags.filenames.begin(); render_file( flags, filename, log, audio_stream ); @@ -2393,7 +2393,10 @@ static int main( int argc, char * argv [] ) { log.writeout(); - std::srand( static_cast( std::time( NULL ) ) ); + std::random_device rd; + std::seed_seq seq{ rd(), static_cast( std::time( NULL ) ) }; + std::default_random_engine prng( seq ); + std::srand( std::uniform_int_distribution()( prng ) ); switch ( flags.mode ) { case ModeProbe: { @@ -2404,42 +2407,42 @@ static int main( int argc, char * argv [] ) { } break; case ModeInfo: { void_audio_stream dummy; - render_files( flags, log, dummy ); + render_files( flags, log, dummy, prng ); } break; case ModeUI: case ModeBatch: { if ( flags.use_stdout ) { flags.apply_default_buffer_sizes(); 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() ) { flags.apply_default_buffer_sizes(); 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 ) } else if ( flags.driver == "pulseaudio" || flags.driver.empty() ) { pulseaudio_stream_raii pulseaudio_stream( flags, log ); - render_files( flags, log, pulseaudio_stream ); + render_files( flags, log, pulseaudio_stream, prng ); #endif #if defined( MPT_WITH_SDL2 ) } else if ( flags.driver == "sdl2" || flags.driver.empty() ) { sdl2_stream_raii sdl2_stream( flags, log ); - render_files( flags, log, sdl2_stream ); + render_files( flags, log, sdl2_stream, prng ); #endif #if defined( MPT_WITH_SDL ) } else if ( flags.driver == "sdl" || flags.driver.empty() ) { sdl_stream_raii sdl_stream( flags, log ); - render_files( flags, log, sdl_stream ); + render_files( flags, log, sdl_stream, prng ); #endif #if defined( MPT_WITH_PORTAUDIO ) } else if ( flags.driver == "portaudio" || flags.driver.empty() ) { portaudio_stream_raii portaudio_stream( flags, log ); - render_files( flags, log, portaudio_stream ); + render_files( flags, log, portaudio_stream, prng ); #endif #if defined( WIN32 ) } else if ( flags.driver == "waveout" || flags.driver.empty() ) { waveout_stream_raii waveout_stream( flags ); - render_files( flags, log, waveout_stream ); + render_files( flags, log, waveout_stream, prng ); #endif } else { if ( flags.driver.empty() ) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h index 61798bd39..294d066d2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h @@ -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 -#define ClipFilter(x) Clamp(x, int16_min * 2, int16_max * 2) +#define ClipFilter(x) Clamp(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) { @@ -370,14 +372,15 @@ struct ResonantFilter for(int i = 0; i < Traits::numChannelsIn; i++) { - typename Traits::output_t val = static_cast(( - Util::mul32to64(outSample[i], chn.nFilter_A0) + + const auto inputAmp = outSample[i] * MIXING_FILTER_PREAMP; + typename Traits::output_t val = static_cast(mpt::rshift_signed( + Util::mul32to64(inputAmp, chn.nFilter_A0) + Util::mul32to64(ClipFilter(fy[i][0]), chn.nFilter_B0) + 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][0] = val - (outSample[i] & chn.nFilter_HP); - outSample[i] = val; + fy[i][0] = val - (inputAmp & chn.nFilter_HP); + outSample[i] = val / MIXING_FILTER_PREAMP; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp index 210c4cb44..e7d872bc1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp @@ -179,7 +179,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) if(sampleHeader.zero != 0 && sampleHeader.zero != 46) // putup10.stm has zero = 46 return false; sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.filename); + mpt::String::Read(m_szNames[smp], sampleHeader.filename); sampleOffsets[smp - 1] = sampleHeader.offset; } @@ -212,7 +212,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) ORDERINDEX breakPos = ORDERINDEX_INVALID; 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; switch(note) @@ -226,9 +226,13 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) m->note = NOTE_NOTECUT; continue; default: - insvol = file.ReadUint8(); - volcmd = file.ReadUint8(); - cmdinf = file.ReadUint8(); + { + uint8 patData[3]; + file.ReadArray(patData); + insvol = patData[0]; + volcmd = patData[1]; + cmdinf = patData[2]; + } break; } @@ -266,16 +270,22 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) { case CMD_VOLUMESLIDE: // Lower nibble always has precedence, and there are no fine slides. - if(m->param & 0x0F) m->param &= 0x0F; - else m->param &= 0xF0; + if(m->param & 0x0F) + m->param &= 0x0F; + else + m->param &= 0xF0; break; case CMD_PATTERNBREAK: 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(breakPos); + breakPos = ORDERINDEX_INVALID; } + LimitMax(breakRow, i / 4u); break; case CMD_POSITIONJUMP: diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp index 37417c561..036fa82d3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp @@ -85,6 +85,17 @@ struct UltSample MPT_BINARY_STRUCT(UltSample, 66) +struct UltPatternCommand +{ + uint8 instr; + uint8 cmd; + uint8 param1; + uint8 param2; +}; + +MPT_BINARY_STRUCT(UltPatternCommand, 4) + + /* Unhandled effects: 5x1 - do not loop sample (x is unused) 9xx - set sample offset to xx * 1024 @@ -215,13 +226,14 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version) b = file.ReadUint8(); } - m.note = (b > 0 && b < 61) ? b + 36 : NOTE_NONE; - m.instr = file.ReadUint8(); - b = file.ReadUint8(); - cmd1 = b & 0x0F; - cmd2 = b >> 4; - param1 = file.ReadUint8(); - param2 = file.ReadUint8(); + m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE; + UltPatternCommand patCmd; + file.ReadStruct(patCmd); + m.instr = patCmd.instr; + cmd1 = patCmd.cmd & 0x0F; + cmd2 = patCmd.cmd >> 4; + param1 = patCmd.param1; + param2 = patCmd.param2; TranslateULTCommands(cmd1, param1, version); TranslateULTCommands(cmd2, param2, version); @@ -295,7 +307,7 @@ struct PostFixUltCommands // Apply porta? 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.param = 0; @@ -345,6 +357,10 @@ static bool ValidateHeader(const UltFileHeader &fileHeader) return true; } +static uint64 GetHeaderMinimumAdditionalSize(const UltFileHeader &fileHeader) +{ + return fileHeader.messageLength * 32u; +} CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) { @@ -357,8 +373,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, co { return ProbeFailure; } - MPT_UNREFERENCED_PARAMETER(pfilesize); - return ProbeSuccess; + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } @@ -379,6 +394,10 @@ bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags) { return true; } + if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } InitializeGlobals(MOD_TYPE_ULT); mpt::String::Read(m_songName, fileHeader.songName); @@ -442,12 +461,9 @@ bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { ModCommand evnote; - ModCommand *note; - evnote.Clear(); - - for(PATTERNINDEX pat = 0; pat < numPats; pat++) + for(PATTERNINDEX pat = 0; pat < numPats && file.CanRead(5); pat++) { - note = Patterns[pat].GetpModCommand(0, chn); + ModCommand *note = Patterns[pat].GetpModCommand(0, chn); ROWINDEX row = 0; while(row < 64) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h index a1813a749..ef5c5b5b2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h @@ -16,7 +16,7 @@ OPENMPT_NAMESPACE_BEGIN #ifdef MPT_INTMIXER 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 typedef float mixsample_t; #endif diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp index e0353e615..40abe0fa3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp @@ -1665,7 +1665,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) } 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]) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h index 80282dc9e..541613423 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h @@ -133,7 +133,7 @@ public: const_iterator end() const { return m_ModCommands.end(); } 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(CPattern &&) noexcept = default; @@ -148,9 +148,9 @@ protected: //BEGIN: DATA protected: std::vector m_ModCommands; - ROWINDEX m_Rows; - ROWINDEX m_RowsPerBeat; // patterns-specific time signature. if != 0, this is implicitely set. - ROWINDEX m_RowsPerMeasure; // ditto + ROWINDEX m_Rows = 0; + ROWINDEX m_RowsPerBeat = 0; // patterns-specific time signature. if != 0, this is implicitely set. + ROWINDEX m_RowsPerMeasure = 0; // ditto TempoSwing m_tempoSwing; std::string m_PatternName; CPatternContainer& m_rPatternContainer;