aboutsummaryrefslogtreecommitdiff
path: root/src/chips/opal/opal.h
blob: ef01069ba0911585af34da7d34361272fe88dc5b (plain)
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*

    The Opal OPL3 emulator.

    Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes.

    Missing features compared to a real OPL3:

        - Timers/interrupts
        - OPL3 enable bit (it defaults to always on)
        - CSW mode
        - Test register
        - Percussion mode

*/

#ifndef OPAL_HHHH
#define OPAL_HHHH

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#define OPAL_TRUE 1
#define OPAL_FALSE 0
typedef int opal_bool;

typedef struct OpalChannel_t OpalChannel;
typedef struct OpalOperator_t OpalOperator;
typedef struct Opal_t Opal;

/* Various constants */
typedef enum OpalEnum_t
{
    OpalOPL3SampleRate      = 49716,
    OpalNumChannels         = 18,
    OpalNumOperators        = 36
} OpalEnum;

/* A single FM operator */
struct OpalOperator_t
{
    Opal*           Master;             /* Master object */
    OpalChannel*    Chan;               /* Owning channel */
    uint32_t        Phase;              /* The current offset in the selected waveform */
    uint16_t        Waveform;           /* The waveform id this operator is using */
    uint16_t        FreqMultTimes2;     /* Frequency multiplier * 2 */
    int             EnvelopeStage;      /* Which stage the envelope is at (see Env* enums above) */
    int16_t         EnvelopeLevel;      /* 0 - $1FF, 0 being the loudest */
    uint16_t        OutputLevel;        /* 0 - $FF */
    uint16_t        AttackRate;
    uint16_t        DecayRate;
    uint16_t        SustainLevel;
    uint16_t        ReleaseRate;
    uint16_t        AttackShift;
    uint16_t        AttackMask;
    uint16_t        AttackAdd;
    const uint16_t* AttackTab;
    uint16_t        DecayShift;
    uint16_t        DecayMask;
    uint16_t        DecayAdd;
    const uint16_t* DecayTab;
    uint16_t        ReleaseShift;
    uint16_t        ReleaseMask;
    uint16_t        ReleaseAdd;
    const uint16_t* ReleaseTab;
    uint16_t        KeyScaleShift;
    uint16_t        KeyScaleLevel;
    int16_t         Out[2];
    opal_bool       KeyOn;
    opal_bool       KeyScaleRate;       /* Affects envelope rate scaling */
    opal_bool       SustainMode;        /* Whether to sustain during the sustain phase, or release instead */
    opal_bool       TremoloEnable;
    opal_bool       VibratoEnable;
};

/* A single channel, which can contain two or more operators */
struct OpalChannel_t
{
    OpalOperator*   Op[4];

    Opal*           Master;             /* Master object */
    uint16_t        Freq;               /* Frequency; actually it's a phase stepping value */
    uint16_t        Octave;             /* Also known as "block" in Yamaha parlance */
    uint32_t        PhaseStep;
    uint16_t        KeyScaleNumber;
    uint16_t        FeedbackShift;
    uint16_t        ModulationType;
    OpalChannel*    ChannelPair;
    opal_bool       Enable;
    opal_bool       LeftEnable, RightEnable;
    uint16_t        LeftPan, RightPan;
};


/*==================================================================================================
            Opal instance.
 ==================================================================================================*/
struct Opal_t
{
    int32_t             SampleRate;
    int32_t             SampleAccum;
    int16_t             LastOutput[2], CurrOutput[2];
    OpalChannel         Chan[OpalNumChannels];
    OpalOperator        Op[OpalNumOperators];
    uint16_t            Clock;
    uint16_t            TremoloClock;
    uint16_t            TremoloLevel;
    uint16_t            VibratoTick;
    uint16_t            VibratoClock;
    opal_bool           NoteSel;
    opal_bool           TremoloDepth;
    opal_bool           VibratoDepth;
};


/*==================================================================================================
            Public API.
 ==================================================================================================*/
/*!
 * \brief Initialize Opal with a given output sample rate
 * \param self Pointer to the Opal instance
 * \param sample_rate Desired output sample rate
 */
extern void Opal_Init(Opal *self, int sample_rate);

/*!
 * \brief Change the output sample rate of existing initialized instance
 * \param self Pointer to the Opal instance
 * \param sample_rate Desired output sample rate
 */
extern void Opal_SetSampleRate(Opal *self, int sample_rate);

/*!
 * \brief Send the register data to the Opal
 * \param self Pointer to the Opal instance
 * \param reg_num OPL3 Register address
 * \param val Value to write
 */
extern void Opal_Port(Opal *self, uint16_t reg_num, uint8_t val);

/*!
 * \brief Panorama level per channel
 * \param self Pointer to the Opal instance
 * \param reg_num Channel number (multiplied by 256)
 * \param pan Panorama level (0 - left, 64 - middle, 127 - right)
 */
extern void Opal_Pan(Opal *self, uint16_t reg_num, uint8_t pan);

/*!
 * \brief Generate one 16-bit PCM sample in system endian (there are two integers will be written)
 * \param self Pointer to the Opal instance
 * \param left Sample for the left chanel
 * \param right Sample for the right channel
 */
extern void Opal_Sample(Opal *self, int16_t* left, int16_t* right);


#ifdef __cplusplus
}
#endif

#endif /* OPAL_HHHH */