-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpaula.js
executable file
·112 lines (78 loc) · 2.77 KB
/
paula.js
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
var BUFFER_SIZE = 4096;
function PaulaChannel() {
this.EN = false;
this.LCH = 0;
this.LCL = 0;
this.LEN = 0;
this.PER = 0;
this.VOL = 0;
this.CIATA = 0;
this.offset = 0;
this.exEnable = false;
this.start = 0;
this.length = 0;
}
function Paula(sampleRate, RAM, vBlankCallBack, audioInterruptCallBack, ciaTimerCallBack, ciaTimerInterval) {
this.NUM_CHANNELS = 4;
this.FPS = 50;
this.vBlankCallBack = vBlankCallBack || function() {};
this.audioInterruptCallBack = audioInterruptCallBack || function(channel) {};
this.ciaTimerCallBack = ciaTimerCallBack || function() {};
this.CIATA = ciaTimerInterval || 0;
this.ciaTimerInterval = this.CIATA;
this.sampleRate = sampleRate || 44100;
this.clock = 3546895; // PAL; NTSC = 3579545
this.clockAdvance = this.clock / this.sampleRate;
this.ciaClockAdvance = this.clockAdvance / 5;
this.frameCount = 0;
this.ciaClock = 0;
this.frameAdvance = this.FPS / this.sampleRate;
this.channel = [];
this.RAM = new DataView(RAM);
for (var i=0;i<this.NUM_CHANNELS;i++) {
this.channel.push(new PaulaChannel());
}
}
Paula.prototype.getNextSample = function() {
var output = 0;
if (Math.floor(this.frameCount+this.frameAdvance) > this.frameCount) {
this.framecount--;
this.vBlankCallBack();
}
this.frameCount+=this.frameAdvance;
if (Math.floor(this.ciaClock+this.ciaClockAdvance) > (this.ciaTimerInterval)) {
this.ciaClock -= this.ciaTimerInterval; // reset CIA timer
this.ciaTimerInterval = this.CIATA; // latch new timer value
this.ciaTimerCallBack();
}
this.ciaClock += this.ciaClockAdvance;
var latch = function(channel) {
channel.start = channel.LCH<<16|channel.LCL;
channel.length = channel.LEN*2;
channel.offset = 0;
this.audioInterruptCallBack(channel);
}.bind(this);
for (var i=0;i<this.NUM_CHANNELS;i++) {
if (this.channel[i].EN) {
if (!this.channel[i].exEnable) {
latch(this.channel[i]);
this.channel[i].exEnable = true;
}
this.channel[i].offset += this.clockAdvance / this.channel[i].PER;
var intOffset = Math.floor(this.channel[i].offset);
if (intOffset >= this.channel[i].length) {
latch(this.channel[i]);
intOffset = 0;
}
var delta = this.channel[i].offset - intOffset;
var thisSample = this.RAM.getInt8(this.channel[i].start+intOffset);
var nextSample = ((intOffset + 1) < this.channel[i].length) ? this.RAM.getInt8(this.channel[i].start+intOffset+1) : this.RAM.getInt8(this.channel[i].start);
output += this.channel[i].VOL * (thisSample + delta * (nextSample - thisSample)); // linear interpolation
}
else {
this.channel[i].exEnable = false;
}
}
return output / 32768;
}
export { Paula };