Subversion Repositories SvarDOS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1661 mateusz.vi 1
/*
2
 * Library to access OPL2/OPL3 hardware (YM3812 / YMF262)
3
 *
4
 * This file is part of the Mateusz' DOS Routines (MDR): http://mdr.osdn.io
5
 * Published under the terms of the MIT License, as stated below.
6
 *
7
 * Copyright (C) 2015-2023 Mateusz Viste
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a copy
10
 * of this software and associated documentation files (the "Software"), to
11
 * deal in the Software without restriction, including without limitation the
12
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13
 * sell copies of the Software, and to permit persons to whom the Software is
14
 * furnished to do so, subject to the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be included in
17
 * all copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25
 * IN THE SOFTWARE.
26
 */
27
 
28
#ifndef mdr_opl_h
29
#define mdr_opl_h
30
 
31
struct mdr_opl_timbre {
32
  unsigned char mod_ws, car_ws; /* waveform select (0-4), reg Exh */
33
  unsigned char mod_sr, car_sr; /* sustain/release, reg 8xh */
34
  unsigned char mod_ad, car_ad; /* attack/decay, reg 6xh */
35
  unsigned char mod_20, car_20; /* tremolo/vibrato/sustain..., reg 2xh */
36
  unsigned char mod_40, car_40; /* reg 4xh */
37
  unsigned char feedconn;
38
};
39
 
40
struct mdr_opl_timbretemplate {
41
  struct {
42
    unsigned char ws;      /* waveform select 0..3 */
43
    unsigned char sustlev; /* sustain level 0..15 */
44
    unsigned char release; /* release level 0..15 */
45
    unsigned char attack;  /* attack rate 0..15 */
46
    unsigned char decay;   /* decay rate 0..15 */
47
    unsigned char tremolo; /* tremolo flag 0..1 */
48
    unsigned char vibrato; /* vibrato flag 0..1 */
49
    unsigned char sustain; /* sustain flag 0..1 */
50
    unsigned char ksr;     /* KSR (envelope scaling) flag 0..1 */
51
    unsigned char mult;    /* frequency multiplication factor 0..15 */
52
    unsigned char ksl;     /* Key Scale Level 0..3 */
53
    unsigned char outlev;  /* output level 0..63 */
54
  } carrier;
55
  struct {
56
    unsigned char ws;      /* waveform select 0..3 */
57
    unsigned char sustlev; /* sustain level 0..15 */
58
    unsigned char release; /* release level 0..15 */
59
    unsigned char attack;  /* attack rate 0..15 */
60
    unsigned char decay;   /* decay rate 0..15 */
61
    unsigned char tremolo; /* tremolo flag 0..1 */
62
    unsigned char vibrato; /* vibrato flag 0..1 */
63
    unsigned char sustain; /* sustain flag 0..1 */
64
    unsigned char ksr;     /* KSR (envelope scaling) flag 0..1 */
65
    unsigned char mult;    /* frequency multiplication factor 0..15 */
66
    unsigned char ksl;     /* Key Scale Level 0..3 */
67
    unsigned char outlev;  /* output level 0..63 */
68
  } modultr;
69
  unsigned char feedback;/* FeedBack Modulation Factor 0..7 */
70
  unsigned char conn;    /* Synthesis type: 0=FM / 1=Additive */
71
};
72
 
73
enum MDR_OPL_TIMER {
74
  MDR_OPL_TIMER_80US  = 2,
75
  MDR_OPL_TIMER_320US = 3
76
};
77
 
78
 
79
/* frequency groups, to be used with mdr_opl_noteon() and mdr_opl_notebend().
80
 * There are 7 frequency groups to choose from. Each group supports a different
81
 * span of frequencies. Higher groups have wider spans, but at the cost of larger
82
 * difference between adjacent notes:
83
 *
84
 * Block     Note 0      Note 1023      Step gap between adjacent notes
85
 * FGROUP0   0.047 Hz     48.503 Hz     0.048 Hz
86
 * FGROUP1   0.094 Hz     97.006 Hz     0.095 Hz
87
 * FGROUP2   0.189 Hz    194.013 Hz     0.190 Hz
88
 * FGROUP3   0.379 Hz    388.026 Hz     0.379 Hz
89
 * FGROUP4   0.758 Hz    776.053 Hz     0.759 Hz
90
 * FGROUP5   1.517 Hz   1552.107 Hz     1.517 Hz
91
 * FGROUP6   3.034 Hz   3104.215 Hz     3.034 Hz
92
 * FGROUP7   6.068 Hz   6208.431 Hz     6.069 Hz
93
 *
94
 * This shows that block 7 is capable of reaching the highest note (6.2kHz) but
95
 * since there are 6 Hz between notes the accuracy suffers. Example: note A-4
96
 * is 440Hz but in this block, the two closest frequency numbers are 72 and 73,
97
 * which create tones at 437Hz and 443Hz respectively, neither of which is
98
 * particularly accurate. Blocks 3 and below are unable to reach as high as
99
 * 440Hz, but block 4 can. With block 4, frequency numbers 579 and 580 produce
100
 * 439.4 Hz and 440.2 Hz, considerably closer to the intended frequency.
101
 *
102
 * In other words, when calculating notes, the best accuracy is achieved by
103
 * selecting the lowest possible block number that can reach the desired note
104
 * frequency.
105
 *
106
 * More details: https://moddingwiki.shikadi.net/wiki/OPL_chip#A0-A8:_Frequency_Number
107
 */
108
 
109
enum mdr_opl_fgroup_t {
110
  MDR_OPL_FGROUP0 = 0,
111
  MDR_OPL_FGROUP1 = 1 << 2,
112
  MDR_OPL_FGROUP2 = 2 << 2,
113
  MDR_OPL_FGROUP3 = 3 << 2,
114
  MDR_OPL_FGROUP4 = 4 << 2,
115
  MDR_OPL_FGROUP5 = 5 << 2,
116
  MDR_OPL_FGROUP6 = 6 << 2,
117
  MDR_OPL_FGROUP7 = 7 << 2
118
};
119
 
120
/* Hardware detection and initialization. Must be called before any other
121
 * OPL function. Returns 0 on success, non-zero otherwise. */
122
int mdr_opl_init(void);
123
 
124
/* close OPL device */
125
void mdr_opl_close(void);
126
 
127
/* turns off all notes */
128
void mdr_opl_clear(void);
129
 
130
/* loads an instrument described by properties in a timbre_t struct into
131
 * the defined voice channel. The OPL2 chip supports up to 9 voice channels,
132
 * from 0 to 8. The timbre struct can be freed right after this call. */
133
void mdr_opl_loadinstrument(unsigned char voice, const struct mdr_opl_timbre *timbre);
134
 
135
/* generate a timbre structure based on a timbre template. this is a
136
 * convenience function meant to provide a human-compatible (more readable)
137
 * way of generating a timbre struct. */
138
int mdr_opl_timbre_gen(struct mdr_opl_timbre *timbre, const struct mdr_opl_timbretemplate *tpl);
139
 
140
/* Triggers a note on selected voice channel.
141
 * freqid is a value between 0 and 1023. The following formula can be used to
142
 * determine the freq number for a given note frequency (Hz) and block:
143
 *
144
 *   freqid = frequency * 2^(20 - block) / 49716
145
 *
146
 * The note will be kept "pressed" until mdr_opl_noteoff() is called. */
147
void mdr_opl_noteon(unsigned char voice, unsigned short freqid, enum mdr_opl_fgroup_t fgroup);
148
 
149
/* changes the frequency of the note currently playing on voice channel, this
150
 * can be used for pitch bending. */
151
void mdr_opl_notebend(unsigned char voice, unsigned short freqid, enum mdr_opl_fgroup_t fgroup);
152
 
153
/* releases a note on selected voice. */
154
void mdr_opl_noteoff(unsigned char voice);
155
 
156
/* adjusts volume of a voice. volume goes from 63 (mute) to 0 (loudest) */
157
void mdr_opl_voicevolume(unsigned char voice, unsigned char volume);
158
 
159
/* this is a LOW-LEVEL function that writes a data byte into the reg register
160
 * of the OPL chip. Use this only if you know exactly what you are doing. */
161
void mdr_opl_regwr(unsigned char reg, unsigned char data);
162
 
163
 
164
/*****************************************************************************
165
 *                          IMF AUDIO FILES PLAYBACK                         *
166
 *                                                                           *
167
 * It is possible to mix IMF playback calls with manual notes, but you must  *
168
 * take care to use only voices not used by your IMF audio. Typically games  *
169
 * tend to use the voice #0 for sound effects and voices #1 to #8 for music. *
170
 *                                                                           *
171
 * The IMF API comes in two version: the normal one, or "imfeasy". The easy  *
172
 * version is easier to use, but requires to have the entire IMF audio file  *
173
 * loaded in memory, while the normal (non-easy) allows for more flexibility *
174
 * in this regard, potentially allowing for playback of huge IMF files.      *
175
 *****************************************************************************/
176
 
177
/*** EASY INTERFACE ***/
178
 
179
/* playback initialization, easy interface. imf points to the start of the IMF
180
 * file. The imf pointer must not be freed as long as playback is ongoing.
181
 * imflength is the size (in bytes) of the IMF data.
182
 * clock must be an incrementing value that wraps to 0 after 65535. The clock
183
 * speed will control the playback's tempo.
184
 * loopscount tells how many times the song will have to be looped (0 means
185
 * "loop forever").
186
 * returns 0 on success, non-zero otherwise. */
187
int mdr_opl_imfeasy_init(void *imf, unsigned short imflength, unsigned short clock, unsigned char loopscount);
188
 
189
/* Playback of an IMF file preloaded via mdr_opl_imfeasy_init(). This function
190
 * must be called repeatedly at a high frequency for best playback quality.
191
 * Returns 0 on success, 1 if playback ended, -1 on error. */
192
int mdr_opl_imfeasy_play(unsigned short clock);
193
 
194
/*** ADVANCED INTERFACE ***/
195
 
196
/* playback initialization, this function must be called immediately before
197
 * playback. imf points to the start of the IMF file and must contain at least
198
 * the first 6 bytes of the audio file.
199
 * clock must be an incrementing value that wraps to 0 after 65535.
200
 * the clock speed will control the playback's tempo.
201
 * returns the amount of consumed bytes (0, 4 or 6) */
202
unsigned short mdr_opl_imf_init(void *imf, unsigned short clock);
203
 
204
/* Playback, advanced version. Feeds the IMF playback routine with IMF data.
205
 * Returns the amount of bytes that have been consumed, hence the next call
206
 * should provide an imf pointer advanced by this many bytes (and imflen
207
 * decreased accordingly). Such approach might not be the most intuitive, but
208
 * it allows to load an imf song partially and provide only short chunks of
209
 * data for playback instead of having to buffer the entire song in memory.
210
 * For a simpler call that requires to buffer the entire IMF file in memory,
211
 * see mdr_opl_imf_playeasy().
212
 * This function must be called repeatedly at a high frequency for best
213
 * playback quality. */
214
unsigned short mdr_opl_imf_play(void *imf, unsigned short imflen, unsigned short clock);
215
 
216
 
217
/*****************************************************************************
218
 * OPL TIMER FUNCTIONS                                                       *
219
 *****************************************************************************/
220
 
221
/* configures and starts a timer given type so it emits a tick every count
222
 * periods. Two timer types are available:
223
 *   MDR_OPL_TIMER_80US  - with a period of 80us
224
 *   MDR_OPL_TIMER_320US - with a period of 320us
225
 * count may range from 0 to 255, but 0 means "256 periods".
226
 *
227
 * You may use only one timer at a time.
228
 *
229
 * EXAMPLE: setting up MDR_OPL_TIMER_80US with a count of 25 would make the
230
 *          timer tick every 2ms (25 * 80us). */
231
void mdr_opl_timer_set(enum MDR_OPL_TIMER timertype, unsigned char count);
232
 
233
/* returns 1 if timer tick occured, 0 otherwise. After a tick has been
234
 * returned, this function will return 0 until next tick.
235
 *
236
 * it is important to note that there is no way to know whether one tick
237
 * passed since last time, or more, so it is up to you to make sure you call
238
 * this function fast enough. */
239
unsigned char mdr_opl_timer_tick(void);
240
 
241
#endif