OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
patternlist.cpp
Go to the documentation of this file.
1#include "music.h"
2
3#include <Tempest/Log>
4#include <fstream>
5#include <algorithm>
6
7#include "dlscollection.h"
8#include "directmusic.h"
9#include "style.h"
10
11using namespace Dx8;
12using namespace Tempest;
13
14// from DX8 SDK docs: https://docs.microsoft.com/en-us/previous-versions/ms808181(v%3Dmsdn.10)
15static uint32_t musicOffset(uint32_t mtGridStart, int16_t nTimeOffset, const DMUS_IO_TIMESIG& timeSig, double tempo) {
16 // const uint32_t ppq = 768;
17
18 // t=100 -> 600ms per grid
19 // t=85 -> 705ms per grid
20 // t=50 -> 1200ms per grid
21 // t=25 -> 2400ms per grid
22 const uint32_t ppq = uint32_t(600*(100.0/tempo));
23 const uint32_t mult = (ppq*4)/timeSig.bBeat;
24 return uint32_t(nTimeOffset) +
25 (mtGridStart / timeSig.wGridsPerBeat) * (mult) +
26 (mtGridStart % timeSig.wGridsPerBeat) * (mult/timeSig.wGridsPerBeat);
27 }
28
29static uint32_t musicDuration(uint32_t duration, double tempo) {
30 return uint32_t(duration*100.0/tempo);
31 }
32
33static bool offsetFromScale(const uint8_t degree, const uint32_t scale, uint8_t& offset) {
34 uint8_t cnt=0;
35 for(uint8_t i=0; i<24; i++) {
36 if(scale & (1 << i)) {
37 offset = i;
38 if(cnt==degree)
39 return true;
40 cnt++;
41 }
42 }
43 return false;
44 }
45
46static bool musicValueToMIDI(const DMUS_IO_STYLENOTE& note,
47 const uint32_t& chord,
48 const std::vector<DMUS_IO_SUBCHORD>& subchords,
49 uint8_t& value) {
51 value = uint8_t(note.wMusicValue);
52 return true;
53 }
54
55 uint32_t dwChordPattern = 0x00000091;
56 uint32_t dwScalePattern = 0x00AB5AB5;
57
58 if(subchords.size()>0) {
59 dwChordPattern = subchords[0].dwChordPattern;
60 dwScalePattern = subchords[0].dwScalePattern;
61 }
62
63 uint8_t octave = uint8_t((note.wMusicValue & 0xF000) >> 12);
64 uint8_t chordTone = uint8_t((note.wMusicValue & 0x0F00) >> 8);
65 uint8_t scaleTone = uint8_t((note.wMusicValue & 0x00F0) >> 4);
66
67 int accidentals = int8_t(note.wMusicValue & 0x000F);
68 if(accidentals>7)
69 accidentals = (accidentals - 16);
70
71 int noteValue = ((chord & 0xFF000000) >> 24) + 12 * octave;
72 uint8_t chordOffset = 0;
73 uint8_t scaleOffset = 0;
74 if(offsetFromScale(chordTone, dwChordPattern, chordOffset)) {
75 noteValue += chordOffset;
76 } else {
77 noteValue += chordOffset+4; //FIXME
78 //return false;
79 }
80
81 if(scaleTone && offsetFromScale(scaleTone, dwScalePattern >> chordOffset, scaleOffset)) {
82 noteValue += scaleOffset;
83 }
84
85 noteValue += accidentals;
86 while(noteValue<0)
87 noteValue += 12;
88 while(noteValue>127)
89 noteValue -= 12;
90
91 value = uint8_t(noteValue);
92 return true;
93 }
94
96 :owner(&owner), intern(std::make_shared<Internal>()) {
97 for(const auto& track : s.track) {
98 if(track.sttr!=nullptr) {
99 auto& sttr = *track.sttr;
100 for(const auto& style : sttr.styles){
101 auto& stl = owner.style(style.reference);
102 this->style = &stl;
103 for(auto& band:stl.band) {
104 for(auto& r:band.intrument){
105 if(r.reference.file.empty())
106 continue;
107 auto& dls = owner.dlsCollection(r.reference);
108 Instrument ins;
109 ins.dls = &dls;
110 if((r.header.dwFlags&DMUS_IO_INST_VOLUME)!=0)
111 ins.volume = r.header.bVolume/127.f; else
112 ins.volume = 1.f;
113 if((r.header.dwFlags&DMUS_IO_INST_PAN)!=0)
114 ins.pan = r.header.bPan/127.f; else
115 ins.pan = 0.5f;
116
117 ins.dwPatch = r.header.dwPatch;
118
119 if(ins.volume<0.f)
120 ins.volume=0;
121
122 instruments[r.header.dwPChannel] = ins;
123 }
124 }
125 }
126 }
127 else if(track.cord!=nullptr) {
128 cordHeader = track.cord->header;
129 subchord = track.cord->subchord;
130 }
131 else if(track.cmnd!=nullptr) {
132 commands = track.cmnd->commands;
133 }
134 }
135 index();
136 }
137
138void PatternList::index() {
139 if(!style)
140 return;
141 const Style& stl = *style;
142 intern->pptn.resize(stl.patterns.size());
143 for(size_t i=0;i<stl.patterns.size();++i){
144 index(stl,intern->pptn[i],stl.patterns[i]);
145 intern->pptn[i].styh = stl.styh;
146 }
147
148 for(auto& i:commands) {
149 if(i.bCommand==DMUS_COMMANDT_GROOVE) {
150 Groove gr;
151 gr.at = i.mtTime;
152 gr.bGrooveLevel = i.bGrooveLevel;
153 gr.bGrooveRange = i.bGrooveRange;
154 intern->groove.push_back(gr);
155 }
156 }
157 }
158
159void PatternList::index(const Style& stl,PatternInternal &inst, const Dx8::Pattern &pattern) {
160 inst.waves.clear();
161 inst.volume.clear();
162 inst.name.assign(pattern.info.unam.begin(),pattern.info.unam.end());
163
164 inst.ptnh = pattern.header;
165
166 auto& instument = inst.instruments;
167 instument.resize(pattern.partref.size());
168
169 // fill instruments upfront
170 for(size_t i=0;i<pattern.partref.size();++i) {
171 const auto& pref = pattern.partref[i];
172 auto pinst = instruments.find(pref.io.wLogicalPartID);
173 if(pinst==instruments.end())
174 continue;
175 if(pref.io.wLogicalPartID!=22)
176 ;//continue;
177 auto& pr = instument[i];
178 const Instrument& ins = ((*pinst).second);
179 pr.key = pref.io.wLogicalPartID;
180 pr.font = ins.dls->toSoundfont(ins.dwPatch);
181
182 pr.font.setVolume(ins.volume);
183 pr.font.setPan(ins.pan);
184
185 pr.volume = ins.volume;
186 pr.pan = ins.pan;
187
188 auto part = stl.findPart(pref.io.guidPartID);
189 if(part!=nullptr) {
190 std::memcpy(pr.dwVariationChoices,part->header.dwVariationChoices,sizeof(pr.dwVariationChoices));
191 for(auto& i:pr.dwVariationChoices)
192 if(i&0x0FFFFFFF)
193 pr.dwVarCount++;
194 }
195 }
196
197 for(size_t i=0;i<pattern.partref.size();++i) {
198 const auto& pref = pattern.partref[i];
199 auto part = stl.findPart(pref.io.guidPartID);
200 if(part==nullptr)
201 continue;
202 index(inst,&instument[i],stl,*part);
203 }
204
205 std::sort(inst.waves.begin(),inst.waves.end(),[](const Note& a,const Note& b){
206 return a.at<b.at;
207 });
208 std::sort(inst.volume.begin(),inst.volume.end(),[](const Curve& a,const Curve& b){
209 return a.at<b.at;
210 });
211
212 inst.timeTotal = inst.waves.size()>0 ? pattern.timeLength(stl.styh.dblTempo) : 0;
213 }
214
215void PatternList::index(PatternInternal &idx, InsInternal* inst,
216 const Style& stl, const Style::Part &part) {
217 for(auto& i:part.notes) {
218 uint8_t note = 0;
219 if(!musicValueToMIDI(i,cordHeader,subchord,note))
220 continue;
221
222 uint32_t time = musicOffset(i.mtGridStart, i.nTimeOffset, part.header.timeSig, stl.styh.dblTempo);
223 uint32_t dur = musicDuration(i.mtDuration, stl.styh.dblTempo);
224
225 Note rec;
226 rec.at = time;
227 rec.duration = dur;
228 rec.note = note;
229 rec.velosity = i.bVelocity;
230 rec.inst = inst;
231 rec.dwVariation = i.dwVariation;
232
233 idx.waves.push_back(rec);
234 }
235
236 for(auto& i:part.curves) {
237 uint32_t time = musicOffset(i.mtGridStart, i.nTimeOffset, part.header.timeSig, stl.styh.dblTempo);
238 uint32_t dur = musicDuration(i.mtDuration, stl.styh.dblTempo);
239 if(i.nStartValue>127 || i.nEndValue>127)
240 continue;
241
242 Curve c;
243 c.at = time;
244 c.duration = dur;
245 c.shape = i.bCurveShape;
246 c.ctrl = i.bCCData;
247 c.startV = float(i.nStartValue&0x7F)/127.f;
248 c.endV = float(i.nEndValue &0x7F)/127.f;
249 c.dwVariation = i.dwVariation;
250 c.inst = inst;
251 if(i.bEventType==DMUS_CURVET_CCCURVE)
252 idx.volume.push_back(c);
253 }
254 }
255
256size_t PatternList::size() const {
257 return intern->pptn.size();
258 }
259
261 for(auto& i:style->patterns) {
262 std::string st(i.info.unam.begin(),i.info.unam.end());
263 Log::i("pattern: ",st);
264 }
265 Log::i("---");
266 }
267
268void PatternList::dbgDump(const size_t patternId) const {
269 if(!style || patternId>=style->patterns.size())
270 return;
271 const Style& stl = *style;
272 const Dx8::Pattern& p = stl.patterns[patternId];
273
274 std::string str(p.info.unam.begin(),p.info.unam.end());
275 Log::i("pattern: ",str," ",p.timeLength(stl.styh.dblTempo));
276 for(size_t i=0;i<p.partref.size();++i) {
277 auto& pref = p.partref[i];
278 auto part = stl.findPart(pref.io.guidPartID);
279 if(part==nullptr)
280 continue;
281
282 if(part->notes.size()>0 || part->curves.size()>0) {
283 std::string st(pref.unfo.unam.begin(),pref.unfo.unam.end());
284 Log::i("part: ",i," ",st," partid=",pref.io.wLogicalPartID);
285 if(/* DISABLES CODE */ (false)) {
286 for(auto& i:part->header.dwVariationChoices) {
287 char buf[16]={};
288 std::snprintf(buf,sizeof(buf),"%#010x",i);
289 Log::i(" var: ",buf);
290 }
291 }
292 dbgDump(stl,pref,*part);
293 }
294 }
295 }
296
297void PatternList::dbgDump(const Style& stl,const Dx8::Pattern::PartRef& pref,const Style::Part &part) const {
298 for(auto& i:part.notes) {
299 uint32_t time = musicOffset(i.mtGridStart, i.nTimeOffset, part.header.timeSig, stl.styh.dblTempo);
300 uint8_t note = 0;
301 if(!musicValueToMIDI(i,cordHeader,subchord,note))
302 continue;
303
304 int cId = note/12;
305 auto inst = instruments.find(pref.io.wLogicalPartID);
306 if(inst!=instruments.end()) {
307 float vol = inst->second.volume;
308 float vel = i.bVelocity;
309 auto w = (*inst).second.dls->findWave(note);
310 const char* name = w==nullptr ? "" : w->info.inam.c_str();
311 Log::i(" note:[C",cId," ", note, "] {",
312 time," - ",time+i.mtDuration,"} ","vol = ",vol," var=",i.dwVariation," vel=",vel," ",name);
313 }
314 }
315
316 for(auto& i:part.curves) {
317 const char* name = "";
318 switch(i.bCCData) {
319 case Dx8::ExpressionCtl: name = "ExpressionCtl"; break;
320 case Dx8::ChannelVolume: name = "ChannelVolume"; break;
321 default: name = "?";
322 }
323 uint32_t time = musicOffset(i.mtGridStart, i.nTimeOffset, part.header.timeSig, stl.styh.dblTempo);
324
325 Log::i(" curve:[", name, "] ",time," - ",time+i.mtDuration," var=",i.dwVariation);
326 }
327 }
const DlsCollection & dlsCollection(const Reference &id)
const Style & style(const Reference &id)
PatternList()=default
size_t size() const
void dbgDumpPatternList() const
void dbgDump(const size_t patternId) const
std::vector< PartRef > partref
Definition pattern.h:21
DMUS_IO_PATTERN header
Definition pattern.h:19
uint32_t timeLength(double tempo) const
Definition pattern.cpp:24
Unfo info
Definition pattern.h:20
DMUS_IO_STYLE styh
Definition style.h:28
std::vector< Pattern > patterns
Definition style.h:31
const Part * findPart(const GUID &guid) const
Definition style.cpp:49
std::u16string unam
Definition info.h:23
Definition band.h:10
@ DMUS_COMMANDT_GROOVE
Definition structs.h:54
@ DMUS_CURVET_CCCURVE
Definition structs.h:230
@ DMUS_IO_INST_VOLUME
Definition structs.h:122
@ DMUS_IO_INST_PAN
Definition structs.h:121
@ DMUS_PLAYMODE_FIXED
Definition structs.h:146
@ ChannelVolume
Definition structs.h:207
@ ExpressionCtl
Definition structs.h:210
static bool offsetFromScale(const uint8_t degree, const uint32_t scale, uint8_t &offset)
static bool musicValueToMIDI(const DMUS_IO_STYLENOTE &note, const uint32_t &chord, const std::vector< DMUS_IO_SUBCHORD > &subchords, uint8_t &value)
static uint32_t musicOffset(uint32_t mtGridStart, int16_t nTimeOffset, const DMUS_IO_TIMESIG &timeSig, double tempo)
static uint32_t musicDuration(uint32_t duration, double tempo)
uint16_t wLogicalPartID
Definition structs.h:176
DMUS_PLAYMODE_FLAGS bPlayModeFlags
Definition structs.h:196
DMUS_IO_TIMESIG timeSig
Definition structs.h:163
uint32_t dwVariationChoices[32]
Definition structs.h:164
uint16_t wGridsPerBeat
Definition structs.h:85
DMUS_IO_PARTREF io
Definition pattern.h:15
std::vector< DMUS_IO_STYLECURVE > curves
Definition style.h:24
DMUS_IO_STYLEPART header
Definition style.h:22
std::vector< DMUS_IO_STYLENOTE > notes
Definition style.h:23