commit 44703a1
zzo38
·
2022-11-04 22:47:55 +0000 UTC
parent fd4753c
Implement audio (it might not be working quite right yet)
2 files changed,
+164,
-2
M
main.c
+31,
-0
1@@ -30,6 +30,9 @@
2 -Z
3 Use UTC instead of local time. (Has no effect in combination with -T.)
4
5+-a #
6+ Set audio. See the below section about audio for details.
7+
8 -d
9 Enable some debugging features. (Some will work even without this.)
10
11@@ -72,6 +75,9 @@
12 F1
13 Advance one frame, even if paused.
14
15+F2
16+ Mute/unmute audio.
17+
18 F3
19 Toggle the mouse mode. When the mouse mode is disabled, the mouse device
20 does not work; the mouse vector is not called and the mouse coordinates
21@@ -103,6 +109,31 @@ work in this program; therefore, END is an alias for the joypad A button
22 together with letter keys, will enter control characters instead).
23
24
25+=== Audio ===
26+
27+The audio setting consists of a sequence of letters and numbers; each item
28+is one letter "a" to "z" followed by a number of any number of digits. (It
29+can optionally have - at first for negative numbers, although most options
30+cannot use negative numbers.)
31+
32+b = Buffer size.
33+
34+c = Number of output channels, which can be 1 (mono) or 2 (stereo).
35+
36+e = Envelope speed. If not specified, then it is fifteenths of seconds.
37+
38+m = If set to 1, then audio is initially muted.
39+
40+n = Transpose amount.
41+
42+r = Sample rate in hertz. Normally, this setting is also the sample rate
43+for middle C.
44+
45+v = Output volume from 0 to 273.
46+
47+(Currently there is not the setting of interpolation, filter, etc.)
48+
49+
50 === Extension device ===
51
52 Device fifteen is the extension device, which is defined as:
M
main.c
+133,
-2
1@@ -41,6 +41,13 @@ typedef struct {
2 Uint8 mode;
3 } UxnFile;
4
5+typedef struct {
6+ Uint32 envtime,phase,freq;
7+ Uint32 length,end,pos;
8+ Uint32 env[4];
9+ Uint8 vol[2];
10+} UxnAudio;
11+
12 static SDL_Color colors[64]={
13 // 0
14 {0xFF,0xFF,0xFF,0},
15@@ -123,6 +130,11 @@ static const Uint8 blend[4][16]={
16 {2,3,1,2,2,3,1,2,2,3,1,2,2,3,1,2},
17 };
18
19+static const Uint32 notes[12]={
20+ 0x20000000,0x21e71f26,0x23eb3588,0x260dfc14,0x285145f3,0x2ab70212,
21+ 0x2d413ccd,0x2ff221af,0x32cbfd4a,0x35d13f33,0x39047c0f,0x3c686fce,
22+};
23+
24 static Uint8 mem[MEMSIZE];
25 static Stack ds,rs;
26 static Device device[16];
27@@ -154,7 +166,11 @@ static Uint8 layers;
28 static Uint8 palet=7;
29 static Uint8 bicycle=0;
30
31+static Sint32*audio_option;
32+static SDL_AudioSpec audiospec;
33+
34 static void debug(int i) {
35+ int j;
36 if(i) fprintf(stderr,"Debug(%02X)\n",i);
37 fprintf(stderr,"ds:");
38 for(i=0;i<ds.p;i++) fprintf(stderr," %02X",ds.d[i]);
39@@ -162,6 +178,13 @@ static void debug(int i) {
40 fprintf(stderr,"rs:");
41 for(i=0;i<rs.p;i++) fprintf(stderr," %02X",rs.d[i]);
42 fputc('\n',stderr);
43+ fprintf(stderr," 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. A. B. C. D. E. F.\n");
44+ for(i=0;i<16;i++) {
45+ fprintf(stderr,"%1X:",i);
46+ for(j=0;j<16;j++) fprintf(stderr," %02X",device[i].d[j]);
47+ if(device[i].aux) fprintf(stderr," *");
48+ fputc('\n',stderr);
49+ }
50 }
51
52 static void uxnerr(int n,int pc) {
53@@ -628,6 +651,35 @@ static void screen_out(Device*dev,Uint8 id) {
54 }
55 }
56
57+static void audio_out(Device*dev,Uint8 id) {
58+ UxnAudio*a;
59+ Uint32 s,n;
60+ if(id!=15) return;
61+ SDL_LockAudio();
62+ a=dev->aux;
63+ a->env[0]=(dev->d[8]>>4)*audio_option['e'-'a'];
64+ a->env[1]=(dev->d[8]&15)*audio_option['e'-'a'];
65+ a->env[2]=(dev->d[9]>>4)*audio_option['e'-'a'];
66+ a->env[3]=(dev->d[9]&15)*audio_option['e'-'a'];
67+ a->length=n=GET16(dev->d+10);
68+ a->pos=s=GET16(dev->d+12);
69+ a->end=s+n;
70+ if(dev->d[15]&0x80) a->length=0;
71+ a->envtime=0;
72+ a->phase=0;
73+ s=(dev->d[15]&0x7F)+audio_option['n'-'a'];
74+ s=notes[s%12]>>(10-s/12);
75+ if(n<256) s=(n*(unsigned long long)s)>>8;
76+ a->freq=s;
77+ if(audiospec.channels==1) {
78+ a->vol[0]=a->vol[1]=((dev->d[14]&15)+(dev->d[14]>>4))/2;
79+ } else {
80+ a->vol[0]=dev->d[14]>>4;
81+ a->vol[1]=dev->d[14]&15;
82+ }
83+ SDL_UnlockAudio();
84+}
85+
86 static Uint32 timer_callback(Uint32 interval) {
87 if(!timer_expired) {
88 SDL_Event e;
89@@ -639,6 +691,54 @@ static Uint32 timer_callback(Uint32 interval) {
90 return interval;
91 }
92
93+static Uint32 envelope(const UxnAudio*a) {
94+ // Returns a number from 0 to 64.
95+ Uint32 t=a->envtime;
96+ if(!(a->env[0]|a->env[1]|a->env[2]|a->env[3])) return 64;
97+ if(t<a->env[0]) return ((64*t)/a->env[0])?:1;
98+ t-=a->env[0];
99+ if(t<a->env[1]) return 64-((32*t)/a->env[1]);
100+ t-=a->env[1];
101+ if(t<a->env[2]) return 32;
102+ t-=a->env[2];
103+ if(t<a->env[3]) return 32-((32*t)/a->env[3]);
104+ return 0;
105+}
106+
107+static void audio_callback(void*userdata,Uint8*stream,int len) {
108+ Uint8 c=audiospec.channels;
109+ Uint32 vol=audio_option['m'-'a']?0:audio_option['v'-'a'];
110+ Sint16*buf=(void*)stream;
111+ UxnAudio*a;
112+ int d,n;
113+ Uint32 o0,o1,e;
114+ len>>=1;
115+ for(n=0;n<len;n+=c) {
116+ o0=o1=0;
117+ for(d=3;d<7;d++) {
118+ a=device[d].aux;
119+ if(a->pos>=a->end && !a->length) continue;
120+ e=envelope(a);
121+ if(!e) {
122+ a->pos=a->length=a->end=0;
123+ continue;
124+ }
125+ a->envtime++;
126+ e*=(Sint8)(mem[a->pos]^0x80);
127+ a->phase+=a->freq;
128+ if(a->phase>=0x1000000) {
129+ a->pos+=a->phase>>24;
130+ a->phase&=0xFFFFFF;
131+ while(a->pos>=a->end && a->length) a->pos-=a->length;
132+ }
133+ o0+=e*a->vol[0];
134+ o1+=e*a->vol[1];
135+ }
136+ buf[n]=(o0*vol)>>12;
137+ if(c==2) buf[n+1]=(o1*vol)>>12;
138+ }
139+}
140+
141 static void redraw(void) {
142 int a,r,u,v,x,y;
143 Uint8*p;
144@@ -694,6 +794,7 @@ static int run_screen(void) {
145 case SDLK_LEFT: i=0x40; break;
146 case SDLK_RIGHT: i=0x80; break;
147 case SDLK_F1: run(GET16(device[2].d)); timer_expired=0; break;
148+ case SDLK_F2: if(audio_option) audio_option['m'-'a']^=1; break;
149 case SDLK_F3: use_mouse^=1; set_cursor(); break;
150 case SDLK_F4: layers=(layers+1)%3; redraw(); break;
151 case SDLK_F5: redraw(); if(SDL_GetModState()&KMOD_RSHIFT) debug(-1); break;
152@@ -763,6 +864,31 @@ static void set_datetime(char*s) {
153 }
154 }
155
156+static void set_audio(char*s) {
157+ int i;
158+ audio_option=calloc(sizeof(Sint32),26);
159+ if(!audio_option) err(1,"Allocation failed");
160+ audio_option['b'-'a']=2048;
161+ audio_option['c'-'a']=2;
162+ audio_option['r'-'a']=44100;
163+ audio_option['v'-'a']=273;
164+ while(*s && *s!='.') {
165+ if(*s<'a' && *s>'z') errx(1,"Invalid audio option");
166+ audio_option[*s-'a']=strtol(s+1,&s,10);
167+ }
168+ audiospec.freq=audio_option['f'-'a'];
169+ audiospec.channels=audio_option['c'-'a'];
170+ audiospec.samples=audio_option['b'-'a'];
171+ audiospec.format=AUDIO_S16SYS;
172+ audiospec.callback=audio_callback;
173+ for(i=3;i<7;i++) {
174+ device[i].aux=calloc(1,sizeof(UxnAudio));
175+ if(!device[i].aux) err(1,"Allocation failed");
176+ device[i].out=audio_out;
177+ }
178+ if(!audio_option['e'-'a']) audio_option['e'-'a']=audio_option['r'-'a']/15;
179+}
180+
181 int main(int argc,char**argv) {
182 int i,j;
183 for(i=0;i<16;i++) {
184@@ -774,7 +900,7 @@ int main(int argc,char**argv) {
185 device[10].aux=&uxnfile0;
186 device[11].aux=&uxnfile1;
187 device[12].in=datetime_in;
188- while((i=getopt(argc,argv,"+ABDFNQT:YZdh:inp:qt:w:xyz:"))>0) switch(i) {
189+ while((i=getopt(argc,argv,"+ABDFNQT:YZa:dh:inp:qt:w:xyz:"))>0) switch(i) {
190 case 'B': bicycle=1; device[0].in=default_in; break;
191 case 'D': scrflags|=SDL_DOUBLEBUF; break;
192 case 'F': scrflags|=SDL_FULLSCREEN; break;
193@@ -783,6 +909,7 @@ int main(int argc,char**argv) {
194 case 'T': device[12].in=default_in; set_datetime(optarg); break;
195 case 'Y': allow_write=1; device[10].out=device[11].out=files_out; device[10].in=device[11].in=files_in; break;
196 case 'Z': use_utc=1; break;
197+ case 'a': set_audio(optarg); break;
198 case 'd': use_debug=1; break;
199 case 'h': default_height=strtol(optarg,0,10); break;
200 case 'i': hide_cursor=1; break;
201@@ -802,7 +929,7 @@ int main(int argc,char**argv) {
202 if(use_screen) {
203 if(zoom<1) errx(1,"Zoom out of range");
204 if(default_height<1 || default_width<1) errx(1,"Screen size out of range");
205- if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) errx(1,"Cannot initialize SDL: %s",SDL_GetError());
206+ if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|(audio_option?SDL_INIT_AUDIO:0))) errx(1,"Cannot initialize SDL: %s",SDL_GetError());
207 atexit(SDL_Quit);
208 set_screen_mode(default_width,default_height);
209 SDL_EnableUNICODE(1);
210@@ -810,6 +937,10 @@ int main(int argc,char**argv) {
211 PUT16(device[2].d+2,default_width);
212 PUT16(device[2].d+4,default_height);
213 if(timer_rate) SDL_SetTimer(timer_rate,timer_callback);
214+ if(audio_option) {
215+ if(SDL_OpenAudio(&audiospec,0)) errx(1,"Cannot initialize SDL audio: %s",SDL_GetError());
216+ SDL_PauseAudio(0);
217+ }
218 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
219 SDL_WM_SetCaption(rom_name,rom_name);
220 set_cursor();