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();