teek 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -0
  3. data/Rakefile +120 -22
  4. data/ext/teek/extconf.rb +19 -1
  5. data/ext/teek/tcltkbridge.c +38 -2
  6. data/ext/teek/tcltkbridge.h +3 -0
  7. data/ext/teek/tkdrop.c +66 -0
  8. data/ext/teek/tkdrop.h +26 -0
  9. data/ext/teek/tkdrop_macos.m +141 -0
  10. data/ext/teek/tkdrop_win.c +232 -0
  11. data/ext/teek/tkdrop_x11.c +337 -0
  12. data/ext/teek/tkwin.c +42 -0
  13. data/lib/teek/platform.rb +29 -0
  14. data/lib/teek/version.rb +1 -1
  15. data/lib/teek.rb +49 -3
  16. data/teek.gemspec +3 -2
  17. metadata +7 -53
  18. data/sample/calculator.rb +0 -255
  19. data/sample/debug_demo.rb +0 -43
  20. data/sample/gamepad_viewer/assets/controller.png +0 -0
  21. data/sample/gamepad_viewer/gamepad_viewer.rb +0 -554
  22. data/sample/goldberg.rb +0 -1803
  23. data/sample/goldberg_helpers.rb +0 -170
  24. data/sample/optcarrot/thwaite.nes +0 -0
  25. data/sample/optcarrot/vendor/optcarrot/apu.rb +0 -856
  26. data/sample/optcarrot/vendor/optcarrot/config.rb +0 -257
  27. data/sample/optcarrot/vendor/optcarrot/cpu.rb +0 -1162
  28. data/sample/optcarrot/vendor/optcarrot/driver.rb +0 -144
  29. data/sample/optcarrot/vendor/optcarrot/mapper/cnrom.rb +0 -14
  30. data/sample/optcarrot/vendor/optcarrot/mapper/mmc1.rb +0 -105
  31. data/sample/optcarrot/vendor/optcarrot/mapper/mmc3.rb +0 -153
  32. data/sample/optcarrot/vendor/optcarrot/mapper/uxrom.rb +0 -14
  33. data/sample/optcarrot/vendor/optcarrot/nes.rb +0 -105
  34. data/sample/optcarrot/vendor/optcarrot/opt.rb +0 -168
  35. data/sample/optcarrot/vendor/optcarrot/pad.rb +0 -92
  36. data/sample/optcarrot/vendor/optcarrot/palette.rb +0 -65
  37. data/sample/optcarrot/vendor/optcarrot/ppu.rb +0 -1468
  38. data/sample/optcarrot/vendor/optcarrot/rom.rb +0 -143
  39. data/sample/optcarrot/vendor/optcarrot.rb +0 -14
  40. data/sample/optcarrot.rb +0 -354
  41. data/sample/paint/assets/bucket.png +0 -0
  42. data/sample/paint/assets/cursor.png +0 -0
  43. data/sample/paint/assets/eraser.png +0 -0
  44. data/sample/paint/assets/pencil.png +0 -0
  45. data/sample/paint/assets/spray.png +0 -0
  46. data/sample/paint/layer.rb +0 -255
  47. data/sample/paint/layer_manager.rb +0 -179
  48. data/sample/paint/paint_demo.rb +0 -837
  49. data/sample/paint/sparse_pixel_buffer.rb +0 -202
  50. data/sample/sdl2_demo.rb +0 -318
  51. data/sample/threading_demo.rb +0 -494
  52. data/sample/yam/assets/MINESWEEPER_0.png +0 -0
  53. data/sample/yam/assets/MINESWEEPER_1.png +0 -0
  54. data/sample/yam/assets/MINESWEEPER_2.png +0 -0
  55. data/sample/yam/assets/MINESWEEPER_3.png +0 -0
  56. data/sample/yam/assets/MINESWEEPER_4.png +0 -0
  57. data/sample/yam/assets/MINESWEEPER_5.png +0 -0
  58. data/sample/yam/assets/MINESWEEPER_6.png +0 -0
  59. data/sample/yam/assets/MINESWEEPER_7.png +0 -0
  60. data/sample/yam/assets/MINESWEEPER_8.png +0 -0
  61. data/sample/yam/assets/MINESWEEPER_F.png +0 -0
  62. data/sample/yam/assets/MINESWEEPER_M.png +0 -0
  63. data/sample/yam/assets/MINESWEEPER_X.png +0 -0
  64. data/sample/yam/assets/click.wav +0 -0
  65. data/sample/yam/assets/explosion.wav +0 -0
  66. data/sample/yam/assets/flag.wav +0 -0
  67. data/sample/yam/assets/music.mp3 +0 -0
  68. data/sample/yam/assets/sweep.wav +0 -0
  69. data/sample/yam/yam.rb +0 -587
@@ -1,856 +0,0 @@
1
- module Optcarrot
2
- # APU implementation (audio output)
3
- class APU
4
- CLK_M2_MUL = 6
5
- CLK_NTSC = 39_375_000 * CLK_M2_MUL
6
- CLK_NTSC_DIV = 11
7
-
8
- CHANNEL_OUTPUT_MUL = 256
9
- CHANNEL_OUTPUT_DECAY = CHANNEL_OUTPUT_MUL / 4 - 1
10
-
11
- FRAME_CLOCKS = [29830, 1, 1, 29828].map {|n| RP2A03_CC * n }
12
- OSCILLATOR_CLOCKS = [
13
- [7458, 7456, 7458, 7458],
14
- [7458, 7456, 7458, 7458 + 7452]
15
- ].map {|a| a.map {|n| RP2A03_CC * n } }
16
-
17
- def inspect
18
- "#<#{ self.class }>"
19
- end
20
-
21
- ###########################################################################
22
- # initialization
23
-
24
- def initialize(conf, cpu, rate, bits)
25
- @conf = conf
26
- @cpu = cpu
27
-
28
- @pulse_0, @pulse_1 = Pulse.new(self), Pulse.new(self)
29
- @triangle = Triangle.new(self)
30
- @noise = Noise.new(self)
31
- @dmc = DMC.new(@cpu, self)
32
- @mixer = Mixer.new(@pulse_0, @pulse_1, @triangle, @noise, @dmc)
33
-
34
- @conf.fatal("audio sample rate must be >= 11050") if rate < 11050
35
- @conf.fatal("audio bit depth must be 8 or 16") if bits != 8 && bits != 16
36
-
37
- @settings_rate = rate
38
-
39
- @output = []
40
- @buffer = []
41
-
42
- @fixed_clock = 1
43
- @rate_clock = 1
44
- @rate_counter = 0
45
- @frame_counter = 0
46
- @frame_divider = 0
47
- @frame_irq_clock = 0
48
- @frame_irq_repeat = 0
49
- @dmc_clock = 0
50
-
51
- reset(false)
52
- end
53
-
54
- def reset_mapping
55
- @frame_counter /= @fixed_clock
56
- @rate_counter /= @fixed_clock
57
- multiplier = 0
58
- while true
59
- multiplier += 1
60
- break if multiplier >= 512
61
- break if CLK_NTSC * multiplier % @settings_rate == 0
62
- end
63
- @rate_clock = CLK_NTSC * multiplier / @settings_rate
64
- @fixed_clock = CLK_NTSC_DIV * multiplier
65
- @frame_counter *= @fixed_clock
66
- @rate_counter *= @fixed_clock
67
-
68
- @mixer.reset
69
- @buffer.clear
70
-
71
- multiplier = 0
72
- while true
73
- multiplier += 1
74
- break if multiplier >= 0x1000
75
- break if CLK_NTSC * (multiplier + 1) / @settings_rate > 0x7ffff
76
- break if CLK_NTSC * multiplier % @settings_rate == 0
77
- end
78
- rate = CLK_NTSC * multiplier / @settings_rate
79
- fixed = CLK_NTSC_DIV * CPU::CLK_1 * multiplier
80
-
81
- @pulse_0 .update_settings(rate, fixed)
82
- @pulse_1 .update_settings(rate, fixed)
83
- @triangle.update_settings(rate, fixed)
84
- @noise .update_settings(rate, fixed)
85
-
86
- @cpu.add_mappings(0x4000, method(:peek_40xx), @pulse_0 .method(:poke_0))
87
- @cpu.add_mappings(0x4001, method(:peek_40xx), @pulse_0 .method(:poke_1))
88
- @cpu.add_mappings(0x4002, method(:peek_40xx), @pulse_0 .method(:poke_2))
89
- @cpu.add_mappings(0x4003, method(:peek_40xx), @pulse_0 .method(:poke_3))
90
- @cpu.add_mappings(0x4004, method(:peek_40xx), @pulse_1 .method(:poke_0))
91
- @cpu.add_mappings(0x4005, method(:peek_40xx), @pulse_1 .method(:poke_1))
92
- @cpu.add_mappings(0x4006, method(:peek_40xx), @pulse_1 .method(:poke_2))
93
- @cpu.add_mappings(0x4007, method(:peek_40xx), @pulse_1 .method(:poke_3))
94
- @cpu.add_mappings(0x4008, method(:peek_40xx), @triangle.method(:poke_0))
95
- @cpu.add_mappings(0x400a, method(:peek_40xx), @triangle.method(:poke_2))
96
- @cpu.add_mappings(0x400b, method(:peek_40xx), @triangle.method(:poke_3))
97
- @cpu.add_mappings(0x400c, method(:peek_40xx), @noise .method(:poke_0))
98
- @cpu.add_mappings(0x400e, method(:peek_40xx), @noise .method(:poke_2))
99
- @cpu.add_mappings(0x400f, method(:peek_40xx), @noise .method(:poke_3))
100
- @cpu.add_mappings(0x4010, method(:peek_40xx), @dmc .method(:poke_0))
101
- @cpu.add_mappings(0x4011, method(:peek_40xx), @dmc .method(:poke_1))
102
- @cpu.add_mappings(0x4012, method(:peek_40xx), @dmc .method(:poke_2))
103
- @cpu.add_mappings(0x4013, method(:peek_40xx), @dmc .method(:poke_3))
104
- @cpu.add_mappings(0x4015, method(:peek_4015), method(:poke_4015))
105
- @frame_irq_clock = (@frame_counter / @fixed_clock) - CPU::CLK_1
106
- end
107
-
108
- def reset(mapping = true)
109
- @cycles_ratecounter = 0
110
- @frame_divider = 0
111
- @frame_irq_clock = FOREVER_CLOCK
112
- @frame_irq_repeat = 0
113
- @dmc_clock = DMC::LUT[0]
114
- @frame_counter = FRAME_CLOCKS[0] * @fixed_clock
115
-
116
- reset_mapping if mapping
117
-
118
- @pulse_0.reset
119
- @pulse_1.reset
120
- @triangle.reset
121
- @noise.reset
122
- @dmc.reset
123
- @mixer.reset
124
- @buffer.clear
125
- @oscillator_clocks = OSCILLATOR_CLOCKS[0]
126
- end
127
-
128
- ###########################################################################
129
- # other APIs
130
-
131
- attr_reader :output
132
-
133
- def do_clock
134
- clock_dma(@cpu.current_clock)
135
- clock_frame_irq(@cpu.current_clock) if @frame_irq_clock <= @cpu.current_clock
136
- @dmc_clock < @frame_irq_clock ? @dmc_clock : @frame_irq_clock
137
- end
138
-
139
- def clock_dma(clk)
140
- clock_dmc(clk) if @dmc_clock <= clk
141
- end
142
-
143
- def update(target = @cpu.update)
144
- target *= @fixed_clock
145
- proceed(target)
146
- clock_frame_counter if @frame_counter < target
147
- end
148
-
149
- def update_latency
150
- update(@cpu.update + 1)
151
- end
152
-
153
- def update_delta
154
- elapsed = @cpu.update
155
- delta = @frame_counter != elapsed * @fixed_clock
156
- update(elapsed + 1)
157
- delta
158
- end
159
-
160
- def vsync
161
- flush_sound
162
- update(@cpu.current_clock)
163
- frame = @cpu.next_frame_clock
164
- @dmc_clock -= frame
165
- @frame_irq_clock -= frame if @frame_irq_clock != FOREVER_CLOCK
166
- frame *= @fixed_clock
167
- @rate_counter -= frame
168
- @frame_counter -= frame
169
- end
170
-
171
- ###########################################################################
172
- # helpers
173
-
174
- def clock_oscillators(two_clocks)
175
- @pulse_0.clock_envelope
176
- @pulse_1.clock_envelope
177
- @triangle.clock_linear_counter
178
- @noise.clock_envelope
179
- return unless two_clocks
180
- @pulse_0.clock_sweep(-1)
181
- @pulse_1.clock_sweep(0)
182
- @triangle.clock_length_counter
183
- @noise.clock_length_counter
184
- end
185
-
186
- def clock_dmc(target)
187
- begin
188
- if @dmc.clock_dac
189
- update(@dmc_clock)
190
- @dmc.update
191
- end
192
- @dmc_clock += @dmc.freq
193
- @dmc.clock_dma
194
- end while @dmc_clock <= target
195
- end
196
-
197
- def clock_frame_counter
198
- clock_oscillators(@frame_divider[0] == 1)
199
- @frame_divider = (@frame_divider + 1) & 3
200
- @frame_counter += @oscillator_clocks[@frame_divider] * @fixed_clock
201
- end
202
-
203
- def clock_frame_irq(target)
204
- @cpu.do_irq(CPU::IRQ_FRAME, @frame_irq_clock)
205
- begin
206
- @frame_irq_clock += FRAME_CLOCKS[1 + @frame_irq_repeat % 3]
207
- @frame_irq_repeat += 1
208
- end while @frame_irq_clock <= target
209
- end
210
-
211
- def flush_sound
212
- if @buffer.size < @settings_rate / 60
213
- target = @cpu.current_clock * @fixed_clock
214
- proceed(target)
215
- if @buffer.size < @settings_rate / 60
216
- clock_frame_counter if @frame_counter < target
217
- @buffer << @mixer.sample while @buffer.size < @settings_rate / 60
218
- end
219
- end
220
- @output.clear
221
- @output.concat(@buffer) # Array#replace creates an object internally
222
- @buffer.clear
223
- end
224
-
225
- def proceed(target)
226
- while @rate_counter < target && @buffer.size < @settings_rate / 60
227
- @buffer << @mixer.sample
228
- clock_frame_counter if @frame_counter <= @rate_counter
229
- @rate_counter += @rate_clock
230
- end
231
- end
232
-
233
- ###########################################################################
234
- # mapped memory handlers
235
-
236
- # Control
237
- def poke_4015(_addr, data)
238
- update
239
- @pulse_0 .enable(data[0] == 1)
240
- @pulse_1 .enable(data[1] == 1)
241
- @triangle.enable(data[2] == 1)
242
- @noise .enable(data[3] == 1)
243
- @dmc .enable(data[4] == 1)
244
- end
245
-
246
- # Status
247
- def peek_4015(_addr)
248
- elapsed = @cpu.update
249
- clock_frame_irq(elapsed) if @frame_irq_clock <= elapsed
250
- update(elapsed) if @frame_counter < elapsed * @fixed_clock
251
- @cpu.clear_irq(CPU::IRQ_FRAME) |
252
- (@pulse_0 .status ? 0x01 : 0) |
253
- (@pulse_1 .status ? 0x02 : 0) |
254
- (@triangle.status ? 0x04 : 0) |
255
- (@noise .status ? 0x08 : 0) |
256
- (@dmc .status ? 0x10 : 0)
257
- end
258
-
259
- # Frame counter (NOTE: this handler is called via Pads)
260
- def poke_4017(_addr, data)
261
- n = @cpu.update
262
- n += CPU::CLK_1 if @cpu.odd_clock?
263
- update(n)
264
- clock_frame_irq(n) if @frame_irq_clock <= n
265
- n += CPU::CLK_1
266
- @oscillator_clocks = OSCILLATOR_CLOCKS[data[7]]
267
- @frame_counter = (n + @oscillator_clocks[0]) * @fixed_clock
268
- @frame_divider = 0
269
- @frame_irq_clock = data & 0xc0 != 0 ? FOREVER_CLOCK : n + FRAME_CLOCKS[0]
270
- @frame_irq_repeat = 0
271
- @cpu.clear_irq(CPU::IRQ_FRAME) if data[6] != 0
272
- clock_oscillators(true) if data[7] != 0
273
- end
274
-
275
- def peek_40xx(_addr)
276
- 0x40
277
- end
278
-
279
- ###########################################################################
280
- # helper classes
281
-
282
- # A counter for note length
283
- class LengthCounter
284
- LUT = [
285
- 0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
286
- 0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
287
- ]
288
- def reset
289
- @enabled = false
290
- @count = 0
291
- end
292
-
293
- attr_reader :count
294
-
295
- def enable(enabled)
296
- @enabled = enabled
297
- @count = 0 unless @enabled
298
- @enabled
299
- end
300
-
301
- def write(data, frame_counter_delta)
302
- @count = @enabled ? LUT[data] : 0 if frame_counter_delta || @count == 0
303
- end
304
-
305
- def clock
306
- return false if @count == 0
307
- @count -= 1
308
- return @count == 0
309
- end
310
- end
311
-
312
- # Wave envelope
313
- class Envelope
314
- attr_reader :output, :looping
315
-
316
- def reset_clock
317
- @reset = true
318
- end
319
-
320
- def reset
321
- @output = 0
322
- @count = 0
323
- @volume_base = @volume = 0
324
- @constant = true
325
- @looping = false
326
- @reset = false
327
- update_output
328
- end
329
-
330
- def clock
331
- if @reset
332
- @reset = false
333
- @volume = 0x0f
334
- else
335
- if @count != 0
336
- @count -= 1
337
- return
338
- end
339
- @volume = (@volume - 1) & 0x0f if @volume != 0 || @looping
340
- end
341
- @count = @volume_base
342
- update_output
343
- end
344
-
345
- def write(data)
346
- @volume_base = data & 0x0f
347
- @constant = data[4] == 1
348
- @looping = data[5] == 1
349
- update_output
350
- end
351
-
352
- def update_output
353
- @output = (@constant ? @volume_base : @volume) * CHANNEL_OUTPUT_MUL
354
- end
355
- end
356
-
357
- # Mixer (with DC Blocking filter)
358
- class Mixer
359
- VOL = 192
360
- P_F = 900
361
- P_0 = 9552 * CHANNEL_OUTPUT_MUL * VOL * (P_F / 100)
362
- P_1 = 8128 * CHANNEL_OUTPUT_MUL * P_F
363
- P_2 = P_F * 100
364
- TND_F = 500
365
- TND_0 = 16367 * CHANNEL_OUTPUT_MUL * VOL * (TND_F / 100)
366
- TND_1 = 24329 * CHANNEL_OUTPUT_MUL * TND_F
367
- TND_2 = TND_F * 100
368
-
369
- def initialize(pulse_0, pulse_1, triangle, noise, dmc)
370
- @pulse_0, @pulse_1, @triangle, @noise, @dmc = pulse_0, pulse_1, triangle, noise, dmc
371
- end
372
-
373
- def reset
374
- @acc = @prev = @next = 0
375
- end
376
-
377
- def sample
378
- dac0 = @pulse_0.sample + @pulse_1.sample
379
- dac1 = @triangle.sample + @noise.sample + @dmc.sample
380
- sample = (P_0 * dac0 / (P_1 + P_2 * dac0)) + (TND_0 * dac1 / (TND_1 + TND_2 * dac1))
381
-
382
- @acc -= @prev
383
- @prev = sample << 15
384
- @acc += @prev - @next * 3 # POLE
385
- sample = @next = @acc >> 15
386
-
387
- sample = -0x7fff if sample < -0x7fff
388
- sample = 0x7fff if sample > 0x7fff
389
- sample
390
- end
391
- end
392
-
393
- # base class for oscillator channels (Pulse, Triangle, and Noise)
394
- class Oscillator
395
- def inspect
396
- "#<#{ self.class }>"
397
- end
398
-
399
- def initialize(apu)
400
- @apu = apu
401
- @rate = @fixed = 1
402
- @envelope = @length_counter = @wave_length = nil
403
- end
404
-
405
- def reset
406
- @timer = 2048 * @fixed # 2048: reset cycles
407
- @freq = @fixed
408
- @amp = 0
409
-
410
- @wave_length = 0 if @wave_length
411
- @envelope.reset if @envelope
412
- @length_counter.reset if @length_counter
413
- @active = active?
414
- end
415
-
416
- def active?
417
- return false if @length_counter && @length_counter.count == 0
418
- return false if @envelope && @envelope.output == 0
419
- return true
420
- end
421
-
422
- def poke_0(_addr, data)
423
- if @envelope
424
- @apu.update_latency
425
- @envelope.write(data)
426
- @active = active?
427
- end
428
- end
429
-
430
- def poke_2(_addr, data)
431
- @apu.update
432
- if @wave_length
433
- @wave_length = (@wave_length & 0x0700) | (data & 0x00ff)
434
- update_freq
435
- end
436
- end
437
-
438
- def poke_3(_addr, data)
439
- delta = @apu.update_delta
440
- if @wave_length
441
- @wave_length = (@wave_length & 0x00ff) | ((data & 0x07) << 8)
442
- update_freq
443
- end
444
- @envelope.reset_clock if @envelope
445
- @length_counter.write(data >> 3, delta) if @length_counter
446
- @active = active?
447
- end
448
-
449
- def enable(enabled)
450
- @length_counter.enable(enabled)
451
- @active = active?
452
- end
453
-
454
- def update_settings(r, f)
455
- @freq = @freq / @fixed * f
456
- @timer = @timer / @fixed * f
457
- @rate, @fixed = r, f
458
- end
459
-
460
- def status
461
- @length_counter.count > 0
462
- end
463
-
464
- def clock_envelope
465
- @envelope.clock
466
- @active = active?
467
- end
468
- end
469
-
470
- #--------------------------------------------------------------------------
471
-
472
- ### Pulse channel ###
473
- class Pulse < Oscillator
474
- MIN_FREQ = 0x0008
475
- MAX_FREQ = 0x07ff
476
- WAVE_FORM = [0b11111101, 0b11111001, 0b11100001, 0b00000110].map {|n| (0..7).map {|i| n[i] * 0x1f } }
477
-
478
- def initialize(_apu)
479
- super
480
- @wave_length = 0
481
- @envelope = Envelope.new
482
- @length_counter = LengthCounter.new
483
- end
484
-
485
- def reset
486
- super
487
- @freq = @fixed * 2
488
- @valid_freq = false
489
- @step = 0
490
- @form = WAVE_FORM[0]
491
- @sweep_rate = 0
492
- @sweep_count = 1
493
- @sweep_reload = false
494
- @sweep_increase = -1
495
- @sweep_shift = 0
496
- end
497
-
498
- def active?
499
- super && @valid_freq
500
- end
501
-
502
- def update_freq
503
- if @wave_length >= MIN_FREQ && @wave_length + (@sweep_increase & @wave_length >> @sweep_shift) <= MAX_FREQ
504
- @freq = (@wave_length + 1) * 2 * @fixed
505
- @valid_freq = true
506
- else
507
- @valid_freq = false
508
- end
509
- @active = active?
510
- end
511
-
512
- def poke_0(_addr, data)
513
- super
514
- @form = WAVE_FORM[data >> 6 & 3]
515
- end
516
-
517
- def poke_1(_addr, data)
518
- @apu.update
519
- @sweep_increase = data[3] != 0 ? 0 : -1
520
- @sweep_shift = data & 0x07
521
- @sweep_rate = 0
522
- if data[7] == 1 && @sweep_shift > 0
523
- @sweep_rate = ((data >> 4) & 0x07) + 1
524
- @sweep_reload = true
525
- end
526
- update_freq
527
- end
528
-
529
- def poke_3(_addr, _data)
530
- super
531
- @step = 0
532
- end
533
-
534
- def clock_sweep(complement)
535
- @active = false if !@envelope.looping && @length_counter.clock
536
- if @sweep_rate != 0
537
- @sweep_count -= 1
538
- if @sweep_count == 0
539
- @sweep_count = @sweep_rate
540
- if @wave_length >= MIN_FREQ
541
- shifted = @wave_length >> @sweep_shift
542
- if @sweep_increase == 0
543
- @wave_length += complement - shifted
544
- update_freq
545
- elsif @wave_length + shifted <= MAX_FREQ
546
- @wave_length += shifted
547
- update_freq
548
- end
549
- end
550
- end
551
- end
552
-
553
- return unless @sweep_reload
554
-
555
- @sweep_reload = false
556
- @sweep_count = @sweep_rate
557
- end
558
-
559
- def sample
560
- sum = @timer
561
- @timer -= @rate
562
- if @active
563
- if @timer < 0
564
- sum >>= @form[@step]
565
- begin
566
- v = -@timer
567
- v = @freq if v > @freq
568
- sum += v >> @form[@step = (@step + 1) & 7]
569
- @timer += @freq
570
- end while @timer < 0
571
- @amp = (sum * @envelope.output + @rate / 2) / @rate
572
- else
573
- @amp = @envelope.output >> @form[@step]
574
- end
575
- else
576
- if @timer < 0
577
- count = (-@timer + @freq - 1) / @freq
578
- @step = (@step + count) & 7
579
- @timer += count * @freq
580
- end
581
- return 0 if @amp < CHANNEL_OUTPUT_DECAY
582
- @amp -= CHANNEL_OUTPUT_DECAY
583
- end
584
- @amp
585
- end
586
- end
587
-
588
- #--------------------------------------------------------------------------
589
-
590
- ### Triangle channel ###
591
- class Triangle < Oscillator
592
- MIN_FREQ = 2 + 1
593
- WAVE_FORM = (0..15).to_a + (0..15).to_a.reverse
594
-
595
- def initialize(_apu)
596
- super
597
- @wave_length = 0
598
- @length_counter = LengthCounter.new
599
- end
600
-
601
- def reset
602
- super
603
- @step = 7
604
- @status = :counting
605
- @linear_counter_load = 0
606
- @linear_counter_start = true
607
- @linear_counter = 0
608
- end
609
-
610
- def active?
611
- super && @linear_counter != 0 && @wave_length >= MIN_FREQ
612
- end
613
-
614
- def update_freq
615
- @freq = (@wave_length + 1) * @fixed
616
- @active = active?
617
- end
618
-
619
- def poke_0(_addr, data)
620
- super
621
- @apu.update
622
- @linear_counter_load = data & 0x7f
623
- @linear_counter_start = data[7] == 0
624
- end
625
-
626
- def poke_3(_addr, _data)
627
- super
628
- @status = :reload
629
- end
630
-
631
- def clock_linear_counter
632
- if @status == :counting
633
- @linear_counter -= 1 if @linear_counter != 0
634
- else
635
- @status = :counting if @linear_counter_start
636
- @linear_counter = @linear_counter_load
637
- end
638
- @active = active?
639
- end
640
-
641
- def clock_length_counter
642
- @active = false if @linear_counter_start && @length_counter.clock
643
- end
644
-
645
- def sample
646
- if @active
647
- sum = @timer
648
- @timer -= @rate
649
- if @timer < 0
650
- sum *= WAVE_FORM[@step]
651
- begin
652
- v = -@timer
653
- v = @freq if v > @freq
654
- sum += v * WAVE_FORM[@step = (@step + 1) & 0x1f]
655
- @timer += @freq
656
- end while @timer < 0
657
- @amp = (sum * CHANNEL_OUTPUT_MUL + @rate / 2) / @rate * 3
658
- else
659
- @amp = WAVE_FORM[@step] * CHANNEL_OUTPUT_MUL * 3
660
- end
661
- else
662
- return 0 if @amp < CHANNEL_OUTPUT_DECAY
663
- @amp -= CHANNEL_OUTPUT_DECAY
664
- @step = 0
665
- end
666
- @amp
667
- end
668
- end
669
-
670
- #--------------------------------------------------------------------------
671
-
672
- ### Noise channel ###
673
- class Noise < Oscillator
674
- LUT = [4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068]
675
- NEXT_BITS_1, NEXT_BITS_6 = [1, 6].map do |shifter|
676
- (0..0x7fff).map {|bits| bits[0] == bits[shifter] ? bits / 2 : bits / 2 + 0x4000 }
677
- end
678
-
679
- def initialize(_apu)
680
- super
681
- @envelope = Envelope.new
682
- @length_counter = LengthCounter.new
683
- end
684
-
685
- def reset
686
- super
687
- @freq = LUT[0] * @fixed
688
- @bits = 0x4000
689
- @shifter = NEXT_BITS_1
690
- end
691
-
692
- def poke_2(_addr, data)
693
- @apu.update
694
- @freq = LUT[data & 0x0f] * @fixed
695
- @shifter = data[7] != 0 ? NEXT_BITS_6 : NEXT_BITS_1
696
- end
697
-
698
- def clock_length_counter
699
- @active = false if !@envelope.looping && @length_counter.clock
700
- end
701
-
702
- def sample
703
- @timer -= @rate
704
- if @active
705
- return @bits.even? ? @envelope.output * 2 : 0 if @timer >= 0
706
-
707
- sum = @bits.even? ? (@timer + @rate) : 0
708
- begin
709
- @bits = @shifter[@bits]
710
- if @bits.even?
711
- v = -@timer
712
- v = @freq if v > @freq
713
- sum += v
714
- end
715
- @timer += @freq
716
- end while @timer < 0
717
- return (sum * @envelope.output + @rate / 2) / @rate * 2
718
- else
719
- while @timer < 0
720
- @bits = @shifter[@bits]
721
- @timer += @freq
722
- end
723
- return 0
724
- end
725
- end
726
- end
727
-
728
- #--------------------------------------------------------------------------
729
-
730
- ### DMC channel ###
731
- class DMC
732
- LUT = [428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54].map {|n| n * RP2A03_CC }
733
-
734
- def initialize(cpu, apu)
735
- @apu = apu
736
- @cpu = cpu
737
- @freq = LUT[0]
738
- end
739
-
740
- def reset
741
- @cur_sample = 0
742
- @lin_sample = 0
743
- @freq = LUT[0]
744
- @loop = false
745
- @irq_enable = false
746
- @regs_length_counter = 1
747
- @regs_address = 0xc000
748
- @out_active = false
749
- @out_shifter = 0
750
- @out_dac = 0
751
- @out_buffer = 0x00
752
- @dma_length_counter = 0
753
- @dma_buffered = false
754
- @dma_address = 0xc000
755
- @dma_buffer = 0x00
756
- end
757
-
758
- attr_reader :freq
759
-
760
- def enable(enabled)
761
- @cpu.clear_irq(CPU::IRQ_DMC)
762
- if !enabled
763
- @dma_length_counter = 0
764
- elsif @dma_length_counter == 0
765
- @dma_length_counter = @regs_length_counter
766
- @dma_address = @regs_address
767
- do_dma unless @dma_buffered
768
- end
769
- end
770
-
771
- def sample
772
- if @cur_sample != @lin_sample
773
- step = CHANNEL_OUTPUT_MUL * 8
774
- if @lin_sample + step < @cur_sample
775
- @lin_sample += step
776
- elsif @cur_sample < @lin_sample - step
777
- @lin_sample -= step
778
- else
779
- @lin_sample = @cur_sample
780
- end
781
- end
782
- @lin_sample
783
- end
784
-
785
- def do_dma
786
- @dma_buffer = @cpu.dmc_dma(@dma_address)
787
- @dma_address = 0x8000 | ((@dma_address + 1) & 0x7fff)
788
- @dma_buffered = true
789
- @dma_length_counter -= 1
790
- if @dma_length_counter == 0
791
- if @loop
792
- @dma_address = @regs_address
793
- @dma_length_counter = @regs_length_counter
794
- elsif @irq_enable
795
- @cpu.do_irq(CPU::IRQ_DMC, @cpu.current_clock)
796
- end
797
- end
798
- end
799
-
800
- def update
801
- @cur_sample = @out_dac * CHANNEL_OUTPUT_MUL
802
- end
803
-
804
- def poke_0(_addr, data)
805
- @loop = data[6] != 0
806
- @irq_enable = data[7] != 0
807
- @freq = LUT[data & 0x0f]
808
- @cpu.clear_irq(CPU::IRQ_DMC) unless @irq_enable
809
- end
810
-
811
- def poke_1(_addr, data)
812
- @apu.update
813
- @out_dac = data & 0x7f
814
- update
815
- end
816
-
817
- def poke_2(_addr, data)
818
- @regs_address = 0xc000 | (data << 6)
819
- end
820
-
821
- def poke_3(_addr, data)
822
- @regs_length_counter = (data << 4) + 1
823
- end
824
-
825
- def clock_dac
826
- if @out_active
827
- n = @out_dac + ((@out_buffer & 1) << 2) - 2
828
- @out_buffer >>= 1
829
- if 0 <= n && n <= 0x7f && n != @out_dac
830
- @out_dac = n
831
- return true
832
- end
833
- end
834
- return false
835
- end
836
-
837
- def clock_dma
838
- if @out_shifter == 0
839
- @out_shifter = 7
840
- @out_active = @dma_buffered
841
- if @out_active
842
- @dma_buffered = false
843
- @out_buffer = @dma_buffer
844
- do_dma if @dma_length_counter != 0
845
- end
846
- else
847
- @out_shifter -= 1
848
- end
849
- end
850
-
851
- def status
852
- @dma_length_counter > 0
853
- end
854
- end
855
- end
856
- end