├── lib └── .gitkeep ├── .gitignore ├── examples ├── demo.imf ├── local.html ├── demo-node.js ├── index.html └── demo-web.js ├── src ├── dosbox.h ├── adlib.h ├── index.cpp ├── dbopl.h └── dbopl.cpp ├── .github └── workflows │ └── pages.yml ├── package.json ├── README.md └── index.js /lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib/opl3-wasm.mjs 2 | node_modules 3 | .DS_Store -------------------------------------------------------------------------------- /examples/demo.imf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/opljs/HEAD/examples/demo.imf -------------------------------------------------------------------------------- /src/dosbox.h: -------------------------------------------------------------------------------- 1 | #ifndef DOSBOX_DOSBOX_H 2 | #define DOSBOX_DOSBOX_H 3 | #include 4 | 5 | typedef uintptr_t Bitu; 6 | typedef intptr_t Bits; 7 | typedef uint8_t Bit8u; 8 | typedef int8_t Bit8s; 9 | typedef uint16_t Bit16u; 10 | typedef int16_t Bit16s; 11 | typedef uint32_t Bit32u; 12 | typedef int32_t Bit32s; 13 | 14 | #define INLINE inline 15 | #define GCC_UNLIKELY 16 | 17 | class MixerChannel { 18 | public: 19 | virtual void AddSamples_m32(Bitu samples, Bit32s *buffer) = 0; 20 | virtual void AddSamples_s32(Bitu samples, Bit32s *buffer) = 0; 21 | }; 22 | 23 | #endif // DOSBOX_DOSBOX_H 24 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | jobs: 14 | deploy: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Upload GitHub Pages artifact 19 | uses: actions/upload-pages-artifact@v3 20 | with: 21 | path: ./examples 22 | 23 | - name: Deploy to GitHub Pages 24 | id: deployment 25 | uses: actions/deploy-pages@v4 26 | with: 27 | token: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /src/adlib.h: -------------------------------------------------------------------------------- 1 | #ifndef DOSBOX_ADLIB_H 2 | #define DOSBOX_ADLIB_H 3 | 4 | #include "dosbox.h" 5 | 6 | namespace Adlib { 7 | class Handler { 8 | public: 9 | //Write an address to a chip, returns the address the chip sets 10 | virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) = 0; 11 | //Write to a specific register in the chip 12 | virtual void WriteReg( Bit32u addr, Bit8u val ) = 0; 13 | //Generate a certain amount of samples 14 | virtual void Generate( MixerChannel* chan, Bitu samples ) = 0; 15 | //Initialize at a specific sample rate and mode 16 | virtual void Init( Bitu rate ) = 0; 17 | virtual ~Handler() { 18 | } 19 | }; 20 | } 21 | 22 | #endif // DOSBOX_ADLIB_H 23 | -------------------------------------------------------------------------------- /examples/local.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | opl.js Example 5 | 10 | 11 | 18 | 19 | 20 | 21 | 22 |
-
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@malvineous/opl", 3 | "version": "1.0.1", 4 | "description": "Yamaha OPL2/3 FM synth chip emulator", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "build": "emcc --bind -std=c++11 -O3 -I src -o lib/opl3-wasm.mjs -Wno-switch -s SINGLE_FILE -s INVOKE_RUN=0 -s MODULARIZE=1 -s FILESYSTEM=0 -s NODEJS_CATCH_EXIT=0 -s ALLOW_MEMORY_GROWTH=0 -s TOTAL_STACK=8192 -s TOTAL_MEMORY=65536 src/dbopl.cpp src/index.cpp", 9 | "start": "npx -y live-server examples --mount=/opl3/index.js:./index.js --mount=/opl3/lib/opl3-wasm.mjs:./lib/opl3-wasm.mjs", 10 | "prestart": "npm run build", 11 | "prepublishOnly": "npm run build" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/Malvineous/opljs.git" 16 | }, 17 | "keywords": [ 18 | "adlib", 19 | "sbfmdrv", 20 | "opl2", 21 | "opl3", 22 | "dro", 23 | "sb16" 24 | ], 25 | "contributors": [ 26 | "Adam Nielsen ", 27 | "David Konsumer " 28 | ], 29 | "license": "GPL-3.0", 30 | "bugs": { 31 | "url": "https://github.com/Malvineous/opljs/issues" 32 | }, 33 | "homepage": "https://github.com/Malvineous/opljs#readme", 34 | "devDependencies": {}, 35 | "files": [ 36 | "./lib/opl3-wasm.mjs" 37 | ], 38 | "dependencies": {} 39 | } 40 | -------------------------------------------------------------------------------- /examples/demo-node.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Example program for using the OPL synth from within the NodeJS environment. 3 | * 4 | * To keep things simple, this doesn't handle audio playback but rather just 5 | * the generation of the samples, writing them to a raw PCM .wav file. 6 | * 7 | * Copyright (C) 2018 Adam Nielsen 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | import { readFile, writeFile } from 'fs/promises'; 24 | import OPL from '../index.js'; 25 | 26 | const [,,filename, outputFilename] = process.argv; 27 | 28 | if (!filename || !outputFilename) { 29 | console.error('Use: demo '); 30 | process.exit(1); 31 | } 32 | 33 | const opl = await OPL.create(); 34 | await writeFile(outputFilename, await opl.wave(await readFile(filename))); 35 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | opl.js Example 5 | 10 | 17 | 18 | 19 |

20 | Here is a simple example:
21 | 22 |

23 | 38 | 39 | 40 | And here is a more complex example: 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opl.js 2 | Javascript wrapper: Copyright 2018 Adam Nielsen <> 3 | OPL emulator: Copyright 2002-2015 The DOSBox Team 4 | 5 | This is a Javascript wrapper around the OPL3 emulator included in DOSBox. It 6 | allows OPL-compatible FM synthesis from within Javascript code, running in Node 7 | or in the browser. 8 | 9 | The emulator is written in C++ and compiled to Javascript WebAssembly with 10 | Emscripten. A small wrapper is provided to simplify the interface between the 11 | C++ code and the Javascript code. 12 | 13 | Example code is provided, both for the Node environment (with `require()`) and 14 | for the browser (using ` 31 | 34 | ``` 35 | 36 | ## Use 37 | 38 | ```js 39 | import OPL from '@malvineous/opl'; 40 | 41 | OPL.create().then(opl => { 42 | opl.write(0xBD, 0x20); 43 | const samples = opl.generate(512); 44 | // samples now contains 512 16-bit stereo samples as a Uint8Array 45 | }); 46 | ``` 47 | 48 | 49 | ## Examples 50 | 51 | In the `examples/` folder there are some short demonstrations showing how to 52 | interface with the OPL emulator. 53 | 54 | The web example will not work if loading the HTML file directly, as some browsers 55 | do not like loading WebAssembly files from `file://` URLs. To run this example, 56 | you can either upload it somewhere or use Node to run `npm start` which 57 | will run a static web-server to host the necessary files. 58 | 59 | 60 | Included is `local.html` that uses the wasm built here, and `index.html` that accesses it, using the CDN (so you don't need to build it.) 61 | 62 | ## Limitations 63 | 64 | The library is only focused on generating the audio. It does not feature any 65 | sort of playback or audio mixing mechanism, as this is likely to be very 66 | different for different projects. You will need to include another library in 67 | your project if you wish to play the generated audio. The web example shows how 68 | to use WebAudio for playback in the browser, and the Node example shows how to 69 | save the generated audio to a .wav file instead. 70 | 71 | 8-bit and floating point audio formats are not supported. You can still use 72 | these formats, but you'll need to convert the signed 16-bit samples into these 73 | formats yourself. The WebAudio sample code shows conversion to floating point. 74 | 75 | ## Contributing 76 | 77 | When you install the release with `npm` the WebAssembly binary is included. 78 | This is not part of the git repository, so if you clone the repo you will need 79 | to install `emscripten` in order to compile the C++ code into Javascript. 80 | 81 | Once `emscripten` is installed, run: 82 | 83 | ``` 84 | npm run build 85 | ``` 86 | 87 | This will compile the C++ code in `src/` and put the compiled files into `lib/`. 88 | -------------------------------------------------------------------------------- /src/index.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * JavaScript wrapper for DOSBox OPL synth. 3 | * 4 | * Copyright (C) 2010-2018 Adam Nielsen 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include "dbopl.h" 24 | 25 | using namespace emscripten; 26 | using namespace DBOPL; 27 | 28 | // Max buffer size. Since only 512 samples can be generated at a time, setting 29 | // this to 512 * 2 channels means it'll be the largest it'll ever need to be. 30 | #define BUFFER_SIZE_SAMPLES 1024 31 | 32 | // Size of each sample in bytes (2 == 16-bit) 33 | #define SAMPLE_SIZE 2 34 | 35 | // Volume amplication (0 == none, 1 == 2x, 2 == 4x) 36 | #define VOL_AMP 1 37 | 38 | // Clipping function to prevent integer wraparound after amplification 39 | #define SAMP_BITS (SAMPLE_SIZE << 3) 40 | #define SAMP_MAX ((1 << (SAMP_BITS-1)) - 1) 41 | #define SAMP_MIN -((1 << (SAMP_BITS-1))) 42 | #define CLIP(v) (((v) > SAMP_MAX) ? SAMP_MAX : (((v) < SAMP_MIN) ? SAMP_MIN : (v))) 43 | 44 | class SampleHandler: public MixerChannel { 45 | public: 46 | int16_t *jsbuffer; 47 | uint8_t channels; 48 | 49 | SampleHandler(int16_t *jsbuffer, uint8_t channels) 50 | : jsbuffer(jsbuffer), 51 | channels(channels) 52 | { 53 | } 54 | 55 | virtual ~SampleHandler() 56 | { 57 | } 58 | 59 | virtual void AddSamples_m32(Bitu samples, Bit32s *buffer) 60 | { 61 | // Convert samples from mono s32 to stereo s16 62 | int16_t *out = (int16_t *)this->jsbuffer; 63 | for (unsigned int i = 0; i < samples; i++) { 64 | Bit32s v = buffer[i] << VOL_AMP; 65 | *out++ = CLIP(v); 66 | if (channels == 2) *out++ = CLIP(v); 67 | } 68 | return; 69 | } 70 | 71 | virtual void AddSamples_s32(Bitu samples, Bit32s *buffer) 72 | { 73 | // Convert samples from stereo s32 to stereo s16 74 | int16_t *out = (int16_t *)this->jsbuffer; 75 | for (unsigned int i = 0; i < samples; i++) { 76 | Bit32s v = buffer[i*2] << VOL_AMP; 77 | *out++ = CLIP(v); 78 | if (channels == 2) { 79 | v = buffer[i*2+1] << VOL_AMP; 80 | *out++ = CLIP(v); 81 | } 82 | } 83 | return; 84 | } 85 | }; 86 | 87 | class OPL { 88 | private: 89 | DBOPL::Handler dbopl; 90 | int16_t *jsbuffer; 91 | int lenBufferSamples; 92 | SampleHandler mixer; 93 | int channels; 94 | 95 | public: 96 | OPL(int freq, int channels, int lenBufferSamples) 97 | : jsbuffer(new int16_t[BUFFER_SIZE_SAMPLES * channels]), 98 | lenBufferSamples(BUFFER_SIZE_SAMPLES), 99 | mixer(jsbuffer, channels), 100 | channels(channels) 101 | { 102 | this->dbopl.Init(freq); 103 | } 104 | 105 | ~OPL() 106 | { 107 | delete[] this->jsbuffer; 108 | } 109 | 110 | void write(int reg, int val) { 111 | this->dbopl.WriteReg(reg, val); 112 | } 113 | 114 | val getBuffer() 115 | { 116 | return val(typed_memory_view(this->lenBufferSamples * channels, this->jsbuffer)); 117 | } 118 | 119 | void generate(int lenSamples) 120 | { 121 | if (lenSamples > 512) { 122 | EM_ASM({ 123 | throw new Error('OPL.generate() cannot generate more than 512 samples per call'); 124 | }); 125 | return; 126 | } 127 | if (lenSamples < 2) { 128 | EM_ASM({ 129 | throw new Error('OPL.generate() cannot generate fewer than 2 samples per call'); 130 | }); 131 | return; 132 | } 133 | this->dbopl.Generate(&this->mixer, lenSamples); 134 | } 135 | }; 136 | 137 | EMSCRIPTEN_BINDINGS(main) { 138 | class_("OPL") 139 | .constructor() 140 | .function("write", &OPL::write) 141 | .function("generate", &OPL::generate) 142 | .function("getBuffer", &OPL::getBuffer) 143 | ; 144 | } 145 | -------------------------------------------------------------------------------- /examples/demo-web.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Example program for using the OPL synth from within a web browser. 3 | * 4 | * You will probably need to run serve-demo-web.js from within Node to set up 5 | * a small web server hosting this file, as some browsers have difficulty 6 | * loading WebAssembly files from file:// URLs. 7 | * 8 | * Copyright (C) 2018 Adam Nielsen 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | // This shows a more complex loading, so you can mute things, but you can also use .wave() (see demo-node.js) 25 | 26 | import OPL from '@malvineous/opl' 27 | 28 | const AudioContext = window.AudioContext || window.webkitAudioContext; 29 | let audioCtx; 30 | let mute = [], muteperc = 0; 31 | 32 | // download imf file 33 | const imf = new Uint8Array(await fetch('demo.imf').then(r => r.arrayBuffer())); 34 | 35 | function setup() { 36 | audioCtx = new AudioContext(); 37 | let source = audioCtx.createBufferSource(); 38 | let scriptNode = audioCtx.createScriptProcessor(8192, 2, 2); 39 | 40 | // When the buffer source stops playing, disconnect everything 41 | source.onended = () => { 42 | console.log('source.onended()'); 43 | source.disconnect(scriptNode); 44 | scriptNode.disconnect(audioCtx.destination); 45 | scriptNode = null; 46 | source = null; 47 | } 48 | 49 | console.log('Sample rate', audioCtx.sampleRate); 50 | 51 | const samplesPerTick = Math.round(audioCtx.sampleRate / 560); 52 | 53 | console.log('Init WASM'); 54 | OPL.create(audioCtx.sampleRate).then(opl => { 55 | console.log('WASM init done'); 56 | 57 | let p = 0; 58 | let lenGen = 0; 59 | scriptNode.onaudioprocess = audioProcessingEvent => { 60 | var b = audioProcessingEvent.outputBuffer; 61 | 62 | var c0 = b.getChannelData(0); 63 | var c1 = b.getChannelData(1); 64 | 65 | let lenFill = b.length; 66 | let posFill = 0; 67 | 68 | while (posFill < lenFill) { 69 | // Fill any leftover delay from the last buffer-fill event first 70 | while (lenGen > 0) { 71 | if (lenFill - posFill < 2) { 72 | // No more space in buffer 73 | return; 74 | } 75 | let lenNow = Math.max(2, Math.min(512, lenGen, lenFill - posFill)); 76 | const samples = opl.generate(lenNow, Int16Array); 77 | //const samples = new Int16Array(s); 78 | for (let i = 0; i < lenNow; i++) { 79 | c0[posFill] = samples[i * 2 + 0] / 32768.0; 80 | c1[posFill] = samples[i * 2 + 1] / 32768.0; 81 | posFill++; 82 | } 83 | lenGen -= lenNow; 84 | } 85 | 86 | let delay; 87 | do { 88 | // Read the song event 89 | const reg = imf[p + 0]; 90 | let val = imf[p + 1]; 91 | delay = imf[p + 2] | (imf[p + 3] << 8); 92 | 93 | // Force the 'note-on' bit off, if the channel is muted 94 | if ((reg & 0xF0) == 0xB0) { 95 | if (reg == 0xBD) { 96 | val &= ~muteperc; 97 | } else if (mute[reg & 0x0F]) { 98 | val &= ~0x20; 99 | } 100 | }// else console.log(reg.toString(16), (reg & 0xF0).toString(16), (reg & 0xF0); 101 | opl.write(reg, val); 102 | 103 | // Advance to the next event in the song 104 | p += 4; 105 | if (p >= imf.length) { 106 | console.log('Looping'); 107 | p = 0; // loop 108 | } 109 | } while (!delay); 110 | 111 | document.getElementById('progress').firstChild.nodeValue = Math.round(p / imf.length * 100) + '%'; 112 | lenGen += delay * samplesPerTick; 113 | } 114 | }; 115 | 116 | scriptNode.connect(audioCtx.destination); 117 | source.connect(scriptNode); 118 | source.start(); 119 | console.log('Ready'); 120 | }); 121 | } 122 | 123 | document.getElementById('play').onclick = () => { 124 | if (!audioCtx) { 125 | setup(); 126 | } 127 | audioCtx.resume(); 128 | console.log('Play'); 129 | }; 130 | document.getElementById('pause').onclick = () => { 131 | audioCtx.suspend(); 132 | console.log('Pause'); 133 | }; 134 | 135 | for (let i = 0; i < 9; i++) { 136 | mute[i] = false; 137 | const ct = document.getElementById('ch' + i); 138 | ct.className = 'play'; 139 | ct.onclick = ev => { 140 | mute[i] = !mute[i]; 141 | ev.target.className = mute[i] ? 'mute' : 'play'; 142 | }; 143 | } 144 | 145 | for (let i = 0; i < 5; i++) { 146 | const ct = document.getElementById('p' + i); 147 | ct.className = 'play'; 148 | ct.onclick = ev => { 149 | muteperc ^= 1 << i; 150 | const muted = !!(muteperc & (1 << i)); 151 | ev.target.className = muted ? 'mute' : 'play'; 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * OPL interface module. 3 | * 4 | * This is basically a wrapper around the Emscripten-produced module to make it 5 | * a little more JS-like. 6 | * 7 | * Copyright (C) 2018 Adam Nielsen 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | import Loader from './lib/opl3-wasm.mjs' 24 | 25 | // 16-bit samples take up two bytes each. 26 | const SIZEOF_INT16 = 2; 27 | 28 | // Maximum buffer size is 512 samples (the max the synth can produce) multiplied 29 | // by the number of bytes needed to store each sample. 30 | const MAX_BUFFER = 512 * 2 * SIZEOF_INT16; 31 | 32 | /** 33 | * OPL interface. 34 | * 35 | * @property {Number} sampleRate 36 | * Sampling rate supplied during creation, e.g. 44100. 37 | * 38 | * @property {Number} channelCount 39 | * Number of channels set during creation. 1 for mono, 2 for stereo. 40 | */ 41 | export default class OPL 42 | { 43 | /** 44 | * Private constructor. Use OPL.create() instead. 45 | * 46 | * @param {Object} Module 47 | * Emscripten interface. 48 | * 49 | * @param {Number} sampleRate 50 | * Playback audio sampling rate. 51 | * 52 | * @param {Number} channelCount 53 | * Number of channels. 54 | */ 55 | /*private*/ constructor(Module, sampleRate, channelCount) { 56 | this.sampleRate = sampleRate; 57 | this.channelCount = channelCount; 58 | 59 | // Create an instance of the C++ class. 60 | this.opl = new Module.OPL(sampleRate, channelCount, MAX_BUFFER); 61 | 62 | // Get the buffer created on the Emscripten heap by the C++ code. This is 63 | // easier than trying to pass a JS buffer to the C++ code. 64 | this.s16array = this.opl.getBuffer(); 65 | } 66 | 67 | /** 68 | * Create an instance of the emulator. 69 | * 70 | * This function returns a {Promise}, which resolves into an instance of the 71 | * {OPL} class when it is ready to use. 72 | * 73 | * @param {Number} sampleRate 74 | * Playback audio Sampling rate, e.g. 44100. Default is the optimum 75 | * 49716 Hz used natively. Note that the sampling rate affects the way 76 | * some sounds are produced, so a different sampling rate will also change 77 | * the way the audio sounds. 78 | * 79 | * @param {Number} channelCount 80 | * Number of channels to generate. 1 for mono, 2 for stereo. The output 81 | * buffer will have samples interleaved if stereo. 82 | */ 83 | static async create(sampleRate = 49716, channelCount = 2) { 84 | return new Promise((resolve, reject) => { 85 | // Initialise Emscripten. 86 | Loader().then(Module => { 87 | resolve(new OPL(Module, sampleRate, channelCount)); 88 | }); 89 | }); 90 | } 91 | 92 | /** 93 | * Generate some OPL samples. 94 | * 95 | * If the synth is running at a sample rate of 44.1 kHz, then calling this 96 | * function with numSamples=44100 will produce one second of audio. 97 | * 98 | * Each sample in the returned buffer is 16-bits, even though the Buffer is 99 | * byte-based. This means the buffer length will be numSamples * 2 for mono, 100 | * or numSamples * 4 for stereo. In stereo, the channels are interleaved in 101 | * the buffer, with every second 16-bit value being for the other channel. 102 | * 103 | * @param {Number} numSamples 104 | * Number of samples to generate. Minimum is 2 and maximum is 512, both 105 | * limitations imposed by the emulator itself. 106 | * 107 | * @param {Object} format 108 | * Which kind of TypedArray to use when returning the data. Defaults to 109 | * Uint8Array for byte-level access, but you can supply Int16Array if you 110 | * want to access individual samples by index. 111 | * 112 | * @return {Uint8Array} (or other typed array if 'format' was specified) 113 | * containing the samples produced. 114 | * 115 | * @note Each generate() call places data into the same buffer, so you must 116 | * use the data (or make a copy of it) before the next call to generate(). 117 | * This is most important when using async functions, which may use the data 118 | * long after the function returns. If you don't copy the buffer in these 119 | * cases, the function will end up working with the wrong set of samples. 120 | */ 121 | generate(numSamples, format = Uint8Array) { 122 | this.opl.generate(numSamples); 123 | 124 | // Return a view of the C++ heap where the generated samples were just 125 | // placed. No data gets copied here. 126 | return new format( 127 | this.s16array.buffer, 128 | this.s16array.byteOffset, 129 | numSamples * this.channelCount * SIZEOF_INT16 130 | ); 131 | } 132 | 133 | /** 134 | * Write data to the emulated OPL chip. 135 | * 136 | * @param {Number} reg 137 | * OPL register, 0-255. 138 | * 139 | * @param {Number} val 140 | * Value to store in OPL register, 0-255. 141 | * 142 | * @return No return value. 143 | */ 144 | write(reg, val) { 145 | this.opl.write(reg, val); 146 | } 147 | 148 | /** 149 | * Output bytes for wav-output 150 | * 151 | * @param {ArrayBuffer/Uint8Array/Buffer} bytes 152 | * The bytes of the input (.imf, .wlf) file 153 | * 154 | * @param {Boolean} wlf 155 | * Trigger wlf file-mode (a bit faster) 156 | */ 157 | async wave(bytes, wlf=false) { 158 | const imf = new Uint8Array(bytes) 159 | const tempoInHz = wlf ? 700 : 560 160 | const samplesPerTick = Math.round(this.sampleRate / tempoInHz) 161 | const wavDataOut = [] 162 | let lenIMF = imf.length 163 | let p = 0 164 | if (imf[0] || imf[1]) { 165 | lenIMF = imf[0] | (imf[1] << 8) 166 | p = 2 167 | } 168 | let samplesWritten = 0 169 | while (p < lenIMF) { 170 | const reg = imf[p + 0] 171 | const val = imf[p + 1] 172 | const delay = imf[p + 2] | (imf[p + 3] << 8) 173 | this.write(reg, val) 174 | if (delay) { 175 | let lenGen = delay * samplesPerTick 176 | while (lenGen > 0) { 177 | const lenNow = Math.max(2, Math.min(512, lenGen)) 178 | wavDataOut.push(...this.generate(lenNow)) 179 | samplesWritten += lenNow 180 | lenGen -= lenNow 181 | } 182 | } 183 | p += 4 184 | } 185 | const lenData = samplesWritten * 4 186 | const lenRIFF = lenData + 36 187 | const a = new ArrayBuffer(44 + lenData) 188 | const u = new Uint8Array(a) 189 | const v = new DataView(a) 190 | u.set(wavDataOut, 44) 191 | v.setUint32(0, 0x52494646); // RIFF 192 | v.setUint32(4, lenRIFF, true); 193 | v.setUint32(8, 0x57415645); // WAVE 194 | v.setUint32(12, 0x666d7420); // fmt_ 195 | v.setUint32(16, 16, true); // fmt chunk length 196 | v.setUint16(20, 1, true); // 1=PCM 197 | v.setUint16(22, 2, true); // stereo 198 | v.setUint32(24, this.sampleRate, true); 199 | v.setUint32(28, this.sampleRate * this.channelCount * 2, true); 200 | v.setUint16(32, this.channelCount * 2, true); // block align 201 | v.setUint16(34, 16, true); // bits per sample 202 | v.setUint32(36, 0x64617461); // data 203 | v.setUint32(40, lenData, true); // data chunk length 204 | return u 205 | } 206 | } 207 | 208 | -------------------------------------------------------------------------------- /src/dbopl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002-2015 The DOSBox Team 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | #include "adlib.h" 20 | #include "dosbox.h" 21 | 22 | //Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume 23 | #define WAVE_HANDLER 10 24 | //Use a logarithmic wavetable with an exponential table for volume 25 | #define WAVE_TABLELOG 11 26 | //Use a linear wavetable with a multiply table for volume 27 | #define WAVE_TABLEMUL 12 28 | 29 | //Select the type of wave generator routine 30 | #define DBOPL_WAVE WAVE_TABLEMUL 31 | 32 | namespace DBOPL { 33 | 34 | struct Chip; 35 | struct Operator; 36 | struct Channel; 37 | 38 | #if (DBOPL_WAVE == WAVE_HANDLER) 39 | typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); 40 | #endif 41 | 42 | typedef Bits ( DBOPL::Operator::*VolumeHandler) ( ); 43 | typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output ); 44 | 45 | //Different synth modes that can generate blocks of data 46 | typedef enum { 47 | sm2AM, 48 | sm2FM, 49 | sm3AM, 50 | sm3FM, 51 | sm4Start, 52 | sm3FMFM, 53 | sm3AMFM, 54 | sm3FMAM, 55 | sm3AMAM, 56 | sm6Start, 57 | sm2Percussion, 58 | sm3Percussion, 59 | } SynthMode; 60 | 61 | //Shifts for the values contained in chandata variable 62 | enum { 63 | SHIFT_KSLBASE = 16, 64 | SHIFT_KEYCODE = 24, 65 | }; 66 | 67 | struct Operator { 68 | public: 69 | //Masks for operator 20 values 70 | enum { 71 | MASK_KSR = 0x10, 72 | MASK_SUSTAIN = 0x20, 73 | MASK_VIBRATO = 0x40, 74 | MASK_TREMOLO = 0x80, 75 | }; 76 | 77 | typedef enum { 78 | OFF, 79 | RELEASE, 80 | SUSTAIN, 81 | DECAY, 82 | ATTACK, 83 | } State; 84 | 85 | VolumeHandler volHandler; 86 | 87 | #if (DBOPL_WAVE == WAVE_HANDLER) 88 | WaveHandler waveHandler; //Routine that generate a wave 89 | #else 90 | Bit16s* waveBase; 91 | Bit32u waveMask; 92 | Bit32u waveStart; 93 | #endif 94 | Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index 95 | Bit32u waveAdd; //The base frequency without vibrato 96 | Bit32u waveCurrent; //waveAdd + vibratao 97 | 98 | Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this 99 | Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? 100 | Bit32u vibrato; //Scaled up vibrato strength 101 | Bit32s sustainLevel; //When stopping at sustain level stop here 102 | Bit32s totalLevel; //totalLevel is added to every generated volume 103 | Bit32u currentLevel; //totalLevel + tremolo 104 | Bit32s volume; //The currently active volume 105 | 106 | Bit32u attackAdd; //Timers for the different states of the envelope 107 | Bit32u decayAdd; 108 | Bit32u releaseAdd; 109 | Bit32u rateIndex; //Current position of the evenlope 110 | 111 | Bit8u rateZero; //Bits for the different states of the envelope having no changes 112 | Bit8u keyOn; //Bitmask of different values that can generate keyon 113 | //Registers, also used to check for changes 114 | Bit8u reg20, reg40, reg60, reg80, regE0; 115 | //Active part of the envelope we're in 116 | Bit8u state; 117 | //0xff when tremolo is enabled 118 | Bit8u tremoloMask; 119 | //Strength of the vibrato 120 | Bit8u vibStrength; 121 | //Keep track of the calculated KSR so we can check for changes 122 | Bit8u ksr; 123 | private: 124 | void SetState( Bit8u s ); 125 | void UpdateAttack( const Chip* chip ); 126 | void UpdateRelease( const Chip* chip ); 127 | void UpdateDecay( const Chip* chip ); 128 | public: 129 | void UpdateAttenuation(); 130 | void UpdateRates( const Chip* chip ); 131 | void UpdateFrequency( ); 132 | 133 | void Write20( const Chip* chip, Bit8u val ); 134 | void Write40( const Chip* chip, Bit8u val ); 135 | void Write60( const Chip* chip, Bit8u val ); 136 | void Write80( const Chip* chip, Bit8u val ); 137 | void WriteE0( const Chip* chip, Bit8u val ); 138 | 139 | bool Silent() const; 140 | void Prepare( const Chip* chip ); 141 | 142 | void KeyOn( Bit8u mask); 143 | void KeyOff( Bit8u mask); 144 | 145 | template< State state> 146 | Bits TemplateVolume( ); 147 | 148 | Bit32s RateForward( Bit32u add ); 149 | Bitu ForwardWave(); 150 | Bitu ForwardVolume(); 151 | 152 | Bits GetSample( Bits modulation ); 153 | Bits GetWave( Bitu index, Bitu vol ); 154 | public: 155 | Operator(); 156 | }; 157 | 158 | struct Channel { 159 | Operator op[2]; 160 | inline Operator* Op( Bitu index ) { 161 | return &( ( this + (index >> 1) )->op[ index & 1 ]); 162 | } 163 | SynthHandler synthHandler; 164 | Bit32u chanData; //Frequency/octave and derived values 165 | Bit32s old[2]; //Old data for feedback 166 | 167 | Bit8u feedback; //Feedback shift 168 | Bit8u regB0; //Register values to check for changes 169 | Bit8u regC0; 170 | //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel 171 | Bit8u fourMask; 172 | Bit8s maskLeft; //Sign extended values for both channel's panning 173 | Bit8s maskRight; 174 | 175 | //Forward the channel data to the operators of the channel 176 | void SetChanData( const Chip* chip, Bit32u data ); 177 | //Change in the chandata, check for new values and if we have to forward to operators 178 | void UpdateFrequency( const Chip* chip, Bit8u fourOp ); 179 | void WriteA0( const Chip* chip, Bit8u val ); 180 | void WriteB0( const Chip* chip, Bit8u val ); 181 | void WriteC0( const Chip* chip, Bit8u val ); 182 | void ResetC0( const Chip* chip ); 183 | 184 | //call this for the first channel 185 | template< bool opl3Mode > 186 | void GeneratePercussion( Chip* chip, Bit32s* output ); 187 | 188 | //Generate blocks of data in specific modes 189 | template 190 | Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ); 191 | Channel(); 192 | }; 193 | 194 | struct Chip { 195 | //This is used as the base counter for vibrato and tremolo 196 | Bit32u lfoCounter; 197 | Bit32u lfoAdd; 198 | 199 | 200 | Bit32u noiseCounter; 201 | Bit32u noiseAdd; 202 | Bit32u noiseValue; 203 | 204 | //Frequency scales for the different multiplications 205 | Bit32u freqMul[16]; 206 | //Rates for decay and release for rate of this chip 207 | Bit32u linearRates[76]; 208 | //Best match attack rates for the rate of this chip 209 | Bit32u attackRates[76]; 210 | 211 | //18 channels with 2 operators each 212 | Channel chan[18]; 213 | 214 | Bit8u reg104; 215 | Bit8u reg08; 216 | Bit8u reg04; 217 | Bit8u regBD; 218 | Bit8u vibratoIndex; 219 | Bit8u tremoloIndex; 220 | Bit8s vibratoSign; 221 | Bit8u vibratoShift; 222 | Bit8u tremoloValue; 223 | Bit8u vibratoStrength; 224 | Bit8u tremoloStrength; 225 | //Mask for allowed wave forms 226 | Bit8u waveFormMask; 227 | //0 or -1 when enabled 228 | Bit8s opl3Active; 229 | 230 | //Return the maximum amount of samples before and LFO change 231 | Bit32u ForwardLFO( Bit32u samples ); 232 | Bit32u ForwardNoise(); 233 | 234 | void WriteBD( Bit8u val ); 235 | void WriteReg(Bit32u reg, Bit8u val ); 236 | 237 | Bit32u WriteAddr( Bit32u port, Bit8u val ); 238 | 239 | void GenerateBlock2( Bitu samples, Bit32s* output ); 240 | void GenerateBlock3( Bitu samples, Bit32s* output ); 241 | 242 | void Generate( Bit32u samples ); 243 | void Setup( Bit32u r ); 244 | 245 | Chip(); 246 | }; 247 | 248 | struct Handler : public Adlib::Handler { 249 | DBOPL::Chip chip; 250 | virtual Bit32u WriteAddr( Bit32u port, Bit8u val ); 251 | virtual void WriteReg( Bit32u addr, Bit8u val ); 252 | virtual void Generate( MixerChannel* chan, Bitu samples ); 253 | virtual void Init( Bitu rate ); 254 | }; 255 | 256 | 257 | }; //Namespace 258 | -------------------------------------------------------------------------------- /src/dbopl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002-2015 The DOSBox Team 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | /* 20 | DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. 21 | Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 22 | Except for the table generation it's all integer math 23 | Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms 24 | The generation was based on the MAME implementation but tried to have it use less memory and be faster in general 25 | MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times 26 | 27 | //TODO Don't delay first operator 1 sample in opl3 mode 28 | //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter 29 | //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? 30 | //TODO Check if having the same accuracy in all frequency multipliers sounds better or not 31 | 32 | //DUNNO Keyon in 4op, switch to 2op without keyoff. 33 | */ 34 | 35 | 36 | 37 | #include 38 | #include 39 | #include 40 | #include "dosbox.h" 41 | #include "dbopl.h" 42 | 43 | 44 | #ifndef PI 45 | #define PI 3.14159265358979323846 46 | #endif 47 | 48 | namespace DBOPL { 49 | 50 | #define OPLRATE ((double)(14318180.0 / 288.0)) 51 | #define TREMOLO_TABLE 52 52 | 53 | //Try to use most precision for frequencies 54 | //Else try to keep different waves in synch 55 | //#define WAVE_PRECISION 1 56 | #ifndef WAVE_PRECISION 57 | //Wave bits available in the top of the 32bit range 58 | //Original adlib uses 10.10, we use 10.22 59 | #define WAVE_BITS 10 60 | #else 61 | //Need some extra bits at the top to have room for octaves and frequency multiplier 62 | //We support to 8 times lower rate 63 | //128 * 15 * 8 = 15350, 2^13.9, so need 14 bits 64 | #define WAVE_BITS 14 65 | #endif 66 | #define WAVE_SH ( 32 - WAVE_BITS ) 67 | #define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) 68 | 69 | //Use the same accuracy as the waves 70 | #define LFO_SH ( WAVE_SH - 10 ) 71 | //LFO is controlled by our tremolo 256 sample limit 72 | #define LFO_MAX ( 256 << ( LFO_SH ) ) 73 | 74 | 75 | //Maximum amount of attenuation bits 76 | //Envelope goes to 511, 9 bits 77 | #if (DBOPL_WAVE == WAVE_TABLEMUL ) 78 | //Uses the value directly 79 | #define ENV_BITS ( 9 ) 80 | #else 81 | //Add 3 bits here for more accuracy and would have to be shifted up either way 82 | #define ENV_BITS ( 9 ) 83 | #endif 84 | //Limits of the envelope with those bits and when the envelope goes silent 85 | #define ENV_MIN 0 86 | #define ENV_EXTRA ( ENV_BITS - 9 ) 87 | #define ENV_MAX ( 511 << ENV_EXTRA ) 88 | #define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) 89 | #define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) 90 | 91 | //Attack/decay/release rate counter shift 92 | #define RATE_SH 24 93 | #define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) 94 | //Has to fit within 16bit lookuptable 95 | #define MUL_SH 16 96 | 97 | //Check some ranges 98 | #if ENV_EXTRA > 3 99 | #error Too many envelope bits 100 | #endif 101 | 102 | 103 | //How much to substract from the base value for the final attenuation 104 | static const Bit8u KslCreateTable[16] = { 105 | //0 will always be be lower than 7 * 8 106 | 64, 32, 24, 19, 107 | 16, 12, 11, 10, 108 | 8, 6, 5, 4, 109 | 3, 2, 1, 0, 110 | }; 111 | 112 | #define M(_X_) ((Bit8u)( (_X_) * 2)) 113 | static const Bit8u FreqCreateTable[16] = { 114 | M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), 115 | M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) 116 | }; 117 | #undef M 118 | 119 | //We're not including the highest attack rate, that gets a special value 120 | static const Bit8u AttackSamplesTable[13] = { 121 | 69, 55, 46, 40, 122 | 35, 29, 23, 20, 123 | 19, 15, 11, 10, 124 | 9 125 | }; 126 | //On a real opl these values take 8 samples to reach and are based upon larger tables 127 | static const Bit8u EnvelopeIncreaseTable[13] = { 128 | 4, 5, 6, 7, 129 | 8, 10, 12, 14, 130 | 16, 20, 24, 28, 131 | 32, 132 | }; 133 | 134 | #if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) 135 | static Bit16u ExpTable[ 256 ]; 136 | #endif 137 | 138 | #if ( DBOPL_WAVE == WAVE_HANDLER ) 139 | //PI table used by WAVEHANDLER 140 | static Bit16u SinTable[ 512 ]; 141 | #endif 142 | 143 | #if ( DBOPL_WAVE > WAVE_HANDLER ) 144 | //Layout of the waveform table in 512 entry intervals 145 | //With overlapping waves we reduce the table to half it's size 146 | 147 | // | |//\\|____|WAV7|//__|/\ |____|/\/\| 148 | // |\\//| | |WAV7| | \/| | | 149 | // |06 |0126|17 |7 |3 |4 |4 5 |5 | 150 | 151 | //6 is just 0 shifted and masked 152 | 153 | static Bit16s WaveTable[ 8 * 512 ]; 154 | //Distance into WaveTable the wave starts 155 | static const Bit16u WaveBaseTable[8] = { 156 | 0x000, 0x200, 0x200, 0x800, 157 | 0xa00, 0xc00, 0x100, 0x400, 158 | 159 | }; 160 | //Mask the counter with this 161 | static const Bit16u WaveMaskTable[8] = { 162 | 1023, 1023, 511, 511, 163 | 1023, 1023, 512, 1023, 164 | }; 165 | 166 | //Where to start the counter on at keyon 167 | static const Bit16u WaveStartTable[8] = { 168 | 512, 0, 0, 0, 169 | 0, 512, 512, 256, 170 | }; 171 | #endif 172 | 173 | #if ( DBOPL_WAVE == WAVE_TABLEMUL ) 174 | static Bit16u MulTable[ 384 ]; 175 | #endif 176 | 177 | static Bit8u KslTable[ 8 * 16 ]; 178 | static Bit8u TremoloTable[ TREMOLO_TABLE ]; 179 | //Start of a channel behind the chip struct start 180 | static Bit16u ChanOffsetTable[32]; 181 | //Start of an operator behind the chip struct start 182 | static Bit16u OpOffsetTable[64]; 183 | 184 | //The lower bits are the shift of the operator vibrato value 185 | //The highest bit is right shifted to generate -1 or 0 for negation 186 | //So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 187 | static const Bit8s VibratoTable[ 8 ] = { 188 | 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, 189 | 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 190 | }; 191 | 192 | //Shift strength for the ksl value determined by ksl strength 193 | static const Bit8u KslShiftTable[4] = { 194 | 31,1,2,0 195 | }; 196 | 197 | //Generate a table index and table shift value using input value from a selected rate 198 | static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) { 199 | if ( val < 13 * 4 ) { //Rate 0 - 12 200 | shift = 12 - ( val >> 2 ); 201 | index = val & 3; 202 | } else if ( val < 15 * 4 ) { //rate 13 - 14 203 | shift = 0; 204 | index = val - 12 * 4; 205 | } else { //rate 15 and up 206 | shift = 0; 207 | index = 12; 208 | } 209 | } 210 | 211 | #if ( DBOPL_WAVE == WAVE_HANDLER ) 212 | /* 213 | Generate the different waveforms out of the sine/exponetial table using handlers 214 | */ 215 | static inline Bits MakeVolume( Bitu wave, Bitu volume ) { 216 | Bitu total = wave + volume; 217 | Bitu index = total & 0xff; 218 | Bitu sig = ExpTable[ index ]; 219 | Bitu exp = total >> 8; 220 | #if 0 221 | //Check if we overflow the 31 shift limit 222 | if ( exp >= 32 ) { 223 | LOG_MSG( "WTF %d %d", total, exp ); 224 | } 225 | #endif 226 | return (sig >> exp); 227 | }; 228 | 229 | static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { 230 | Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 231 | Bitu wave = SinTable[i & 511]; 232 | return (MakeVolume( wave, volume ) ^ neg) - neg; 233 | } 234 | static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { 235 | Bit32u wave = SinTable[i & 511]; 236 | wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); 237 | return MakeVolume( wave, volume ); 238 | } 239 | static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { 240 | Bitu wave = SinTable[i & 511]; 241 | return MakeVolume( wave, volume ); 242 | } 243 | static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { 244 | Bitu wave = SinTable[i & 255]; 245 | wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); 246 | return MakeVolume( wave, volume ); 247 | } 248 | static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { 249 | //Twice as fast 250 | i <<= 1; 251 | Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 252 | Bitu wave = SinTable[i & 511]; 253 | wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); 254 | return (MakeVolume( wave, volume ) ^ neg) - neg; 255 | } 256 | static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { 257 | //Twice as fast 258 | i <<= 1; 259 | Bitu wave = SinTable[i & 511]; 260 | wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); 261 | return MakeVolume( wave, volume ); 262 | } 263 | static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { 264 | Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 265 | return (MakeVolume( 0, volume ) ^ neg) - neg; 266 | } 267 | static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { 268 | //Negative is reversed here 269 | Bits neg = (( i >> 9) & 1) - 1; 270 | Bitu wave = (i << 3); 271 | //When negative the volume also runs backwards 272 | wave = ((wave ^ neg) - neg) & 4095; 273 | return (MakeVolume( wave, volume ) ^ neg) - neg; 274 | } 275 | 276 | static const WaveHandler WaveHandlerTable[8] = { 277 | WaveForm0, WaveForm1, WaveForm2, WaveForm3, 278 | WaveForm4, WaveForm5, WaveForm6, WaveForm7 279 | }; 280 | 281 | #endif 282 | 283 | /* 284 | Operator 285 | */ 286 | 287 | //We zero out when rate == 0 288 | inline void Operator::UpdateAttack( const Chip* chip ) { 289 | Bit8u rate = reg60 >> 4; 290 | if ( rate ) { 291 | Bit8u val = (rate << 2) + ksr; 292 | attackAdd = chip->attackRates[ val ]; 293 | rateZero &= ~(1 << ATTACK); 294 | } else { 295 | attackAdd = 0; 296 | rateZero |= (1 << ATTACK); 297 | } 298 | } 299 | inline void Operator::UpdateDecay( const Chip* chip ) { 300 | Bit8u rate = reg60 & 0xf; 301 | if ( rate ) { 302 | Bit8u val = (rate << 2) + ksr; 303 | decayAdd = chip->linearRates[ val ]; 304 | rateZero &= ~(1 << DECAY); 305 | } else { 306 | decayAdd = 0; 307 | rateZero |= (1 << DECAY); 308 | } 309 | } 310 | inline void Operator::UpdateRelease( const Chip* chip ) { 311 | Bit8u rate = reg80 & 0xf; 312 | if ( rate ) { 313 | Bit8u val = (rate << 2) + ksr; 314 | releaseAdd = chip->linearRates[ val ]; 315 | rateZero &= ~(1 << RELEASE); 316 | if ( !(reg20 & MASK_SUSTAIN ) ) { 317 | rateZero &= ~( 1 << SUSTAIN ); 318 | } 319 | } else { 320 | rateZero |= (1 << RELEASE); 321 | releaseAdd = 0; 322 | if ( !(reg20 & MASK_SUSTAIN ) ) { 323 | rateZero |= ( 1 << SUSTAIN ); 324 | } 325 | } 326 | } 327 | 328 | inline void Operator::UpdateAttenuation( ) { 329 | Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff); 330 | Bit32u tl = reg40 & 0x3f; 331 | Bit8u kslShift = KslShiftTable[ reg40 >> 6 ]; 332 | //Make sure the attenuation goes to the right bits 333 | totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max 334 | totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; 335 | } 336 | 337 | void Operator::UpdateFrequency( ) { 338 | Bit32u freq = chanData & (( 1 << 10 ) - 1); 339 | Bit32u block = (chanData >> 10) & 0xff; 340 | #ifdef WAVE_PRECISION 341 | block = 7 - block; 342 | waveAdd = ( freq * freqMul ) >> block; 343 | #else 344 | waveAdd = ( freq << block ) * freqMul; 345 | #endif 346 | if ( reg20 & MASK_VIBRATO ) { 347 | vibStrength = (Bit8u)(freq >> 7); 348 | 349 | #ifdef WAVE_PRECISION 350 | vibrato = ( vibStrength * freqMul ) >> block; 351 | #else 352 | vibrato = ( vibStrength << block ) * freqMul; 353 | #endif 354 | } else { 355 | vibStrength = 0; 356 | vibrato = 0; 357 | } 358 | } 359 | 360 | void Operator::UpdateRates( const Chip* chip ) { 361 | //Mame seems to reverse this where enabling ksr actually lowers 362 | //the rate, but pdf manuals says otherwise? 363 | Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff); 364 | if ( !( reg20 & MASK_KSR ) ) { 365 | newKsr >>= 2; 366 | } 367 | if ( ksr == newKsr ) 368 | return; 369 | ksr = newKsr; 370 | UpdateAttack( chip ); 371 | UpdateDecay( chip ); 372 | UpdateRelease( chip ); 373 | } 374 | 375 | INLINE Bit32s Operator::RateForward( Bit32u add ) { 376 | rateIndex += add; 377 | Bit32s ret = rateIndex >> RATE_SH; 378 | rateIndex = rateIndex & RATE_MASK; 379 | return ret; 380 | } 381 | 382 | template< Operator::State yes> 383 | Bits Operator::TemplateVolume( ) { 384 | Bit32s vol = volume; 385 | Bit32s change; 386 | switch ( yes ) { 387 | case OFF: 388 | return ENV_MAX; 389 | case ATTACK: 390 | change = RateForward( attackAdd ); 391 | if ( !change ) 392 | return vol; 393 | vol += ( (~vol) * change ) >> 3; 394 | if ( vol < ENV_MIN ) { 395 | volume = ENV_MIN; 396 | rateIndex = 0; 397 | SetState( DECAY ); 398 | return ENV_MIN; 399 | } 400 | break; 401 | case DECAY: 402 | vol += RateForward( decayAdd ); 403 | if ( GCC_UNLIKELY(vol >= sustainLevel) ) { 404 | //Check if we didn't overshoot max attenuation, then just go off 405 | if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { 406 | volume = ENV_MAX; 407 | SetState( OFF ); 408 | return ENV_MAX; 409 | } 410 | //Continue as sustain 411 | rateIndex = 0; 412 | SetState( SUSTAIN ); 413 | } 414 | break; 415 | case SUSTAIN: 416 | if ( reg20 & MASK_SUSTAIN ) { 417 | return vol; 418 | } 419 | //In sustain phase, but not sustaining, do regular release 420 | case RELEASE: 421 | vol += RateForward( releaseAdd );; 422 | if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { 423 | volume = ENV_MAX; 424 | SetState( OFF ); 425 | return ENV_MAX; 426 | } 427 | break; 428 | } 429 | volume = vol; 430 | return vol; 431 | } 432 | 433 | static const VolumeHandler VolumeHandlerTable[5] = { 434 | &Operator::TemplateVolume< Operator::OFF >, 435 | &Operator::TemplateVolume< Operator::RELEASE >, 436 | &Operator::TemplateVolume< Operator::SUSTAIN >, 437 | &Operator::TemplateVolume< Operator::DECAY >, 438 | &Operator::TemplateVolume< Operator::ATTACK > 439 | }; 440 | 441 | INLINE Bitu Operator::ForwardVolume() { 442 | return currentLevel + (this->*volHandler)(); 443 | } 444 | 445 | 446 | INLINE Bitu Operator::ForwardWave() { 447 | waveIndex += waveCurrent; 448 | return waveIndex >> WAVE_SH; 449 | } 450 | 451 | void Operator::Write20( const Chip* chip, Bit8u val ) { 452 | Bit8u change = (reg20 ^ val ); 453 | if ( !change ) 454 | return; 455 | reg20 = val; 456 | //Shift the tremolo bit over the entire register, saved a branch, YES! 457 | tremoloMask = (Bit8s)(val) >> 7; 458 | tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); 459 | //Update specific features based on changes 460 | if ( change & MASK_KSR ) { 461 | UpdateRates( chip ); 462 | } 463 | //With sustain enable the volume doesn't change 464 | if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) { 465 | rateZero |= ( 1 << SUSTAIN ); 466 | } else { 467 | rateZero &= ~( 1 << SUSTAIN ); 468 | } 469 | //Frequency multiplier or vibrato changed 470 | if ( change & (0xf | MASK_VIBRATO) ) { 471 | freqMul = chip->freqMul[ val & 0xf ]; 472 | UpdateFrequency(); 473 | } 474 | } 475 | 476 | void Operator::Write40( const Chip* /*chip*/, Bit8u val ) { 477 | if (!(reg40 ^ val )) 478 | return; 479 | reg40 = val; 480 | UpdateAttenuation( ); 481 | } 482 | 483 | void Operator::Write60( const Chip* chip, Bit8u val ) { 484 | Bit8u change = reg60 ^ val; 485 | reg60 = val; 486 | if ( change & 0x0f ) { 487 | UpdateDecay( chip ); 488 | } 489 | if ( change & 0xf0 ) { 490 | UpdateAttack( chip ); 491 | } 492 | } 493 | 494 | void Operator::Write80( const Chip* chip, Bit8u val ) { 495 | Bit8u change = (reg80 ^ val ); 496 | if ( !change ) 497 | return; 498 | reg80 = val; 499 | Bit8u sustain = val >> 4; 500 | //Turn 0xf into 0x1f 501 | sustain |= ( sustain + 1) & 0x10; 502 | sustainLevel = sustain << ( ENV_BITS - 5 ); 503 | if ( change & 0x0f ) { 504 | UpdateRelease( chip ); 505 | } 506 | } 507 | 508 | void Operator::WriteE0( const Chip* chip, Bit8u val ) { 509 | if ( !(regE0 ^ val) ) 510 | return; 511 | //in opl3 mode you can always selet 7 waveforms regardless of waveformselect 512 | Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); 513 | regE0 = val; 514 | #if ( DBOPL_WAVE == WAVE_HANDLER ) 515 | waveHandler = WaveHandlerTable[ waveForm ]; 516 | #else 517 | waveBase = WaveTable + WaveBaseTable[ waveForm ]; 518 | waveStart = WaveStartTable[ waveForm ] << WAVE_SH; 519 | waveMask = WaveMaskTable[ waveForm ]; 520 | #endif 521 | } 522 | 523 | INLINE void Operator::SetState( Bit8u s ) { 524 | state = s; 525 | volHandler = VolumeHandlerTable[ s ]; 526 | } 527 | 528 | INLINE bool Operator::Silent() const { 529 | if ( !ENV_SILENT( totalLevel + volume ) ) 530 | return false; 531 | if ( !(rateZero & ( 1 << state ) ) ) 532 | return false; 533 | return true; 534 | } 535 | 536 | INLINE void Operator::Prepare( const Chip* chip ) { 537 | currentLevel = totalLevel + (chip->tremoloValue & tremoloMask); 538 | waveCurrent = waveAdd; 539 | if ( vibStrength >> chip->vibratoShift ) { 540 | Bit32s add = vibrato >> chip->vibratoShift; 541 | //Sign extend over the shift value 542 | Bit32s neg = chip->vibratoSign; 543 | //Negate the add with -1 or 0 544 | add = ( add ^ neg ) - neg; 545 | waveCurrent += add; 546 | } 547 | } 548 | 549 | void Operator::KeyOn( Bit8u mask ) { 550 | if ( !keyOn ) { 551 | //Restart the frequency generator 552 | #if ( DBOPL_WAVE > WAVE_HANDLER ) 553 | waveIndex = waveStart; 554 | #else 555 | waveIndex = 0; 556 | #endif 557 | rateIndex = 0; 558 | SetState( ATTACK ); 559 | } 560 | keyOn |= mask; 561 | } 562 | 563 | void Operator::KeyOff( Bit8u mask ) { 564 | keyOn &= ~mask; 565 | if ( !keyOn ) { 566 | if ( state != OFF ) { 567 | SetState( RELEASE ); 568 | } 569 | } 570 | } 571 | 572 | INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) { 573 | #if ( DBOPL_WAVE == WAVE_HANDLER ) 574 | return waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); 575 | #elif ( DBOPL_WAVE == WAVE_TABLEMUL ) 576 | return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; 577 | #elif ( DBOPL_WAVE == WAVE_TABLELOG ) 578 | Bit32s wave = waveBase[ index & waveMask ]; 579 | Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); 580 | Bit32s sig = ExpTable[ total & 0xff ]; 581 | Bit32u exp = total >> 8; 582 | Bit32s neg = wave >> 16; 583 | return ((sig ^ neg) - neg) >> exp; 584 | #else 585 | #error "No valid wave routine" 586 | #endif 587 | } 588 | 589 | Bits INLINE Operator::GetSample( Bits modulation ) { 590 | Bitu vol = ForwardVolume(); 591 | if ( ENV_SILENT( vol ) ) { 592 | //Simply forward the wave 593 | waveIndex += waveCurrent; 594 | return 0; 595 | } else { 596 | Bitu index = ForwardWave(); 597 | index += modulation; 598 | return GetWave( index, vol ); 599 | } 600 | } 601 | 602 | Operator::Operator() { 603 | chanData = 0; 604 | freqMul = 0; 605 | waveIndex = 0; 606 | waveAdd = 0; 607 | waveCurrent = 0; 608 | keyOn = 0; 609 | ksr = 0; 610 | reg20 = 0; 611 | reg40 = 0; 612 | reg60 = 0; 613 | reg80 = 0; 614 | regE0 = 0; 615 | SetState( OFF ); 616 | rateZero = (1 << OFF); 617 | sustainLevel = ENV_MAX; 618 | currentLevel = ENV_MAX; 619 | totalLevel = ENV_MAX; 620 | volume = ENV_MAX; 621 | releaseAdd = 0; 622 | } 623 | 624 | /* 625 | Channel 626 | */ 627 | 628 | Channel::Channel() { 629 | old[0] = old[1] = 0; 630 | chanData = 0; 631 | regB0 = 0; 632 | regC0 = 0; 633 | maskLeft = -1; 634 | maskRight = -1; 635 | feedback = 31; 636 | fourMask = 0; 637 | synthHandler = &Channel::BlockTemplate< sm2FM >; 638 | }; 639 | 640 | void Channel::SetChanData( const Chip* chip, Bit32u data ) { 641 | Bit32u change = chanData ^ data; 642 | chanData = data; 643 | Op( 0 )->chanData = data; 644 | Op( 1 )->chanData = data; 645 | //Since a frequency update triggered this, always update frequency 646 | Op( 0 )->UpdateFrequency(); 647 | Op( 1 )->UpdateFrequency(); 648 | if ( change & ( 0xff << SHIFT_KSLBASE ) ) { 649 | Op( 0 )->UpdateAttenuation(); 650 | Op( 1 )->UpdateAttenuation(); 651 | } 652 | if ( change & ( 0xff << SHIFT_KEYCODE ) ) { 653 | Op( 0 )->UpdateRates( chip ); 654 | Op( 1 )->UpdateRates( chip ); 655 | } 656 | } 657 | 658 | void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) { 659 | //Extrace the frequency bits 660 | Bit32u data = chanData & 0xffff; 661 | Bit32u kslBase = KslTable[ data >> 6 ]; 662 | Bit32u keyCode = ( data & 0x1c00) >> 9; 663 | if ( chip->reg08 & 0x40 ) { 664 | keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ 665 | } else { 666 | keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ 667 | } 668 | //Add the keycode and ksl into the highest bits of chanData 669 | data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); 670 | ( this + 0 )->SetChanData( chip, data ); 671 | if ( fourOp & 0x3f ) { 672 | ( this + 1 )->SetChanData( chip, data ); 673 | } 674 | } 675 | 676 | void Channel::WriteA0( const Chip* chip, Bit8u val ) { 677 | Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; 678 | //Don't handle writes to silent fourop channels 679 | if ( fourOp > 0x80 ) 680 | return; 681 | Bit32u change = (chanData ^ val ) & 0xff; 682 | if ( change ) { 683 | chanData ^= change; 684 | UpdateFrequency( chip, fourOp ); 685 | } 686 | } 687 | 688 | void Channel::WriteB0( const Chip* chip, Bit8u val ) { 689 | Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; 690 | //Don't handle writes to silent fourop channels 691 | if ( fourOp > 0x80 ) 692 | return; 693 | Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00; 694 | if ( change ) { 695 | chanData ^= change; 696 | UpdateFrequency( chip, fourOp ); 697 | } 698 | //Check for a change in the keyon/off state 699 | if ( !(( val ^ regB0) & 0x20)) 700 | return; 701 | regB0 = val; 702 | if ( val & 0x20 ) { 703 | Op(0)->KeyOn( 0x1 ); 704 | Op(1)->KeyOn( 0x1 ); 705 | if ( fourOp & 0x3f ) { 706 | ( this + 1 )->Op(0)->KeyOn( 1 ); 707 | ( this + 1 )->Op(1)->KeyOn( 1 ); 708 | } 709 | } else { 710 | Op(0)->KeyOff( 0x1 ); 711 | Op(1)->KeyOff( 0x1 ); 712 | if ( fourOp & 0x3f ) { 713 | ( this + 1 )->Op(0)->KeyOff( 1 ); 714 | ( this + 1 )->Op(1)->KeyOff( 1 ); 715 | } 716 | } 717 | } 718 | 719 | void Channel::WriteC0( const Chip* chip, Bit8u val ) { 720 | Bit8u change = val ^ regC0; 721 | if ( !change ) 722 | return; 723 | regC0 = val; 724 | feedback = ( val >> 1 ) & 7; 725 | if ( feedback ) { 726 | //We shift the input to the right 10 bit wave index value 727 | feedback = 9 - feedback; 728 | } else { 729 | feedback = 31; 730 | } 731 | //Select the new synth mode 732 | if ( chip->opl3Active ) { 733 | //4-op mode enabled for this channel 734 | if ( (chip->reg104 & fourMask) & 0x3f ) { 735 | Channel* chan0, *chan1; 736 | //Check if it's the 2nd channel in a 4-op 737 | if ( !(fourMask & 0x80 ) ) { 738 | chan0 = this; 739 | chan1 = this + 1; 740 | } else { 741 | chan0 = this - 1; 742 | chan1 = this; 743 | } 744 | 745 | Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); 746 | switch ( synth ) { 747 | case 0: 748 | chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >; 749 | break; 750 | case 1: 751 | chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >; 752 | break; 753 | case 2: 754 | chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >; 755 | break; 756 | case 3: 757 | chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >; 758 | break; 759 | } 760 | //Disable updating percussion channels 761 | } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) { 762 | 763 | //Regular dual op, am or fm 764 | } else if ( val & 1 ) { 765 | synthHandler = &Channel::BlockTemplate< sm3AM >; 766 | } else { 767 | synthHandler = &Channel::BlockTemplate< sm3FM >; 768 | } 769 | maskLeft = ( val & 0x10 ) ? -1 : 0; 770 | maskRight = ( val & 0x20 ) ? -1 : 0; 771 | //opl2 active 772 | } else { 773 | //Disable updating percussion channels 774 | if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { 775 | 776 | //Regular dual op, am or fm 777 | } else if ( val & 1 ) { 778 | synthHandler = &Channel::BlockTemplate< sm2AM >; 779 | } else { 780 | synthHandler = &Channel::BlockTemplate< sm2FM >; 781 | } 782 | } 783 | } 784 | 785 | void Channel::ResetC0( const Chip* chip ) { 786 | Bit8u val = regC0; 787 | regC0 ^= 0xff; 788 | WriteC0( chip, val ); 789 | }; 790 | 791 | template< bool opl3Mode> 792 | INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) { 793 | Channel* chan = this; 794 | 795 | //BassDrum 796 | Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; 797 | old[0] = old[1]; 798 | old[1] = Op(0)->GetSample( mod ); 799 | 800 | //When bassdrum is in AM mode first operator is ignoed 801 | if ( chan->regC0 & 1 ) { 802 | mod = 0; 803 | } else { 804 | mod = old[0]; 805 | } 806 | Bit32s sample = Op(1)->GetSample( mod ); 807 | 808 | 809 | //Precalculate stuff used by other outputs 810 | Bit32u noiseBit = chip->ForwardNoise() & 0x1; 811 | Bit32u c2 = Op(2)->ForwardWave(); 812 | Bit32u c5 = Op(5)->ForwardWave(); 813 | Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; 814 | 815 | //Hi-Hat 816 | Bit32u hhVol = Op(2)->ForwardVolume(); 817 | if ( !ENV_SILENT( hhVol ) ) { 818 | Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); 819 | sample += Op(2)->GetWave( hhIndex, hhVol ); 820 | } 821 | //Snare Drum 822 | Bit32u sdVol = Op(3)->ForwardVolume(); 823 | if ( !ENV_SILENT( sdVol ) ) { 824 | Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); 825 | sample += Op(3)->GetWave( sdIndex, sdVol ); 826 | } 827 | //Tom-tom 828 | sample += Op(4)->GetSample( 0 ); 829 | 830 | //Top-Cymbal 831 | Bit32u tcVol = Op(5)->ForwardVolume(); 832 | if ( !ENV_SILENT( tcVol ) ) { 833 | Bit32u tcIndex = (1 + phaseBit) << 8; 834 | sample += Op(5)->GetWave( tcIndex, tcVol ); 835 | } 836 | sample <<= 1; 837 | if ( opl3Mode ) { 838 | output[0] += sample; 839 | output[1] += sample; 840 | } else { 841 | output[0] += sample; 842 | } 843 | } 844 | 845 | template 846 | Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) { 847 | switch( mode ) { 848 | case sm2AM: 849 | case sm3AM: 850 | if ( Op(0)->Silent() && Op(1)->Silent() ) { 851 | old[0] = old[1] = 0; 852 | return (this + 1); 853 | } 854 | break; 855 | case sm2FM: 856 | case sm3FM: 857 | if ( Op(1)->Silent() ) { 858 | old[0] = old[1] = 0; 859 | return (this + 1); 860 | } 861 | break; 862 | case sm3FMFM: 863 | if ( Op(3)->Silent() ) { 864 | old[0] = old[1] = 0; 865 | return (this + 2); 866 | } 867 | break; 868 | case sm3AMFM: 869 | if ( Op(0)->Silent() && Op(3)->Silent() ) { 870 | old[0] = old[1] = 0; 871 | return (this + 2); 872 | } 873 | break; 874 | case sm3FMAM: 875 | if ( Op(1)->Silent() && Op(3)->Silent() ) { 876 | old[0] = old[1] = 0; 877 | return (this + 2); 878 | } 879 | break; 880 | case sm3AMAM: 881 | if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) { 882 | old[0] = old[1] = 0; 883 | return (this + 2); 884 | } 885 | break; 886 | } 887 | //Init the operators with the the current vibrato and tremolo values 888 | Op( 0 )->Prepare( chip ); 889 | Op( 1 )->Prepare( chip ); 890 | if ( mode > sm4Start ) { 891 | Op( 2 )->Prepare( chip ); 892 | Op( 3 )->Prepare( chip ); 893 | } 894 | if ( mode > sm6Start ) { 895 | Op( 4 )->Prepare( chip ); 896 | Op( 5 )->Prepare( chip ); 897 | } 898 | for ( Bitu i = 0; i < samples; i++ ) { 899 | //Early out for percussion handlers 900 | if ( mode == sm2Percussion ) { 901 | GeneratePercussion( chip, output + i ); 902 | continue; //Prevent some unitialized value bitching 903 | } else if ( mode == sm3Percussion ) { 904 | GeneratePercussion( chip, output + i * 2 ); 905 | continue; //Prevent some unitialized value bitching 906 | } 907 | 908 | //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise 909 | Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; 910 | old[0] = old[1]; 911 | old[1] = Op(0)->GetSample( mod ); 912 | Bit32s sample; 913 | Bit32s out0 = old[0]; 914 | if ( mode == sm2AM || mode == sm3AM ) { 915 | sample = out0 + Op(1)->GetSample( 0 ); 916 | } else if ( mode == sm2FM || mode == sm3FM ) { 917 | sample = Op(1)->GetSample( out0 ); 918 | } else if ( mode == sm3FMFM ) { 919 | Bits next = Op(1)->GetSample( out0 ); 920 | next = Op(2)->GetSample( next ); 921 | sample = Op(3)->GetSample( next ); 922 | } else if ( mode == sm3AMFM ) { 923 | sample = out0; 924 | Bits next = Op(1)->GetSample( 0 ); 925 | next = Op(2)->GetSample( next ); 926 | sample += Op(3)->GetSample( next ); 927 | } else if ( mode == sm3FMAM ) { 928 | sample = Op(1)->GetSample( out0 ); 929 | Bits next = Op(2)->GetSample( 0 ); 930 | sample += Op(3)->GetSample( next ); 931 | } else if ( mode == sm3AMAM ) { 932 | sample = out0; 933 | Bits next = Op(1)->GetSample( 0 ); 934 | sample += Op(2)->GetSample( next ); 935 | sample += Op(3)->GetSample( 0 ); 936 | } 937 | switch( mode ) { 938 | case sm2AM: 939 | case sm2FM: 940 | output[ i ] += sample; 941 | break; 942 | case sm3AM: 943 | case sm3FM: 944 | case sm3FMFM: 945 | case sm3AMFM: 946 | case sm3FMAM: 947 | case sm3AMAM: 948 | output[ i * 2 + 0 ] += sample & maskLeft; 949 | output[ i * 2 + 1 ] += sample & maskRight; 950 | break; 951 | } 952 | } 953 | switch( mode ) { 954 | case sm2AM: 955 | case sm2FM: 956 | case sm3AM: 957 | case sm3FM: 958 | return ( this + 1 ); 959 | case sm3FMFM: 960 | case sm3AMFM: 961 | case sm3FMAM: 962 | case sm3AMAM: 963 | return( this + 2 ); 964 | case sm2Percussion: 965 | case sm3Percussion: 966 | return( this + 3 ); 967 | } 968 | return 0; 969 | } 970 | 971 | /* 972 | Chip 973 | */ 974 | 975 | Chip::Chip() { 976 | reg08 = 0; 977 | reg04 = 0; 978 | regBD = 0; 979 | reg104 = 0; 980 | opl3Active = 0; 981 | } 982 | 983 | INLINE Bit32u Chip::ForwardNoise() { 984 | noiseCounter += noiseAdd; 985 | Bitu count = noiseCounter >> LFO_SH; 986 | noiseCounter &= WAVE_MASK; 987 | for ( ; count > 0; --count ) { 988 | //Noise calculation from mame 989 | noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) ); 990 | noiseValue >>= 1; 991 | } 992 | return noiseValue; 993 | } 994 | 995 | INLINE Bit32u Chip::ForwardLFO( Bit32u samples ) { 996 | //Current vibrato value, runs 4x slower than tremolo 997 | vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7; 998 | vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; 999 | tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; 1000 | 1001 | //Check hom many samples there can be done before the value changes 1002 | Bit32u todo = LFO_MAX - lfoCounter; 1003 | Bit32u count = (todo + lfoAdd - 1) / lfoAdd; 1004 | if ( count > samples ) { 1005 | count = samples; 1006 | lfoCounter += count * lfoAdd; 1007 | } else { 1008 | lfoCounter += count * lfoAdd; 1009 | lfoCounter &= (LFO_MAX - 1); 1010 | //Maximum of 7 vibrato value * 4 1011 | vibratoIndex = ( vibratoIndex + 1 ) & 31; 1012 | //Clip tremolo to the the table size 1013 | if ( tremoloIndex + 1 < TREMOLO_TABLE ) 1014 | ++tremoloIndex; 1015 | else 1016 | tremoloIndex = 0; 1017 | } 1018 | return count; 1019 | } 1020 | 1021 | 1022 | void Chip::WriteBD( Bit8u val ) { 1023 | Bit8u change = regBD ^ val; 1024 | if ( !change ) 1025 | return; 1026 | regBD = val; 1027 | //TODO could do this with shift and xor? 1028 | vibratoStrength = (val & 0x40) ? 0x00 : 0x01; 1029 | tremoloStrength = (val & 0x80) ? 0x00 : 0x02; 1030 | if ( val & 0x20 ) { 1031 | //Drum was just enabled, make sure channel 6 has the right synth 1032 | if ( change & 0x20 ) { 1033 | if ( opl3Active ) { 1034 | chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >; 1035 | } else { 1036 | chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >; 1037 | } 1038 | } 1039 | //Bass Drum 1040 | if ( val & 0x10 ) { 1041 | chan[6].op[0].KeyOn( 0x2 ); 1042 | chan[6].op[1].KeyOn( 0x2 ); 1043 | } else { 1044 | chan[6].op[0].KeyOff( 0x2 ); 1045 | chan[6].op[1].KeyOff( 0x2 ); 1046 | } 1047 | //Hi-Hat 1048 | if ( val & 0x1 ) { 1049 | chan[7].op[0].KeyOn( 0x2 ); 1050 | } else { 1051 | chan[7].op[0].KeyOff( 0x2 ); 1052 | } 1053 | //Snare 1054 | if ( val & 0x8 ) { 1055 | chan[7].op[1].KeyOn( 0x2 ); 1056 | } else { 1057 | chan[7].op[1].KeyOff( 0x2 ); 1058 | } 1059 | //Tom-Tom 1060 | if ( val & 0x4 ) { 1061 | chan[8].op[0].KeyOn( 0x2 ); 1062 | } else { 1063 | chan[8].op[0].KeyOff( 0x2 ); 1064 | } 1065 | //Top Cymbal 1066 | if ( val & 0x2 ) { 1067 | chan[8].op[1].KeyOn( 0x2 ); 1068 | } else { 1069 | chan[8].op[1].KeyOff( 0x2 ); 1070 | } 1071 | //Toggle keyoffs when we turn off the percussion 1072 | } else if ( change & 0x20 ) { 1073 | //Trigger a reset to setup the original synth handler 1074 | chan[6].ResetC0( this ); 1075 | chan[6].op[0].KeyOff( 0x2 ); 1076 | chan[6].op[1].KeyOff( 0x2 ); 1077 | chan[7].op[0].KeyOff( 0x2 ); 1078 | chan[7].op[1].KeyOff( 0x2 ); 1079 | chan[8].op[0].KeyOff( 0x2 ); 1080 | chan[8].op[1].KeyOff( 0x2 ); 1081 | } 1082 | } 1083 | 1084 | 1085 | #define REGOP( _FUNC_ ) \ 1086 | index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ 1087 | if ( OpOffsetTable[ index ] ) { \ 1088 | Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \ 1089 | regOp->_FUNC_( this, val ); \ 1090 | } 1091 | 1092 | #define REGCHAN( _FUNC_ ) \ 1093 | index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ 1094 | if ( ChanOffsetTable[ index ] ) { \ 1095 | Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \ 1096 | regChan->_FUNC_( this, val ); \ 1097 | } 1098 | 1099 | void Chip::WriteReg( Bit32u reg, Bit8u val ) { 1100 | Bitu index; 1101 | switch ( (reg & 0xf0) >> 4 ) { 1102 | case 0x00 >> 4: 1103 | if ( reg == 0x01 ) { 1104 | waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0; 1105 | } else if ( reg == 0x104 ) { 1106 | //Only detect changes in lowest 6 bits 1107 | if ( !((reg104 ^ val) & 0x3f) ) 1108 | return; 1109 | //Always keep the highest bit enabled, for checking > 0x80 1110 | reg104 = 0x80 | ( val & 0x3f ); 1111 | } else if ( reg == 0x105 ) { 1112 | //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register 1113 | if ( !((opl3Active ^ val) & 1 ) ) 1114 | return; 1115 | opl3Active = ( val & 1 ) ? 0xff : 0; 1116 | //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers 1117 | for ( int i = 0; i < 18;i++ ) { 1118 | chan[i].ResetC0( this ); 1119 | } 1120 | } else if ( reg == 0x08 ) { 1121 | reg08 = val; 1122 | } 1123 | case 0x10 >> 4: 1124 | break; 1125 | case 0x20 >> 4: 1126 | case 0x30 >> 4: 1127 | REGOP( Write20 ); 1128 | break; 1129 | case 0x40 >> 4: 1130 | case 0x50 >> 4: 1131 | REGOP( Write40 ); 1132 | break; 1133 | case 0x60 >> 4: 1134 | case 0x70 >> 4: 1135 | REGOP( Write60 ); 1136 | break; 1137 | case 0x80 >> 4: 1138 | case 0x90 >> 4: 1139 | REGOP( Write80 ); 1140 | break; 1141 | case 0xa0 >> 4: 1142 | REGCHAN( WriteA0 ); 1143 | break; 1144 | case 0xb0 >> 4: 1145 | if ( reg == 0xbd ) { 1146 | WriteBD( val ); 1147 | } else { 1148 | REGCHAN( WriteB0 ); 1149 | } 1150 | break; 1151 | case 0xc0 >> 4: 1152 | REGCHAN( WriteC0 ); 1153 | case 0xd0 >> 4: 1154 | break; 1155 | case 0xe0 >> 4: 1156 | case 0xf0 >> 4: 1157 | REGOP( WriteE0 ); 1158 | break; 1159 | } 1160 | } 1161 | 1162 | 1163 | Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) { 1164 | switch ( port & 3 ) { 1165 | case 0: 1166 | return val; 1167 | case 2: 1168 | if ( opl3Active || (val == 0x05) ) 1169 | return 0x100 | val; 1170 | else 1171 | return val; 1172 | } 1173 | return 0; 1174 | } 1175 | 1176 | void Chip::GenerateBlock2( Bitu total, Bit32s* output ) { 1177 | while ( total > 0 ) { 1178 | Bit32u samples = ForwardLFO( total ); 1179 | memset(output, 0, sizeof(Bit32s) * samples); 1180 | int count = 0; 1181 | for( Channel* ch = chan; ch < chan + 9; ) { 1182 | count++; 1183 | ch = (ch->*(ch->synthHandler))( this, samples, output ); 1184 | } 1185 | total -= samples; 1186 | output += samples; 1187 | } 1188 | } 1189 | 1190 | void Chip::GenerateBlock3( Bitu total, Bit32s* output ) { 1191 | while ( total > 0 ) { 1192 | Bit32u samples = ForwardLFO( total ); 1193 | memset(output, 0, sizeof(Bit32s) * samples *2); 1194 | int count = 0; 1195 | for( Channel* ch = chan; ch < chan + 18; ) { 1196 | count++; 1197 | ch = (ch->*(ch->synthHandler))( this, samples, output ); 1198 | } 1199 | total -= samples; 1200 | output += samples * 2; 1201 | } 1202 | } 1203 | 1204 | void Chip::Setup( Bit32u rate ) { 1205 | double original = OPLRATE; 1206 | // double original = rate; 1207 | double scale = original / (double)rate; 1208 | 1209 | //Noise counter is run at the same precision as general waves 1210 | noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); 1211 | noiseCounter = 0; 1212 | noiseValue = 1; //Make sure it triggers the noise xor the first time 1213 | //The low frequency oscillation counter 1214 | //Every time his overflows vibrato and tremoloindex are increased 1215 | lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); 1216 | lfoCounter = 0; 1217 | vibratoIndex = 0; 1218 | tremoloIndex = 0; 1219 | 1220 | //With higher octave this gets shifted up 1221 | //-1 since the freqCreateTable = *2 1222 | #ifdef WAVE_PRECISION 1223 | double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); 1224 | for ( int i = 0; i < 16; i++ ) { 1225 | freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); 1226 | } 1227 | #else 1228 | Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); 1229 | for ( int i = 0; i < 16; i++ ) { 1230 | freqMul[i] = freqScale * FreqCreateTable[ i ]; 1231 | } 1232 | #endif 1233 | 1234 | //-3 since the real envelope takes 8 steps to reach the single value we supply 1235 | for ( Bit8u i = 0; i < 76; i++ ) { 1236 | Bit8u index, shift; 1237 | EnvelopeSelect( i, index, shift ); 1238 | linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); 1239 | } 1240 | //Generate the best matching attack rate 1241 | for ( Bit8u i = 0; i < 62; i++ ) { 1242 | Bit8u index, shift; 1243 | EnvelopeSelect( i, index, shift ); 1244 | //Original amount of samples the attack would take 1245 | Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale); 1246 | 1247 | Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 ))); 1248 | Bit32s bestAdd = guessAdd; 1249 | Bit32u bestDiff = 1 << 30; 1250 | for( Bit32u passes = 0; passes < 16; passes ++ ) { 1251 | Bit32s volume = ENV_MAX; 1252 | Bit32s samples = 0; 1253 | Bit32u count = 0; 1254 | while ( volume > 0 && samples < original * 2 ) { 1255 | count += guessAdd; 1256 | Bit32s change = count >> RATE_SH; 1257 | count &= RATE_MASK; 1258 | if ( GCC_UNLIKELY(change) ) { // less than 1 % 1259 | volume += ( ~volume * change ) >> 3; 1260 | } 1261 | samples++; 1262 | 1263 | } 1264 | Bit32s diff = original - samples; 1265 | Bit32u lDiff = labs( diff ); 1266 | //Init last on first pass 1267 | if ( lDiff < bestDiff ) { 1268 | bestDiff = lDiff; 1269 | bestAdd = guessAdd; 1270 | if ( !bestDiff ) 1271 | break; 1272 | } 1273 | //Below our target 1274 | if ( diff < 0 ) { 1275 | //Better than the last time 1276 | Bit32s mul = ((original - diff) << 12) / original; 1277 | guessAdd = ((guessAdd * mul) >> 12); 1278 | guessAdd++; 1279 | } else if ( diff > 0 ) { 1280 | Bit32s mul = ((original - diff) << 12) / original; 1281 | guessAdd = (guessAdd * mul) >> 12; 1282 | guessAdd--; 1283 | } 1284 | } 1285 | attackRates[i] = bestAdd; 1286 | } 1287 | for ( Bit8u i = 62; i < 76; i++ ) { 1288 | //This should provide instant volume maximizing 1289 | attackRates[i] = 8 << RATE_SH; 1290 | } 1291 | //Setup the channels with the correct four op flags 1292 | //Channels are accessed through a table so they appear linear here 1293 | chan[ 0].fourMask = 0x00 | ( 1 << 0 ); 1294 | chan[ 1].fourMask = 0x80 | ( 1 << 0 ); 1295 | chan[ 2].fourMask = 0x00 | ( 1 << 1 ); 1296 | chan[ 3].fourMask = 0x80 | ( 1 << 1 ); 1297 | chan[ 4].fourMask = 0x00 | ( 1 << 2 ); 1298 | chan[ 5].fourMask = 0x80 | ( 1 << 2 ); 1299 | 1300 | chan[ 9].fourMask = 0x00 | ( 1 << 3 ); 1301 | chan[10].fourMask = 0x80 | ( 1 << 3 ); 1302 | chan[11].fourMask = 0x00 | ( 1 << 4 ); 1303 | chan[12].fourMask = 0x80 | ( 1 << 4 ); 1304 | chan[13].fourMask = 0x00 | ( 1 << 5 ); 1305 | chan[14].fourMask = 0x80 | ( 1 << 5 ); 1306 | 1307 | //mark the percussion channels 1308 | chan[ 6].fourMask = 0x40; 1309 | chan[ 7].fourMask = 0x40; 1310 | chan[ 8].fourMask = 0x40; 1311 | 1312 | //Clear Everything in opl3 mode 1313 | WriteReg( 0x105, 0x1 ); 1314 | for ( int i = 0; i < 512; i++ ) { 1315 | if ( i == 0x105 ) 1316 | continue; 1317 | WriteReg( i, 0xff ); 1318 | WriteReg( i, 0x0 ); 1319 | } 1320 | WriteReg( 0x105, 0x0 ); 1321 | //Clear everything in opl2 mode 1322 | for ( int i = 0; i < 255; i++ ) { 1323 | WriteReg( i, 0xff ); 1324 | WriteReg( i, 0x0 ); 1325 | } 1326 | } 1327 | 1328 | static bool doneTables = false; 1329 | void InitTables( void ) { 1330 | if ( doneTables ) 1331 | return; 1332 | doneTables = true; 1333 | #if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) 1334 | //Exponential volume table, same as the real adlib 1335 | for ( int i = 0; i < 256; i++ ) { 1336 | //Save them in reverse 1337 | ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); 1338 | ExpTable[i] += 1024; //or remove the -1 oh well :) 1339 | //Preshift to the left once so the final volume can shift to the right 1340 | ExpTable[i] *= 2; 1341 | } 1342 | #endif 1343 | #if ( DBOPL_WAVE == WAVE_HANDLER ) 1344 | //Add 0.5 for the trunc rounding of the integer cast 1345 | //Do a PI sinetable instead of the original 0.5 PI 1346 | for ( int i = 0; i < 512; i++ ) { 1347 | SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); 1348 | } 1349 | #endif 1350 | #if ( DBOPL_WAVE == WAVE_TABLEMUL ) 1351 | //Multiplication based tables 1352 | for ( int i = 0; i < 384; i++ ) { 1353 | int s = i * 8; 1354 | //TODO maybe keep some of the precision errors of the original table? 1355 | double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); 1356 | MulTable[i] = (Bit16u)(val); 1357 | } 1358 | 1359 | //Sine Wave Base 1360 | for ( int i = 0; i < 512; i++ ) { 1361 | WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); 1362 | WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; 1363 | } 1364 | //Exponential wave 1365 | for ( int i = 0; i < 256; i++ ) { 1366 | WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); 1367 | WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; 1368 | } 1369 | #endif 1370 | #if ( DBOPL_WAVE == WAVE_TABLELOG ) 1371 | //Sine Wave Base 1372 | for ( int i = 0; i < 512; i++ ) { 1373 | WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); 1374 | WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; 1375 | } 1376 | //Exponential wave 1377 | for ( int i = 0; i < 256; i++ ) { 1378 | WaveTable[ 0x700 + i ] = i * 8; 1379 | WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; 1380 | } 1381 | #endif 1382 | 1383 | // | |//\\|____|WAV7|//__|/\ |____|/\/\| 1384 | // |\\//| | |WAV7| | \/| | | 1385 | // |06 |0126|27 |7 |3 |4 |4 5 |5 | 1386 | 1387 | #if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) 1388 | for ( int i = 0; i < 256; i++ ) { 1389 | //Fill silence gaps 1390 | WaveTable[ 0x400 + i ] = WaveTable[0]; 1391 | WaveTable[ 0x500 + i ] = WaveTable[0]; 1392 | WaveTable[ 0x900 + i ] = WaveTable[0]; 1393 | WaveTable[ 0xc00 + i ] = WaveTable[0]; 1394 | WaveTable[ 0xd00 + i ] = WaveTable[0]; 1395 | //Replicate sines in other pieces 1396 | WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; 1397 | //double speed sines 1398 | WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; 1399 | WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; 1400 | WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; 1401 | WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; 1402 | } 1403 | #endif 1404 | 1405 | //Create the ksl table 1406 | for ( int oct = 0; oct < 8; oct++ ) { 1407 | int base = oct * 8; 1408 | for ( int i = 0; i < 16; i++ ) { 1409 | int val = base - KslCreateTable[i]; 1410 | if ( val < 0 ) 1411 | val = 0; 1412 | //*4 for the final range to match attenuation range 1413 | KslTable[ oct * 16 + i ] = val * 4; 1414 | } 1415 | } 1416 | //Create the Tremolo table, just increase and decrease a triangle wave 1417 | for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) { 1418 | Bit8u val = i << ENV_EXTRA; 1419 | TremoloTable[i] = val; 1420 | TremoloTable[TREMOLO_TABLE - 1 - i] = val; 1421 | } 1422 | //Create a table with offsets of the channels from the start of the chip 1423 | DBOPL::Chip* chip = 0; 1424 | for ( Bitu i = 0; i < 32; i++ ) { 1425 | Bitu index = i & 0xf; 1426 | if ( index >= 9 ) { 1427 | ChanOffsetTable[i] = 0; 1428 | continue; 1429 | } 1430 | //Make sure the four op channels follow eachother 1431 | if ( index < 6 ) { 1432 | index = (index % 3) * 2 + ( index / 3 ); 1433 | } 1434 | //Add back the bits for highest ones 1435 | if ( i >= 16 ) 1436 | index += 9; 1437 | Bitu blah = reinterpret_cast( &(chip->chan[ index ]) ); 1438 | ChanOffsetTable[i] = blah; 1439 | } 1440 | //Same for operators 1441 | for ( Bitu i = 0; i < 64; i++ ) { 1442 | if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { 1443 | OpOffsetTable[i] = 0; 1444 | continue; 1445 | } 1446 | Bitu chNum = (i / 8) * 3 + (i % 8) % 3; 1447 | //Make sure we use 16 and up for the 2nd range to match the chanoffset gap 1448 | if ( chNum >= 12 ) 1449 | chNum += 16 - 12; 1450 | Bitu opNum = ( i % 8 ) / 3; 1451 | DBOPL::Channel* chan = 0; 1452 | Bitu blah = reinterpret_cast( &(chan->op[opNum]) ); 1453 | OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah; 1454 | } 1455 | #if 0 1456 | //Stupid checks if table's are correct 1457 | for ( Bitu i = 0; i < 18; i++ ) { 1458 | Bit32u find = (Bit16u)( &(chip->chan[ i ]) ); 1459 | for ( Bitu c = 0; c < 32; c++ ) { 1460 | if ( ChanOffsetTable[c] == find ) { 1461 | find = 0; 1462 | break; 1463 | } 1464 | } 1465 | if ( find ) { 1466 | find = find; 1467 | } 1468 | } 1469 | for ( Bitu i = 0; i < 36; i++ ) { 1470 | Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) ); 1471 | for ( Bitu c = 0; c < 64; c++ ) { 1472 | if ( OpOffsetTable[c] == find ) { 1473 | find = 0; 1474 | break; 1475 | } 1476 | } 1477 | if ( find ) { 1478 | find = find; 1479 | } 1480 | } 1481 | #endif 1482 | } 1483 | 1484 | Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) { 1485 | return chip.WriteAddr( port, val ); 1486 | 1487 | } 1488 | void Handler::WriteReg( Bit32u addr, Bit8u val ) { 1489 | chip.WriteReg( addr, val ); 1490 | } 1491 | 1492 | void Handler::Generate( MixerChannel* chan, Bitu samples ) { 1493 | Bit32s buffer[ 512 * 2 ]; 1494 | if ( GCC_UNLIKELY(samples > 512) ) 1495 | samples = 512; 1496 | if ( !chip.opl3Active ) { 1497 | chip.GenerateBlock2( samples, buffer ); 1498 | chan->AddSamples_m32( samples, buffer ); 1499 | } else { 1500 | chip.GenerateBlock3( samples, buffer ); 1501 | chan->AddSamples_s32( samples, buffer ); 1502 | } 1503 | } 1504 | 1505 | void Handler::Init( Bitu rate ) { 1506 | InitTables(); 1507 | chip.Setup( rate ); 1508 | } 1509 | 1510 | 1511 | }; //Namespace DBOPL 1512 | --------------------------------------------------------------------------------