Browse code

Some template cleanup:

* Move the ToIntegral function from NCSF specifically to the framework in general, so it can be used in the base framework's code too.
* Use type traits a bit more.
* Move convertTo into the ConvertFuncs class.

Naram Qashat authored on 2021/04/05 21:47:28
Showing 14 changed files
... ...
@@ -23,6 +23,7 @@
23 23
 #include "XSFCommon.h"
24 24
 #include "common.h"
25 25
 #include "consts.h"
26
+#include "convert.h"
26 27
 
27 28
 NDSSoundRegister::NDSSoundRegister() : volumeMul(0), volumeDiv(0), panning(0), waveDuty(0), repeatMode(0), format(0), enable(false),
28 29
 	source(nullptr), timer(0), psgX(0), psgLast(0), psgLastCount(0), samplePosition(0), sampleIncrease(0), loopStart(0), length(0), totalLength(0)
... ...
@@ -123,7 +124,7 @@ void Channel::UpdatePorta(const Track &trk)
123 124
 	this->manualSweep = false;
124 125
 	this->sweepPitch = trk.sweepPitch;
125 126
 	this->sweepCnt = 0;
126
-	if (!trk.state[ToIntegral(TrackState::PortamentoBit)])
127
+	if (!trk.state[ConvertFuncs::ToIntegral(TrackState::PortamentoBit)])
127 128
 	{
128 129
 		this->sweepLen = 0;
129 130
 		return;
... ...
@@ -192,7 +193,7 @@ void Channel::UpdateTrack()
192 193
 	if (trackFlags.none())
193 194
 		return;
194 195
 
195
-	if (trackFlags[ToIntegral(TrackUpdateFlag::Length)])
196
+	if (trackFlags[ConvertFuncs::ToIntegral(TrackUpdateFlag::Length)])
196 197
 	{
197 198
 		ChannelState st = this->state;
198 199
 		if (st > ChannelState::Start)
... ...
@@ -203,30 +204,30 @@ void Channel::UpdateTrack()
203 204
 				++this->sweepCnt;
204 205
 		}
205 206
 	}
206
-	if (trackFlags[ToIntegral(TrackUpdateFlag::Volume)])
207
+	if (trackFlags[ConvertFuncs::ToIntegral(TrackUpdateFlag::Volume)])
207 208
 	{
208 209
 		this->UpdateVol(trk);
209
-		this->flags.set(ToIntegral(ChannelFlag::UpdateVolume));
210
+		this->flags.set(ConvertFuncs::ToIntegral(ChannelFlag::UpdateVolume));
210 211
 	}
211
-	if (trackFlags[ToIntegral(TrackUpdateFlag::Pan)])
212
+	if (trackFlags[ConvertFuncs::ToIntegral(TrackUpdateFlag::Pan)])
212 213
 	{
213 214
 		this->UpdatePan(trk);
214
-		this->flags.set(ToIntegral(ChannelFlag::UpdatePan));
215
+		this->flags.set(ConvertFuncs::ToIntegral(ChannelFlag::UpdatePan));
215 216
 	}
216
-	if (trackFlags[ToIntegral(TrackUpdateFlag::Timer)])
217
+	if (trackFlags[ConvertFuncs::ToIntegral(TrackUpdateFlag::Timer)])
217 218
 	{
218 219
 		this->UpdateTune(trk);
219
-		this->flags.set(ToIntegral(ChannelFlag::UpdateTimer));
220
+		this->flags.set(ConvertFuncs::ToIntegral(ChannelFlag::UpdateTimer));
220 221
 	}
221
-	if (trackFlags[ToIntegral(TrackUpdateFlag::Modulation)])
222
+	if (trackFlags[ConvertFuncs::ToIntegral(TrackUpdateFlag::Modulation)])
222 223
 	{
223 224
 		int oldType = this->modType;
224 225
 		int newType = trk.modType;
225 226
 		this->UpdateMod(trk);
226 227
 		if (oldType != newType)
227 228
 		{
228
-			this->flags.set(ToIntegral(getModFlag(oldType)));
229
-			this->flags.set(ToIntegral(getModFlag(newType)));
229
+			this->flags.set(ConvertFuncs::ToIntegral(getModFlag(oldType)));
230
+			this->flags.set(ConvertFuncs::ToIntegral(getModFlag(newType)));
230 231
 		}
231 232
 	}
232 233
 }
... ...
@@ -444,9 +445,9 @@ void Channel::Update()
444 445
 	bool bInStart = this->state == ChannelState::Start;
445 446
 	bool bPitchSweep = this->sweepPitch && this->sweepLen && this->sweepCnt <= this->sweepLen;
446 447
 	bool bModulation = !!this->modDepth;
447
-	bool bVolNeedUpdate = this->flags[ToIntegral(ChannelFlag::UpdateVolume)] || bNotInSustain;
448
-	bool bPanNeedUpdate = this->flags[ToIntegral(ChannelFlag::UpdatePan)] || bInStart;
449
-	bool bTmrNeedUpdate = this->flags[ToIntegral(ChannelFlag::UpdateTimer)] || bInStart || bPitchSweep;
448
+	bool bVolNeedUpdate = this->flags[ConvertFuncs::ToIntegral(ChannelFlag::UpdateVolume)] || bNotInSustain;
449
+	bool bPanNeedUpdate = this->flags[ConvertFuncs::ToIntegral(ChannelFlag::UpdatePan)] || bInStart;
450
+	bool bTmrNeedUpdate = this->flags[ConvertFuncs::ToIntegral(ChannelFlag::UpdateTimer)] || bInStart || bPitchSweep;
450 451
 	int modParam = 0;
451 452
 
452 453
 	switch (this->state)
... ...
@@ -548,7 +549,7 @@ void Channel::Update()
548 549
 			tmr = Timer_Adjust(tmr, totalAdj);
549 550
 		this->reg.timer = -tmr;
550 551
 		this->reg.sampleIncrease = (ARM7_CLOCK / static_cast<double>(this->ply->sampleRate * 2)) / (0x10000 - this->reg.timer);
551
-		this->flags.reset(ToIntegral(ChannelFlag::UpdateTimer));
552
+		this->flags.reset(ConvertFuncs::ToIntegral(ChannelFlag::UpdateTimer));
552 553
 	}
553 554
 
554 555
 	if (bVolNeedUpdate || bPanNeedUpdate)
... ...
@@ -576,7 +577,7 @@ void Channel::Update()
576 577
 
577 578
 			this->vol = static_cast<std::uint16_t>(((cr & SOUND_VOL(0x7F)) << 4) >> calcVolDivShift((cr & SOUND_VOLDIV(3)) >> 8));
578 579
 
579
-			this->flags.reset(ToIntegral(ChannelFlag::UpdateVolume));
580
+			this->flags.reset(ConvertFuncs::ToIntegral(ChannelFlag::UpdateVolume));
580 581
 		}
581 582
 
582 583
 		if (bPanNeedUpdate)
... ...
@@ -590,7 +591,7 @@ void Channel::Update()
590 591
 
591 592
 			cr &= ~SOUND_PAN(0x7F);
592 593
 			cr |= SOUND_PAN(realPan);
593
-			this->flags.reset(ToIntegral(ChannelFlag::UpdatePan));
594
+			this->flags.reset(ConvertFuncs::ToIntegral(ChannelFlag::UpdatePan));
594 595
 		}
595 596
 
596 597
 		this->tempReg.CR = cr;
... ...
@@ -18,6 +18,7 @@
18 18
 #include <cstdint>
19 19
 #include "common.h"
20 20
 #include "consts.h"
21
+#include "convert.h"
21 22
 
22 23
 struct SWAV;
23 24
 struct Track;
... ...
@@ -179,7 +180,7 @@ struct Channel
179 180
 	std::uint8_t prio;
180 181
 	bool manualSweep;
181 182
 
182
-	std::bitset<ToIntegral(ChannelFlag::Bits)> flags;
183
+	std::bitset<ConvertFuncs::ToIntegral(ChannelFlag::Bits)> flags;
183 184
 	std::int8_t pan; // -64 .. 63
184 185
 	std::int16_t extAmpl;
185 186
 
... ...
@@ -9,6 +9,7 @@
9 9
 #pragma once
10 10
 
11 11
 #include <map>
12
+#include <type_traits>
12 13
 #include <cstdint>
13 14
 #include "INFOEntry.h"
14 15
 
... ...
@@ -16,7 +17,7 @@ struct PseudoFile;
16 17
 
17 18
 template<typename T> struct INFORecord
18 19
 {
19
-	std::map<std::uint32_t, T> entries;
20
+	std::enable_if_t<std::is_base_of_v<INFOEntry, T>, std::map<std::uint32_t, T>> entries;
20 21
 
21 22
 	INFORecord();
22 23
 
... ...
@@ -13,6 +13,7 @@
13 13
 #include "SSEQ.h"
14 14
 #include "common.h"
15 15
 #include "consts.h"
16
+#include "convert.h"
16 17
 
17 18
 Player::Player() : prio(0), nTracks(0), tempo(0), tempoCount(0), tempoRate(0), masterVol(0), sseqVol(0), sseq(nullptr), allowedChannels(0), sampleRate(0),
18 19
 	interpolation(Interpolation::None)
... ...
@@ -96,8 +97,8 @@ int Player::ChannelAlloc(ChannelAllocateType type, int priority)
96 97
 	static const std::uint8_t arraySizes[] = { sizeof(pcmChnArray), sizeof(psgChnArray), sizeof(noiseChnArray) };
97 98
 	static const std::uint8_t *const arrayArray[] = { pcmChnArray, psgChnArray, noiseChnArray };
98 99
 
99
-	auto chnArray = arrayArray[ToIntegral(type)];
100
-	int arraySize = arraySizes[ToIntegral(type)];
100
+	auto chnArray = arrayArray[ConvertFuncs::ToIntegral(type)];
101
+	int arraySize = arraySizes[ConvertFuncs::ToIntegral(type)];
101 102
 
102 103
 	int curChnNo = -1;
103 104
 	for (int i = 0; i < arraySize; ++i)
... ...
@@ -133,10 +134,10 @@ int Player::TrackAlloc()
133 134
 	for (int i = 0; i < FSS_MAXTRACKS; ++i)
134 135
 	{
135 136
 		Track &thisTrk = this->tracks[i];
136
-		if (!thisTrk.state[ToIntegral(TrackState::AllocateBit)])
137
+		if (!thisTrk.state[ConvertFuncs::ToIntegral(TrackState::AllocateBit)])
137 138
 		{
138 139
 			thisTrk.Zero();
139
-			thisTrk.state.set(ToIntegral(TrackState::AllocateBit));
140
+			thisTrk.state.set(ConvertFuncs::ToIntegral(TrackState::AllocateBit));
140 141
 			thisTrk.updateFlags.reset();
141 142
 			return i;
142 143
 		}
... ...
@@ -19,6 +19,7 @@
19 19
 #include "Track.h"
20 20
 #include "common.h"
21 21
 #include "consts.h"
22
+#include "convert.h"
22 23
 
23 24
 Track::Track()
24 25
 {
... ...
@@ -71,8 +72,8 @@ void Track::Zero()
71 72
 void Track::ClearState()
72 73
 {
73 74
 	this->state.reset();
74
-	this->state.set(ToIntegral(TrackState::AllocateBit));
75
-	this->state.set(ToIntegral(TrackState::NoteWait));
75
+	this->state.set(ConvertFuncs::ToIntegral(TrackState::AllocateBit));
76
+	this->state.set(ConvertFuncs::ToIntegral(TrackState::NoteWait));
76 77
 	this->prio = this->ply->prio + 64;
77 78
 
78 79
 	this->pos = this->startPos;
... ...
@@ -253,7 +254,7 @@ int Track::NoteOnTie(std::uint8_t key, int vel)
253 254
 	chn->UpdatePorta(*this);
254 255
 
255 256
 	this->portaKey = key;
256
-	chn->flags.set(ToIntegral(ChannelFlag::UpdateTimer));
257
+	chn->flags.set(ConvertFuncs::ToIntegral(ChannelFlag::UpdateTimer));
257 258
 
258 259
 	return i;
259 260
 }
... ...
@@ -493,10 +494,10 @@ static inline std::function<bool (std::int16_t, std::int16_t)> CompareFunc(int c
493 494
 void Track::Run()
494 495
 {
495 496
 	// Indicate "heartbeat" for this track
496
-	this->updateFlags.set(ToIntegral(TrackUpdateFlag::Length));
497
+	this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Length));
497 498
 
498 499
 	// Exit if the track has already ended
499
-	if (this->state[ToIntegral(TrackState::End)])
500
+	if (this->state[ConvertFuncs::ToIntegral(TrackState::End)])
500 501
 		return;
501 502
 
502 503
 	if (this->wait)
... ...
@@ -521,9 +522,9 @@ void Track::Run()
521 522
 			std::uint8_t key = static_cast<std::uint8_t>(cmd + this->transpose);
522 523
 			int vel = this->overriding.val<std::uint8_t>(pData, read8, true);
523 524
 			int len = this->overriding.val<int>(pData, readvl);
524
-			if (this->state[ToIntegral(TrackState::NoteWait)])
525
+			if (this->state[ConvertFuncs::ToIntegral(TrackState::NoteWait)])
525 526
 				this->wait = len;
526
-			if (this->state[ToIntegral(TrackState::TieBit)])
527
+			if (this->state[ConvertFuncs::ToIntegral(TrackState::TieBit)])
527 528
 				this->NoteOnTie(key, vel);
528 529
 			else
529 530
 				this->NoteOn(key, vel, len);
... ...
@@ -579,18 +580,18 @@ void Track::Run()
579 580
 
580 581
 				case SSEQCommand::Pan:
581 582
 					this->pan = this->overriding.val<std::uint8_t>(pData, read8) - 64;
582
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Pan));
583
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Pan));
583 584
 					break;
584 585
 
585 586
 				case SSEQCommand::Volume:
586 587
 					this->vol = this->overriding.val<std::uint8_t>(pData, read8);
587
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Volume));
588
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Volume));
588 589
 					break;
589 590
 
590 591
 				case SSEQCommand::MasterVolume:
591 592
 					this->ply->masterVol = Cnv_Sust(this->overriding.val<std::uint8_t>(pData, read8));
592 593
 					for (std::uint8_t i = 0; i < this->ply->nTracks; ++i)
593
-						this->ply->tracks[this->ply->trackIds[i]].updateFlags.set(ToIntegral(TrackUpdateFlag::Volume));
594
+						this->ply->tracks[this->ply->trackIds[i]].updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Volume));
594 595
 					break;
595 596
 
596 597
 				case SSEQCommand::Priority:
... ...
@@ -599,17 +600,17 @@ void Track::Run()
599 600
 					break;
600 601
 
601 602
 				case SSEQCommand::NoteWait:
602
-					this->state.set(ToIntegral(TrackState::NoteWait), !!read8(pData));
603
+					this->state.set(ConvertFuncs::ToIntegral(TrackState::NoteWait), !!read8(pData));
603 604
 					break;
604 605
 
605 606
 				case SSEQCommand::Tie:
606
-					this->state.set(ToIntegral(TrackState::TieBit), !!read8(pData));
607
+					this->state.set(ConvertFuncs::ToIntegral(TrackState::TieBit), !!read8(pData));
607 608
 					this->ReleaseAllNotes();
608 609
 					break;
609 610
 
610 611
 				case SSEQCommand::Expression:
611 612
 					this->expr = this->overriding.val<std::uint8_t>(pData, read8);
612
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Volume));
613
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Volume));
613 614
 					break;
614 615
 
615 616
 				case SSEQCommand::Tempo:
... ...
@@ -617,7 +618,7 @@ void Track::Run()
617 618
 					break;
618 619
 
619 620
 				case SSEQCommand::End:
620
-					this->state.set(ToIntegral(TrackState::End));
621
+					this->state.set(ConvertFuncs::ToIntegral(TrackState::End));
621 622
 					return;
622 623
 
623 624
 				case SSEQCommand::LoopStart:
... ...
@@ -652,12 +653,12 @@ void Track::Run()
652 653
 
653 654
 				case SSEQCommand::PitchBend:
654 655
 					this->pitchBend = this->overriding.val<std::int8_t>(pData, read8);
655
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Timer));
656
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Timer));
656 657
 					break;
657 658
 
658 659
 				case SSEQCommand::PitchBendRange:
659 660
 					this->pitchBendRange = read8(pData);
660
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Timer));
661
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Timer));
661 662
 					break;
662 663
 
663 664
 				//-----------------------------------------------------------------
... ...
@@ -686,24 +687,24 @@ void Track::Run()
686 687
 
687 688
 				case SSEQCommand::PortamentoKey:
688 689
 					this->portaKey = static_cast<std::uint8_t>(read8(pData) + this->transpose);
689
-					this->state.set(ToIntegral(TrackState::PortamentoBit));
690
+					this->state.set(ConvertFuncs::ToIntegral(TrackState::PortamentoBit));
690 691
 					// Update here?
691 692
 					break;
692 693
 
693 694
 				case SSEQCommand::PortamentoFlag:
694
-					this->state.set(ToIntegral(TrackState::PortamentoBit), !!read8(pData));
695
+					this->state.set(ConvertFuncs::ToIntegral(TrackState::PortamentoBit), !!read8(pData));
695 696
 					// Update here?
696 697
 					break;
697 698
 
698 699
 				case SSEQCommand::PortamentoTime:
699 700
 					this->portaTime = this->overriding.val<std::uint8_t>(pData, read8);
700
-					this->state.set(ToIntegral(TrackState::PortamentoBit));
701
+					this->state.set(ConvertFuncs::ToIntegral(TrackState::PortamentoBit));
701 702
 					// Update here?
702 703
 					break;
703 704
 
704 705
 				case SSEQCommand::SweepPitch:
705 706
 					this->sweepPitch = this->overriding.val<std::int16_t>(pData, read16);
706
-					this->state.set(ToIntegral(TrackState::PortamentoBit));
707
+					this->state.set(ConvertFuncs::ToIntegral(TrackState::PortamentoBit));
707 708
 					// Update here?
708 709
 					break;
709 710
 
... ...
@@ -713,27 +714,27 @@ void Track::Run()
713 714
 
714 715
 				case SSEQCommand::ModulationDepth:
715 716
 					this->modDepth = this->overriding.val<std::uint8_t>(pData, read8);
716
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
717
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Modulation));
717 718
 					break;
718 719
 
719 720
 				case SSEQCommand::ModulationSpeed:
720 721
 					this->modSpeed = this->overriding.val<std::uint8_t>(pData, read8);
721
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
722
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Modulation));
722 723
 					break;
723 724
 
724 725
 				case SSEQCommand::ModulationType:
725 726
 					this->modType = read8(pData);
726
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
727
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Modulation));
727 728
 					break;
728 729
 
729 730
 				case SSEQCommand::ModulationRange:
730 731
 					this->modRange = read8(pData);
731
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
732
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Modulation));
732 733
 					break;
733 734
 
734 735
 				case SSEQCommand::ModulationDelay:
735 736
 					this->modDelay = this->overriding.val<std::uint16_t>(pData, read16);
736
-					this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
737
+					this->updateFlags.set(ConvertFuncs::ToIntegral(TrackUpdateFlag::Modulation));
737 738
 					break;
738 739
 
739 740
 				//-----------------------------------------------------------------
... ...
@@ -744,7 +745,7 @@ void Track::Run()
744 745
 				{
745 746
 					this->overriding() = true;
746 747
 					this->overriding.cmd = read8(pData);
747
-					if ((this->overriding.cmd >= ToIntegral(SSEQCommand::SetVariable) && this->overriding.cmd <= ToIntegral(SSEQCommand::CompareNotEqualTo)) || this->overriding.cmd < 0x80)
748
+					if ((this->overriding.cmd >= ConvertFuncs::ToIntegral(SSEQCommand::SetVariable) && this->overriding.cmd <= ConvertFuncs::ToIntegral(SSEQCommand::CompareNotEqualTo)) || this->overriding.cmd < 0x80)
748 749
 						this->overriding.extraValue = read8(pData);
749 750
 					std::int16_t minVal = static_cast<std::int16_t>(read16(pData));
750 751
 					std::int16_t maxVal = static_cast<std::int16_t>(read16(pData));
... ...
@@ -759,7 +760,7 @@ void Track::Run()
759 760
 				case SSEQCommand::FromVariable:
760 761
 					this->overriding() = true;
761 762
 					this->overriding.cmd = read8(pData);
762
-					if ((this->overriding.cmd >= ToIntegral(SSEQCommand::SetVariable) && this->overriding.cmd <= ToIntegral(SSEQCommand::CompareNotEqualTo)) || this->overriding.cmd < 0x80)
763
+					if ((this->overriding.cmd >= ConvertFuncs::ToIntegral(SSEQCommand::SetVariable) && this->overriding.cmd <= ConvertFuncs::ToIntegral(SSEQCommand::CompareNotEqualTo)) || this->overriding.cmd < 0x80)
763 764
 						this->overriding.extraValue = read8(pData);
764 765
 					this->overriding.value = this->ply->variables[read8(pData)];
765 766
 					break;
... ...
@@ -774,7 +775,7 @@ void Track::Run()
774 775
 				{
775 776
 					std::int8_t varNo = this->overriding.val<std::int8_t>(pData, read8, true);
776 777
 					value = this->overriding.val<std::int16_t>(pData, read16);
777
-					if (cmd == ToIntegral(SSEQCommand::DivideVariable) && !value) // Division by 0, skip it to prevent crashing
778
+					if (cmd == ConvertFuncs::ToIntegral(SSEQCommand::DivideVariable) && !value) // Division by 0, skip it to prevent crashing
778 779
 						break;
779 780
 					this->ply->variables[varNo] = VarFunc(cmd)(this->ply->variables[varNo], static_cast<std::int16_t>(value));
780 781
 					break;
... ...
@@ -808,7 +809,7 @@ void Track::Run()
808 809
 						if (extraByte)
809 810
 						{
810 811
 							int extraCmd = read8(pData);
811
-							if ((extraCmd >= ToIntegral(SSEQCommand::SetVariable) && extraCmd <= ToIntegral(SSEQCommand::CompareNotEqualTo)) || extraCmd < 0x80)
812
+							if ((extraCmd >= ConvertFuncs::ToIntegral(SSEQCommand::SetVariable) && extraCmd <= ConvertFuncs::ToIntegral(SSEQCommand::CompareNotEqualTo)) || extraCmd < 0x80)
812 813
 								++cmdBytes;
813 814
 						}
814 815
 						*pData += cmdBytes;
... ...
@@ -822,7 +823,7 @@ void Track::Run()
822 823
 			}
823 824
 		}
824 825
 
825
-		if (cmd != ToIntegral(SSEQCommand::Random) && cmd != ToIntegral(SSEQCommand::FromVariable))
826
+		if (cmd != ConvertFuncs::ToIntegral(SSEQCommand::Random) && cmd != ConvertFuncs::ToIntegral(SSEQCommand::FromVariable))
826 827
 			this->overriding() = false;
827 828
 	}
828 829
 }
... ...
@@ -14,6 +14,7 @@
14 14
 #include <cstdint>
15 15
 #include "common.h"
16 16
 #include "consts.h"
17
+#include "convert.h"
17 18
 
18 19
 struct Player;
19 20
 
... ...
@@ -55,7 +56,7 @@ struct Track
55 56
 {
56 57
 	std::int8_t trackId;
57 58
 
58
-	std::bitset<ToIntegral(TrackState::Bits)> state;
59
+	std::bitset<ConvertFuncs::ToIntegral(TrackState::Bits)> state;
59 60
 	std::uint8_t num, prio;
60 61
 	Player *ply;
61 62
 
... ...
@@ -82,7 +83,7 @@ struct Track
82 83
 	std::uint8_t modType, modSpeed, modDepth, modRange;
83 84
 	std::uint16_t modDelay;
84 85
 
85
-	std::bitset<ToIntegral(TrackUpdateFlag::Bits)> updateFlags;
86
+	std::bitset<ConvertFuncs::ToIntegral(TrackUpdateFlag::Bits)> updateFlags;
86 87
 
87 88
 	Track();
88 89
 
... ...
@@ -29,7 +29,7 @@ struct PseudoFile
29 29
 	{
30 30
 	}
31 31
 
32
-	template<typename T> T ReadLE()
32
+	template<typename T> typename std::enable_if_t<std::is_integral_v<T>, T> ReadLE()
33 33
 	{
34 34
 		T finalVal = 0;
35 35
 		for (std::size_t i = 0; i < sizeof(T); ++i)
... ...
@@ -37,7 +37,7 @@ struct PseudoFile
37 37
 		return finalVal;
38 38
 	}
39 39
 
40
-	template<typename T, std::size_t N> void ReadLE(T (&arr)[N])
40
+	template<typename T, std::size_t N> typename std::enable_if_t<std::is_integral_v<T>> ReadLE(T (&arr)[N])
41 41
 	{
42 42
 		for (std::size_t i = 0; i < N; ++i)
43 43
 			arr[i] = this->ReadLE<T>();
... ...
@@ -49,7 +49,7 @@ struct PseudoFile
49 49
 		this->pos += N;
50 50
 	}
51 51
 
52
-	template<typename T> void ReadLE(std::vector<T> &arr)
52
+	template<typename T> typename std::enable_if_t<std::is_integral_v<T>> ReadLE(std::vector<T> &arr)
53 53
 	{
54 54
 		for (std::size_t i = 0, len = arr.size(); i < len; ++i)
55 55
 			arr[i] = this->ReadLE<T>();
... ...
@@ -83,7 +83,7 @@ struct PseudoFile
83 83
  * as little-endian formating.
84 84
  */
85 85
 
86
-template<typename T> inline T ReadLE(const std::uint8_t *arr)
86
+template<typename T> inline typename std::enable_if_t<std::is_integral_v<T>, T> ReadLE(const std::uint8_t *arr)
87 87
 {
88 88
 	T finalVal = 0;
89 89
 	for (std::size_t i = 0; i < sizeof(T); ++i)
... ...
@@ -97,7 +97,7 @@ template<typename T> inline T ReadLE(const std::uint8_t *arr)
97 97
  * integers are in the format of 0x00, 16-bit integers are in the format of
98 98
  * 0x0000, and so on.
99 99
  */
100
-template<typename T> inline std::string NumToHexString(const T &num)
100
+template<typename T> inline typename std::enable_if_t<std::is_integral_v<T>, std::string> NumToHexString(const T &num)
101 101
 {
102 102
 	std::string hex;
103 103
 	std::uint8_t len = sizeof(T) * 2;
... ...
@@ -123,12 +123,6 @@ inline constexpr int REC_GROUP = 5;
123 123
 inline constexpr int REC_PLAYER2 = 6;
124 124
 inline constexpr int REC_STRM = 7;
125 125
 
126
-// Comes from https://stackoverflow.com/a/14589519
127
-template<typename T> inline constexpr auto ToIntegral(const T &e)
128
-{
129
-	return static_cast<std::underlying_type_t<T>>(e);
130
-}
131
-
132 126
 template<std::size_t N> inline bool VerifyHeader(std::int8_t (&arr)[N], const std::string &header)
133 127
 {
134 128
 	std::string arrHeader = std::string(&arr[0], &arr[N]);
... ...
@@ -312,7 +312,7 @@ void XSFConfig_NCSF::RefreshSoundView()
312 312
 			}
313 313
 
314 314
 			static const std::wstring states[] = { L"NONE", L"START", L"ATTACK", L"DECAY", L"SUSTAIN", L"RELEASE" };
315
-			SetDlgItemTextW(hDlg, IDC_SOUND0STATE + chanId, states[ToIntegral(chn.state)].c_str());
315
+			SetDlgItemTextW(hDlg, IDC_SOUND0STATE + chanId, states[ConvertFuncs::ToIntegral(chn.state)].c_str());
316 316
 
317 317
 			SetDlgItemTextW(hDlg, IDC_SOUND0PNT + chanId, (L"samp #" + std::to_wstring(chn.reg.loopStart)).c_str());
318 318
 
... ...
@@ -7,9 +7,10 @@
7 7
 
8 8
 #pragma once
9 9
 
10
-#include <limits>
11 10
 #include <fstream>
11
+#include <limits>
12 12
 #include <string>
13
+#include <type_traits>
13 14
 #define _USE_MATH_DEFINES
14 15
 #include <cmath>
15 16
 #include <cstddef>
... ...
@@ -19,7 +20,7 @@
19 20
 #include "convert.h"
20 21
 
21 22
 // Code from http://learningcppisfun.blogspot.com/2010/04/comparing-floating-point-numbers.html
22
-template<typename T> inline bool fEqual(T x, T y, int N = 1)
23
+template<typename T> inline typename std::enable_if_t<std::is_floating_point_v<T>, bool> fEqual(T x, T y, int N = 1)
23 24
 {
24 25
 	T diff = std::abs(x - y);
25 26
 	T tolerance = N * std::numeric_limits<T>::epsilon();
... ...
@@ -40,7 +41,7 @@ inline std::uint32_t Get32BitsLE(std::ifstream &input)
40 41
 
41 42
 // Code from the following answer on Stack Overflow:
42 43
 // http://stackoverflow.com/a/15479212
43
-template<typename T> inline T NextHighestPowerOf2(T value)
44
+template<typename T> inline typename std::enable_if_t<std::is_integral_v<T>, T> NextHighestPowerOf2(T value)
44 45
 {
45 46
 	if (value < 1)
46 47
 		return 1;
... ...
@@ -270,8 +270,8 @@ void XSFConfig::InitializeConfigDialog(XSFConfigDialog *dialog)
270 270
 	dialog->skipSilenceOnStart = ConvertFuncs::MSToString(this->skipSilenceOnStartSec);
271 271
 	dialog->detectSilence = ConvertFuncs::MSToString(this->detectSilenceSec);
272 272
 	dialog->volume = this->volume;
273
-	dialog->replayGain = static_cast<std::underlying_type_t<VolumeType>>(this->volumeType);
274
-	dialog->clipProtect = static_cast<std::underlying_type_t<PeakType>>(this->peakType);
273
+	dialog->replayGain = ConvertFuncs::ToIntegral(this->volumeType);
274
+	dialog->clipProtect = ConvertFuncs::ToIntegral(this->peakType);
275 275
 	auto found = std::find(this->supportedSampleRates.begin(), this->supportedSampleRates.end(), this->sampleRate);
276 276
 	dialog->sampleRate = found == this->supportedSampleRates.end() ? 0 : found - this->supportedSampleRates.begin();
277 277
 	dialog->titleFormat = this->titleFormat;
... ...
@@ -287,8 +287,8 @@ void XSFConfig::ResetConfigDefaults(XSFConfigDialog *dialog)
287 287
 	dialog->skipSilenceOnStart = XSFConfig::initSkipSilenceOnStartSec;
288 288
 	dialog->detectSilence = XSFConfig::initDetectSilenceSec;
289 289
 	dialog->volume = XSFConfig::initVolume;
290
-	dialog->replayGain = static_cast<std::underlying_type_t<VolumeType>>(XSFConfig::initVolumeType);
291
-	dialog->clipProtect = static_cast<std::underlying_type_t<PeakType>>(XSFConfig::initPeakType);
290
+	dialog->replayGain = ConvertFuncs::ToIntegral(XSFConfig::initVolumeType);
291
+	dialog->clipProtect = ConvertFuncs::ToIntegral(XSFConfig::initPeakType);
292 292
 	auto found = std::find(this->supportedSampleRates.begin(), this->supportedSampleRates.end(), XSFConfig::initSampleRate);
293 293
 	dialog->sampleRate = found == this->supportedSampleRates.end() ? 0 : found - this->supportedSampleRates.begin();
294 294
 	dialog->titleFormat = XSFConfig::initTitleFormat;
... ...
@@ -26,17 +26,17 @@ private:
26 26
 	// enum versions
27 27
 	template<typename T> typename std::enable_if_t<std::is_enum_v<T>, T> GetValueInternal(const std::string &name, const T &defaultValue) const
28 28
 	{
29
-		return convertTo<T>(this->GetValueString(name, std::to_string(static_cast<std::underlying_type_t<T>>(defaultValue))));
29
+		return ConvertFuncs::To<T>(this->GetValueString(name, std::to_string(ConvertFuncs::ToIntegral(defaultValue))));
30 30
 	}
31 31
 	template<typename T> typename std::enable_if_t<std::is_enum_v<T>> SetValueInternal(const std::string &name, const T &value)
32 32
 	{
33
-		this->SetValueString(name, std::to_string(static_cast<std::underlying_type_t<T>>(value)));
33
+		this->SetValueString(name, std::to_string(ConvertFuncs::ToIntegral(value)));
34 34
 	}
35 35
 
36 36
 	// non-enum versions
37 37
 	template<typename T> typename std::enable_if_t<!std::is_enum_v<T> && std::is_arithmetic_v<T>, T> GetValueInternal(const std::string &name, const T &defaultValue) const
38 38
 	{
39
-		return convertTo<T>(this->GetValueString(name, std::to_string(defaultValue)));
39
+		return ConvertFuncs::To<T>(this->GetValueString(name, std::to_string(defaultValue)));
40 40
 	}
41 41
 	template<typename T> typename std::enable_if_t<!std::is_enum_v<T> && std::is_arithmetic_v<T>> SetValueInternal(const std::string &name, const T &value)
42 42
 	{
... ...
@@ -301,24 +301,24 @@ double XSFFile::GetVolume(VolumeType preferredVolumeType, PeakType preferredPeak
301 301
 	bool hadReplayGain = false;
302 302
 	if (preferredVolumeType == VolumeType::ReplayGainAlbum && !replaygain_album_gain.empty())
303 303
 	{
304
-		gain = convertTo<double>(replaygain_album_gain);
304
+		gain = ConvertFuncs::To<double>(replaygain_album_gain);
305 305
 		hadReplayGain = true;
306 306
 	}
307 307
 	if (!hadReplayGain && preferredVolumeType != VolumeType::Volume && !replaygain_track_gain.empty())
308 308
 	{
309
-		gain = convertTo<double>(replaygain_track_gain);
309
+		gain = ConvertFuncs::To<double>(replaygain_track_gain);
310 310
 		hadReplayGain = true;
311 311
 	}
312 312
 	if (hadReplayGain)
313 313
 	{
314 314
 		double vol = std::pow(10.0, gain / 20.0), peak = 1.0;
315 315
 		if (preferredPeakType == PeakType::ReplayGainAlbum && !replaygain_album_peak.empty())
316
-			peak = convertTo<double>(replaygain_album_peak);
316
+			peak = ConvertFuncs::To<double>(replaygain_album_peak);
317 317
 		else if (preferredPeakType != PeakType::None && !replaygain_track_peak.empty())
318
-			peak = convertTo<double>(replaygain_track_peak);
318
+			peak = ConvertFuncs::To<double>(replaygain_track_peak);
319 319
 		return !fEqual(peak, 1.0) ? std::min(vol, 1.0 / peak) : vol;
320 320
 	}
321
-	return volume.empty() ? 1.0 : convertTo<double>(volume);
321
+	return volume.empty() ? 1.0 : ConvertFuncs::To<double>(volume);
322 322
 }
323 323
 
324 324
 std::string XSFFile::FormattedTitleOptionalBlock(const std::string &block, bool &hadReplacement, unsigned level) const
... ...
@@ -61,7 +61,7 @@ public:
61 61
 	std::string GetTagValue(const std::string &name) const;
62 62
 	template<typename T> T GetTagValue(const std::string &name, const T &defaultValue) const
63 63
 	{
64
-		return this->GetTagExists(name) ? convertTo<T>(this->GetTagValue(name)) : defaultValue;
64
+		return this->GetTagExists(name) ? ConvertFuncs::To<T>(this->GetTagValue(name)) : defaultValue;
65 65
 	}
66 66
 	unsigned long GetLengthMS(unsigned long defaultLength) const;
67 67
 	unsigned long GetFadeMS(unsigned long defaultFade) const;
... ...
@@ -13,47 +13,6 @@
13 13
 #include <cstddef>
14 14
 #include "windowsh_wrapper.h"
15 15
 
16
-/*
17
- * Originally the convert* functions came from the C++ FAQ, Miscellaneous Technical Issues:
18
- * https://isocpp.org/wiki/faq/misc-technical-issues#convert-string-to-any
19
- *
20
- * They have been replaced with a couple functions that use the C++11 std::enable_if
21
- * construct along with various other type traits constructs to use the proper
22
- * string conversions.
23
- */
24
-template<typename T, typename S> inline typename std::enable_if_t<!std::is_enum_v<T> &&std::is_arithmetic_v<T>, T> convertTo(const std::basic_string<S> &s)
25
-{
26
-	if (std::is_integral_v<T>)
27
-	{
28
-		if (std::is_unsigned_v<T>)
29
-		{
30
-			if (std::is_same_v<unsigned long long, std::remove_cv_t<T>>)
31
-				return static_cast<T>(std::stoull(s));
32
-			else
33
-				return static_cast<T>(std::stoul(s));
34
-		}
35
-		else if (std::is_same_v<long long, std::remove_cv_t<T>>)
36
-			return static_cast<T>(std::stoll(s));
37
-		else if (std::is_same_v<long, std::remove_cv_t<T>>)
38
-			return static_cast<T>(std::stol(s));
39
-		else
40
-			return static_cast<T>(std::stoi(s));
41
-	}
42
-	else if (std::is_floating_point_v<T>)
43
-	{
44
-		if (std::is_same_v<long double, std::remove_cv_t<T>>)
45
-			return static_cast<T>(std::stold(s));
46
-		else if (std::is_same_v<double, std::remove_cv_t<T>>)
47
-			return static_cast<T>(std::stod(s));
48
-		else
49
-			return static_cast<T>(std::stof(s));
50
-	}
51
-}
52
-template<typename T, typename S> inline typename std::enable_if_t<std::is_enum_v<T>, T> convertTo(const std::basic_string<S> &s)
53
-{
54
-	return static_cast<T>(convertTo<std::underlying_type_t<T>>(s));
55
-}
56
-
57 16
 // Miscellaneous conversion functions
58 17
 class ConvertFuncs
59 18
 {
... ...
@@ -69,8 +28,55 @@ private:
69 28
 				return false;
70 29
 		return true;
71 30
 	}
72
-
73 31
 public:
32
+	/*
33
+	 * Originally the convert* functions came from the C++ FAQ, Miscellaneous Technical Issues:
34
+	 * https://isocpp.org/wiki/faq/misc-technical-issues#convert-string-to-any
35
+	 *
36
+	 * They have been replaced with a couple functions that use the C++11 std::enable_if
37
+	 * construct along with various other type traits constructs to use the proper
38
+	 * string conversions.
39
+	 */
40
+	template<typename T, typename S> static typename std::enable_if_t<!std::is_enum_v<T> && std::is_arithmetic_v<T>, T> To(const std::basic_string<S> &s)
41
+	{
42
+		if (std::is_integral_v<T>)
43
+		{
44
+			if (std::is_unsigned_v<T>)
45
+			{
46
+				if (std::is_same_v<unsigned long long, std::remove_cv_t<T>>)
47
+					return static_cast<T>(std::stoull(s));
48
+				else
49
+					return static_cast<T>(std::stoul(s));
50
+			}
51
+			else if (std::is_same_v<long long, std::remove_cv_t<T>>)
52
+				return static_cast<T>(std::stoll(s));
53
+			else if (std::is_same_v<long, std::remove_cv_t<T>>)
54
+				return static_cast<T>(std::stol(s));
55
+			else
56
+				return static_cast<T>(std::stoi(s));
57
+		}
58
+		else if (std::is_floating_point_v<T>)
59
+		{
60
+			if (std::is_same_v<long double, std::remove_cv_t<T>>)
61
+				return static_cast<T>(std::stold(s));
62
+			else if (std::is_same_v<double, std::remove_cv_t<T>>)
63
+				return static_cast<T>(std::stod(s));
64
+			else
65
+				return static_cast<T>(std::stof(s));
66
+		}
67
+	}
68
+
69
+	template<typename T, typename S> static typename std::enable_if_t<std::is_enum_v<T>, T> To(const std::basic_string<S> &s)
70
+	{
71
+		return static_cast<T>(ConvertFuncs::To<std::underlying_type_t<T>>(s));
72
+	}
73
+
74
+	// Comes from https://stackoverflow.com/a/14589519
75
+	template<typename T> static constexpr typename std::enable_if_t<std::is_enum_v<T>, std::underlying_type_t<T>> ToIntegral(const T &e)
76
+	{
77
+		return static_cast<std::underlying_type_t<T>>(e);
78
+	}
79
+
74 80
 	static unsigned long StringToMS(const std::string &time)
75 81
 	{
76 82
 		unsigned long hours = 0, minutes = 0;
... ...
@@ -99,13 +105,13 @@ public:
99 105
 		{
100 106
 			if (!ConvertFuncs::IsDigitsOnly(hoursStr))
101 107
 				return 0;
102
-			hours = convertTo<unsigned long>(hoursStr);
108
+			hours = ConvertFuncs::To<unsigned long>(hoursStr);
103 109
 		}
104 110
 		if (!minutesStr.empty())
105 111
 		{
106 112
 			if (!ConvertFuncs::IsDigitsOnly(minutesStr))
107 113
 				return 0;
108
-			minutes = convertTo<unsigned long>(minutesStr);
114
+			minutes = ConvertFuncs::To<unsigned long>(minutesStr);
109 115
 		}
110 116
 		if (!secondsStr.empty())
111 117
 		{
... ...
@@ -114,7 +120,7 @@ public:
114 120
 			std::size_t comma = secondsStr.find(',');
115 121
 			if (comma != std::string::npos)
116 122
 				secondsStr[comma] = '.';
117
-			seconds = convertTo<double>(secondsStr);
123
+			seconds = ConvertFuncs::To<double>(secondsStr);
118 124
 		}
119 125
 		seconds += minutes * 60 + hours * 1440;
120 126
 		return static_cast<unsigned long>(std::floor(seconds * 1000 + 0.5));