-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathYM2413.h
149 lines (131 loc) · 4.57 KB
/
YM2413.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#pragma once
#include <Arduino.h>
#include <math.h>
#include "Instruments.h"
#include "Utils.h"
#include "YMBase.h"
/*
* YM2413.h
*
* Created on: 17.07.2016
* Author: tly
*/
#define __unused(x,y) volatile uint8_t u##y[x]
#define _unused(x,y) __unused(x,y)
#define unused(x) _unused((x),__LINE__)
template<typename Writer> struct YM2413 : YMBase<YM2413<Writer>,Writer>{
constexpr static double ADDRESS_WRITE_DELAY = 5;
constexpr static double VALUE_WRITE_DELAY = 30;
using YMBase<YM2413,Writer>::YMBase;
static constexpr int CYCLES_PER_SAMPLE = 72;
static constexpr float CLOCK_FREQ = 4000000; //3579545.0;
static constexpr float SAMPLING_FREQ = CLOCK_FREQ / CYCLES_PER_SAMPLE;
union{
uint8_t registers[64];
struct{
struct {
uint8_t frequencyMultiple : 4;
bool keyboardScalingRate : 1;
bool envGeneratorType : 1;
bool enableVibrato : 1;
bool enableAM : 1;
} settings1[2];
struct{
uint8_t totalLevel: 6;
uint8_t ksl1 : 2;
} levelSettings1;
struct{
uint8_t feedback : 3;
bool dm : 1;
bool dc : 1;
bool : 1;
uint8_t ksl2 : 2;
} levelSettings2;
struct {
uint8_t decay :4;
uint8_t attack : 4;
} attackDecaySettings[2];
struct {
uint8_t release :4;
uint8_t sustain :4;
} sustainReleaseSettings[2];
unused(6);
struct{
bool hihatOn : 1;
bool cymbalOn: 1;
bool tomOn: 1;
bool snareOn: 1;
bool bassDrumOn: 1;
bool rythmOn: 1;
uint8_t : 2;
} drums;
uint8_t testData;
uint8_t fNumberLow[9];
unused(7);
struct{
uint8_t fNumberHigh :1;
uint8_t octave :3;
bool keyOn :1;
bool susOn :1;
uint8_t :2;
} keyOn[9];
unused(7);
struct {
uint8_t level :4;
uint8_t instrument : 4;
} instrumentSettings[9];
unused(7);
} __attribute__ ((packed)) registerData;
};
void calculateFNumberAndBlock(float frequency, uint8_t& block, uint16_t& fnum) {
block = log2_fast(frequency) - 5;
fnum = frequency * uint32_t(1L << (18L - block)) / SAMPLING_FREQ;
}
void setFrequency(int channel, float frequency) {
uint16_t fnum;
uint8_t block;
calculateFNumberAndBlock(frequency, block, fnum);
this->registerData.fNumberLow[channel] = fnum & 0xFF;
writeRegister(this->registerData.fNumberLow[channel]);
this->registerData.keyOn[channel].fNumberHigh = (fnum >> 8) & 1;
this->registerData.keyOn[channel].octave = block;
writeRegister(this->registerData.keyOn[channel]);
}
void keyOn(int channel, float frequency) {
this->registerData.keyOn[channel].keyOn = true;
this->setFrequency(channel, frequency);
}
void keyOff(int channel) {
this->registerData.keyOn[channel].keyOn = false;
writeRegister(this->registerData.keyOn[channel]);
}
bool isKeyOn(int channel) const {
return this->registerData.keyOn[channel].keyOn;
}
void setInstrument(int channel, int instrument) {
registerData.instrumentSettings[channel].level = 0;
registerData.instrumentSettings[channel].instrument = instrument;
writeRegister(registerData.instrumentSettings[channel]);
}
//The SBI files are actually meant for OPL2/OPL3 but most presets work "fine"
void loadInstrument(const Instrument& inst){
writeRegister(reinterpret_cast<uint8_t&>(this->registerData.settings1[0]) = inst.modChar);
writeRegister(reinterpret_cast<uint8_t&>(this->registerData.settings1[0+1]) = inst.carChar);
struct LevelSettings{
uint8_t level :6;
uint8_t scalingLevel : 2;
};
this->registerData.levelSettings1.ksl1 = reinterpret_cast<const LevelSettings&>(inst.modScale).scalingLevel;
this->registerData.levelSettings1.totalLevel = reinterpret_cast<const LevelSettings&>(inst.modScale).level;
writeRegister(this->registerData.levelSettings1);
writeRegister(reinterpret_cast<uint8_t&>(this->registerData.attackDecaySettings[0]) = inst.modAttack);
writeRegister(reinterpret_cast<uint8_t&>(this->registerData.attackDecaySettings[0+1]) = inst.carAttack);
writeRegister(reinterpret_cast<uint8_t&>(this->registerData.sustainReleaseSettings[0]) = inst.modSustain);
writeRegister(reinterpret_cast<uint8_t&>(this->registerData.sustainReleaseSettings[0+1]) = inst.carSustain);
this->registerData.levelSettings2.dm = inst.modWaveSelect & 1;
this->registerData.levelSettings2.dc = inst.carWaveSelect & 1;
this->registerData.levelSettings2.feedback = inst.feedback;
this->registerData.levelSettings2.ksl2 = reinterpret_cast<const LevelSettings&>(inst.carScale).scalingLevel;
writeRegister(this->registerData.levelSettings2);
}
};