aboutsummaryrefslogtreecommitdiff
path: root/libopna/opnafm.h
blob: 359517b23548087b0e36f96630eb428db9eb4e0e (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
#ifndef LIBOPNA_OPNAFM_H_INCLUDED
#define LIBOPNA_OPNAFM_H_INCLUDED

#include <stdint.h>
#include <stdbool.h>
#include "leveldata/leveldata.h"

#ifdef __cplusplus
extern "C" {
#endif

#define LIBOPNA_FM_ENV_MAX 1023
enum {
  ENV_ATTACK,
  ENV_DECAY,
  ENV_SUSTAIN,
  ENV_RELEASE,
  ENV_OFF,
};

struct opna_fm_slot {
  // 20bits, upper 10 bits will be the index to sine table
  uint32_t phase;
  // 10 bits
  uint16_t env;
  // 12 bits
  uint16_t env_hires;
  uint16_t env_count;
  uint8_t env_state;
  uint8_t rate_shifter;
  uint8_t rate_selector;
  uint8_t rate_mul;

  uint8_t tl;
  uint8_t sl;

  uint8_t ar;
  uint8_t dr;
  uint8_t sr;
  uint8_t rr;

  uint8_t mul;
  uint8_t det;
  uint8_t ks;

  uint8_t keycode;

  // set with opna_write
  bool keyon_ext;
  // synchronized with env update (once per 3 samples)
  bool keyon;
  // set when opna_fm_slotout called
  int16_t prevout;
};

struct opna_fm_channel {
  struct opna_fm_slot slot[4];

  // save 2 samples for slot 1 feedback
  uint16_t fbmem;
  // save sample for long (>2) chain of slots
  uint16_t alg_mem;

  uint8_t alg;
  uint8_t fb;
  uint16_t fnum;
  uint8_t blk;
  
  struct leveldata leveldata;
};

struct opna_fm {
  struct opna_fm_channel channel[6];

  // remember here what was written on higher byte,
  // actually write when lower byte written
  uint8_t blkfnum_h;
  // channel 3 blk, fnum
  struct {
    uint16_t fnum[3];
    uint8_t blk[3];
    uint8_t mode;
  } ch3;

  // do envelope once per 3 samples
  uint8_t env_div3;

  // pan
  bool lselect[6];
  bool rselect[6];

  // mask
  // when (1<<channel), the channel is masked
  unsigned mask;
};

void opna_fm_reset(struct opna_fm *fm);
struct oscillodata;
void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples, struct oscillodata *oscillo, unsigned offset);
void opna_fm_writereg(struct opna_fm *fm, unsigned reg, unsigned val);

//
void opna_fm_chan_reset(struct opna_fm_channel *chan);
void opna_fm_chan_phase(struct opna_fm_channel *chan);
void opna_fm_chan_env(struct opna_fm_channel *chan);
void opna_fm_chan_set_blkfnum(struct opna_fm_channel *chan, unsigned blk, unsigned fnum);

struct opna_fm_frame {
  int16_t data[2];
};

struct opna_fm_frame opna_fm_chanout(struct opna_fm_channel *chan);
void opna_fm_slot_key(struct opna_fm_channel *chan, int slotnum, bool keyon);

void opna_fm_chan_set_alg(struct opna_fm_channel *chan, unsigned alg);
void opna_fm_chan_set_fb(struct opna_fm_channel *chan, unsigned fb);
void opna_fm_slot_set_ar(struct opna_fm_slot *slot, unsigned ar);
void opna_fm_slot_set_dr(struct opna_fm_slot *slot, unsigned dr);
void opna_fm_slot_set_sr(struct opna_fm_slot *slot, unsigned sr);
void opna_fm_slot_set_rr(struct opna_fm_slot *slot, unsigned rr);
void opna_fm_slot_set_sl(struct opna_fm_slot *slot, unsigned sl);
void opna_fm_slot_set_tl(struct opna_fm_slot *slot, unsigned tl);
void opna_fm_slot_set_ks(struct opna_fm_slot *slot, unsigned ks);
void opna_fm_slot_set_mul(struct opna_fm_slot *slot, unsigned mul);
void opna_fm_slot_set_det(struct opna_fm_slot *slot, unsigned det);

#ifdef __cplusplus
}
#endif

#endif /* LIBOPNA_OPNAFM_H_INCLUDED */