cc5f0fa11f10358647572429c57af68ea7859cb6
[cyclone68000.git] / Pico / Psnd.cpp
1 \r
2 // This file is part of the PicoDrive Megadrive Emulator\r
3 \r
4 // This code is licensed under the GNU General Public License version 2.0 and the MAME License.\r
5 // You can choose the license that has the most advantages for you.\r
6 \r
7 // SVN repository can be found at http://code.google.com/p/cyclone68000/\r
8 \r
9 #include "PicoInt.h"\r
10 \r
11 #ifdef MSOUND\r
12 extern "C"\r
13 {\r
14 int YM2612UpdateReq(int) { return 0; }\r
15 void *errorlog=NULL;\r
16 }\r
17 #endif\r
18 \r
19 int PsndRate=0,PsndLen=0;\r
20 short *PsndOut=NULL;\r
21 \r
22 // An operator is a single sine wave generator\r
23 struct Operator\r
24 {\r
25   unsigned short angle; // 0-0xffff\r
26   unsigned short freq; // Converted frequency\r
27   unsigned char key;\r
28   unsigned char state; // 1=attack, 2=decay, 3=sustain, 4=release\r
29   int vol;\r
30   int delta; // Change in volume per sample\r
31   int limit; // Next volume limit\r
32 };\r
33 \r
34 struct Channel\r
35 {\r
36   struct Operator op[4]; // 4 operators for the channel\r
37   unsigned short note; // Written to 0xa4 and 0xa0\r
38 };\r
39 \r
40 static struct Channel Chan[8];\r
41 \r
42 unsigned char PicoSreg[0x200];\r
43 \r
44 static int WriteReg(int side,int a,int d)\r
45 {\r
46   struct Channel *pc=NULL;\r
47 \r
48   PicoSreg[(side<<8)|a]=(unsigned char)d;\r
49 \r
50   if (a==0x28)\r
51   {\r
52     pc=Chan+(d&7);\r
53     // Key On/Key Off\r
54     if (d&0xf0) pc->op[0].state=1; // Attack\r
55     else        pc->op[0].state=4; // Release\r
56 \r
57     return 0;\r
58   }\r
59 \r
60   // Find channel:\r
61   pc=Chan+(a&3); if (side) pc+=4;\r
62 \r
63   if ((a&0xf0)==0xa0)\r
64   {\r
65     int n=0,f=0,mult=2455;\r
66 \r
67     if (PsndRate>0) mult=44100*2455/PsndRate;\r
68 \r
69     if (a&4) pc->note =(unsigned short)(d<<8);\r
70     else     pc->note|=d;\r
71 \r
72     // Convert to an actual frequency:\r
73     n=pc->note; f=(n&0x7ff)<<((n>>11)&7);\r
74 \r
75     pc->op[0].freq=(unsigned short)((f*mult)>>16);\r
76     return 0;\r
77   }\r
78 \r
79   return 0;\r
80 }\r
81 \r
82 int PsndReset()\r
83 {\r
84   int i=0;\r
85   memset(&Chan,0,sizeof(Chan));\r
86   memset(PicoSreg,0,sizeof(PicoSreg));\r
87 \r
88 // Change Sine wave into a square wave\r
89   for (i=0x00; i<0x080; i++) Sine[i]= 0x2000;\r
90   for (i=0x80; i<0x100; i++) Sine[i]=-0x2000;\r
91 \r
92   return 0;\r
93 }\r
94 \r
95 int PsndFm(int a,int d)\r
96 {\r
97   int side=0;\r
98 \r
99 #ifdef MSOUND\r
100   YM2612Write(0,a&3,(unsigned char)d);\r
101 #endif\r
102 \r
103   a&=3; side=a>>1; // Which side: channels 0-2 or 3-5\r
104 \r
105   if (a&1) WriteReg(side,Pico.s.fmsel[side],d); // Write register\r
106   else     Pico.s.fmsel[side]=(unsigned char)d; // Select register\r
107 \r
108   return 0;\r
109 }\r
110 \r
111 static void BlankSound(short *dest,int len)\r
112 {\r
113   short *end=NULL;\r
114 \r
115   end=dest+(len<<1);\r
116 \r
117   // Init sound to silence:\r
118   do { *dest=0; dest++; } while (dest<end);\r
119 }\r
120 \r
121 // Add to output and clip:\r
122 static inline void AddClip(short *targ,int d)\r
123 {\r
124   int total=*targ+d;\r
125   if ((total+0x8000)&0xffff0000)\r
126   {\r
127     if (total>0) total=0x7fff; else total=-0x8000;\r
128   }\r
129   *targ=(short)total;\r
130 }\r
131 \r
132 static void OpNewState(int c)\r
133 {\r
134   struct Operator *op=Chan[c].op;\r
135   int off=0;\r
136 \r
137   off=((c&4)<<6)|(c&3);\r
138 \r
139   switch (op->state)\r
140   {\r
141     case 1:\r
142     {\r
143       // Attack:\r
144       int ar=PicoSreg[0x50|off];\r
145       ar&=0x1f; if (ar) ar+=0x10;\r
146       op->delta=ar<<7; op->limit=0x1000000; break;\r
147     }\r
148     case 2:\r
149     {\r
150       // Decay:\r
151       int d1r=PicoSreg[0x60|off];\r
152       d1r&=0x1f; if (d1r) d1r+=0x10;\r
153       op->delta=-(d1r<<5); op->limit=0;\r
154     }\r
155     break;\r
156     case 3:\r
157     {\r
158       // Sustain:\r
159       int d2r=0,rr=0;\r
160       \r
161       d2r=PicoSreg[0x70|off];\r
162       d2r&=0x1f; if (d2r) d2r+=0x10;\r
163       rr =PicoSreg[0x80|off];\r
164       rr>>=4;\r
165 \r
166       op->delta=-(d2r<<5); op->limit=rr<<20;\r
167     }\r
168     break;\r
169     case 4:\r
170       // Release:\r
171       int rr=PicoSreg[0x80|off];\r
172       rr&=0x0f; rr<<=1; rr+=0x10;\r
173       op->delta=-(rr<<5); op->limit=0;\r
174     break;\r
175   }\r
176 }\r
177 \r
178 // Merely adding this code in seems to bugger up the N-Gage???\r
179 static void UpdateOp(int c)\r
180 {\r
181   struct Operator *op=Chan[c].op;\r
182 \r
183   op->angle=(unsigned short)(op->angle+op->freq);\r
184   op->vol+=op->delta;\r
185 \r
186   switch (op->state)\r
187   {\r
188     case 1:\r
189       if (op->vol>=op->limit) { op->vol=op->limit; op->state++; OpNewState(c); }\r
190     break;\r
191     case 2: case 3: // Decay/Sustain\r
192       if (op->vol< op->limit) { op->vol=op->limit; op->state++; OpNewState(c); }\r
193     break;\r
194     case 4:\r
195       if (op->vol< op->limit) { op->vol=op->limit; }\r
196     break;\r
197   }\r
198 }\r
199 \r
200 static void AddChannel(int c,short *dest,int len)\r
201 {\r
202   struct Channel *pc=Chan+c;\r
203   struct Operator *op=pc->op;\r
204   short *end=NULL;\r
205 \r
206   // Work out volume delta for this operator:\r
207   OpNewState(c);\r
208 \r
209   end=dest+len;\r
210   do\r
211   {\r
212     int d=0;\r
213     d=Sine[(op->angle>>8)&0xff]>>2;\r
214 \r
215     d*=(op->vol>>8); d>>=16;\r
216 \r
217     // Add to output:\r
218     AddClip(dest,d);\r
219     UpdateOp(c);\r
220 \r
221     dest++;\r
222   }\r
223   while (dest<end);\r
224 }\r
225 \r
226 int PsndRender()\r
227 {\r
228   int i=0;\r
229 \r
230   if (PsndOut==NULL) return 1; // Not rendering sound\r
231 \r
232 #ifdef MSOUND\r
233   if (PicoOpt&1)\r
234   {\r
235     short *wave[2]={NULL,NULL};\r
236     wave[0]=PsndOut;\r
237     wave[1]=PsndOut+PsndLen;\r
238     YM2612UpdateOne(0,wave,PsndLen);\r
239   }\r
240 #endif\r
241 \r
242   if ((PicoOpt&1)==0)\r
243   {\r
244     BlankSound(PsndOut,PsndLen);\r
245     for (i=0;i<7;i++)\r
246     {\r
247       if (i!=3) AddChannel(i,PsndOut,PsndLen);\r
248     }\r
249   }\r
250 \r
251   return 0;\r
252 }\r