diff options
Diffstat (limited to 'apps/codecs/libgme/nes_fme7_apu.c')
-rw-r--r-- | apps/codecs/libgme/nes_fme7_apu.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/apps/codecs/libgme/nes_fme7_apu.c b/apps/codecs/libgme/nes_fme7_apu.c new file mode 100644 index 0000000000..cb5ed93be5 --- /dev/null +++ b/apps/codecs/libgme/nes_fme7_apu.c | |||
@@ -0,0 +1,135 @@ | |||
1 | // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "nes_fme7_apu.h" | ||
4 | |||
5 | #include <string.h> | ||
6 | |||
7 | /* Copyright (C) 2003-2006 Shay Green. This module is free software; you | ||
8 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
9 | General Public License as published by the Free Software Foundation; either | ||
10 | version 2.1 of the License, or (at your option) any later version. This | ||
11 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
13 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
14 | details. You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this module; if not, write to the Free Software Foundation, | ||
16 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
17 | |||
18 | #include "blargg_source.h" | ||
19 | |||
20 | void Fme7_init( struct Nes_Fme7_Apu* this ) | ||
21 | { | ||
22 | Synth_init( &this->synth ); | ||
23 | |||
24 | Fme7_output( this, NULL ); | ||
25 | Fme7_volume( this, 1.0 ); | ||
26 | Fme7_reset( this ); | ||
27 | } | ||
28 | |||
29 | void Fme7_reset( struct Nes_Fme7_Apu* this ) | ||
30 | { | ||
31 | this->last_time = 0; | ||
32 | |||
33 | int i; | ||
34 | for ( i = 0; i < fme7_osc_count; i++ ) | ||
35 | this->oscs [i].last_amp = 0; | ||
36 | |||
37 | this->latch = 0; | ||
38 | memset( this->regs, 0, sizeof this->regs); | ||
39 | memset( this->phases, 0, sizeof this->phases ); | ||
40 | memset( this->delays, 0, sizeof this->delays ); | ||
41 | } | ||
42 | |||
43 | static unsigned char const amp_table [16] ICONST_ATTR = | ||
44 | { | ||
45 | #define ENTRY( n ) (unsigned char) (n * amp_range + 0.5) | ||
46 | ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156), | ||
47 | ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624), | ||
48 | ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498), | ||
49 | ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000) | ||
50 | #undef ENTRY | ||
51 | }; | ||
52 | |||
53 | void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time ) | ||
54 | { | ||
55 | require( end_time >= this->last_time ); | ||
56 | |||
57 | int index; | ||
58 | for ( index = 0; index < fme7_osc_count; index++ ) | ||
59 | { | ||
60 | int mode = this->regs [7] >> index; | ||
61 | int vol_mode = this->regs [010 + index]; | ||
62 | int volume = amp_table [vol_mode & 0x0F]; | ||
63 | |||
64 | struct Blip_Buffer* const osc_output = this->oscs [index].output; | ||
65 | if ( !osc_output ) | ||
66 | continue; | ||
67 | /* osc_output->set_modified(); */ | ||
68 | Blip_set_modified( osc_output ); | ||
69 | |||
70 | // check for unsupported mode | ||
71 | #ifndef NDEBUG | ||
72 | if ( (mode & 011) <= 001 && vol_mode & 0x1F ) | ||
73 | debug_printf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", | ||
74 | mode, vol_mode & 0x1F ); | ||
75 | #endif | ||
76 | |||
77 | if ( (mode & 001) | (vol_mode & 0x10) ) | ||
78 | volume = 0; // noise and envelope aren't supported | ||
79 | |||
80 | // period | ||
81 | int const period_factor = 16; | ||
82 | unsigned period = (this->regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor + | ||
83 | this->regs [index * 2] * period_factor; | ||
84 | if ( period < 50 ) // around 22 kHz | ||
85 | { | ||
86 | volume = 0; | ||
87 | if ( !period ) // on my AY-3-8910A, period doesn't have extra one added | ||
88 | period = period_factor; | ||
89 | } | ||
90 | |||
91 | // current amplitude | ||
92 | int amp = volume; | ||
93 | if ( !this->phases [index] ) | ||
94 | amp = 0; | ||
95 | { | ||
96 | int delta = amp - this->oscs [index].last_amp; | ||
97 | if ( delta ) | ||
98 | { | ||
99 | this->oscs [index].last_amp = amp; | ||
100 | Synth_offset( &this->synth, this->last_time, delta, osc_output ); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | blip_time_t time = this->last_time + this->delays [index]; | ||
105 | if ( time < end_time ) | ||
106 | { | ||
107 | int delta = amp * 2 - volume; | ||
108 | if ( volume ) | ||
109 | { | ||
110 | do | ||
111 | { | ||
112 | delta = -delta; | ||
113 | Synth_offset_inline( &this->synth, time, delta, osc_output ); | ||
114 | time += period; | ||
115 | } | ||
116 | while ( time < end_time ); | ||
117 | |||
118 | this->oscs [index].last_amp = (delta + volume) >> 1; | ||
119 | this->phases [index] = (delta > 0); | ||
120 | } | ||
121 | else | ||
122 | { | ||
123 | // maintain phase when silent | ||
124 | int count = (end_time - time + period - 1) / period; | ||
125 | this->phases [index] ^= count & 1; | ||
126 | time += (blargg_long) count * period; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | this->delays [index] = time - end_time; | ||
131 | } | ||
132 | |||
133 | this->last_time = end_time; | ||
134 | } | ||
135 | |||