/[qemu]/qemu/hw/adlib.c
ViewVC logotype

Diff of /qemu/hw/adlib.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.2 by bellard, Tue Nov 9 23:08:30 2004 UTC revision 1.3 by bellard, Sun Oct 30 18:58:22 2005 UTC
# Line 1  Line 1 
1  /*  /*
2   * QEMU Adlib emulation   * QEMU Proxy for OPL2/3 emulation by MAME team
3   *   *
4   * Copyright (c) 2004 Vassili Karpov (malc)   * Copyright (c) 2004-2005 Vassili Karpov (malc)
5   *   *
6   * Permission is hereby granted, free of charge, to any person obtaining a copy   * Permission is hereby granted, free of charge, to any person obtaining a copy
7   * of this software and associated documentation files (the "Software"), to deal   * of this software and associated documentation files (the "Software"), to deal
8   * in the Software without restriction, including without limitation the rights   * in the Software without restriction, including without limitation the rights
# Line 21  Line 21 
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22   * THE SOFTWARE.   * THE SOFTWARE.
23   */   */
24    #include <assert.h>
25  #include "vl.h"  #include "vl.h"
26    
27    #define ADLIB_KILL_TIMERS 1
28    
29  #define dolog(...) AUD_log ("adlib", __VA_ARGS__)  #define dolog(...) AUD_log ("adlib", __VA_ARGS__)
30  #ifdef DEBUG  #ifdef DEBUG
31  #define ldebug(...) dolog (__VA_ARGS__)  #define ldebug(...) dolog (__VA_ARGS__)
# Line 30  Line 33 
33  #define ldebug(...)  #define ldebug(...)
34  #endif  #endif
35    
36  #ifdef USE_YMF262  #ifdef HAS_YMF262
 #define HAS_YMF262 1  
37  #include "ymf262.h"  #include "ymf262.h"
38  void YMF262UpdateOneQEMU(int which, INT16 *dst, int length);  void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
39  #define SHIFT 2  #define SHIFT 2
40  #else  #else
41  #include "fmopl.h"  #include "fmopl.h"
42  #define SHIFT 1  #define SHIFT 1
43  #endif  #endif
44    
 #ifdef _WIN32  
 #include <windows.h>  
 #define small_delay() Sleep (1)  
 #else  
 #define small_delay() usleep (1)  
 #endif  
   
45  #define IO_READ_PROTO(name) \  #define IO_READ_PROTO(name) \
46      uint32_t name (void *opaque, uint32_t nport)      uint32_t name (void *opaque, uint32_t nport)
47  #define IO_WRITE_PROTO(name) \  #define IO_WRITE_PROTO(name) \
# Line 58  static struct { Line 53  static struct {
53  } conf = {0x220, 44100};  } conf = {0x220, 44100};
54    
55  typedef struct {  typedef struct {
56        int ticking[2];
57      int enabled;      int enabled;
58      int active;      int active;
     int cparam;  
     int64_t ticks;  
59      int bufpos;      int bufpos;
60    #ifdef DEBUG
61        int64_t exp[2];
62    #endif
63      int16_t *mixbuf;      int16_t *mixbuf;
64      double interval;      uint64_t dexp[2];
65      QEMUTimer *ts, *opl_ts;      SWVoiceOut *voice;
66      SWVoice *voice;      int left, pos, samples;
67      int left, pos, samples, bytes_per_second, old_free;      QEMUAudioTimeStamp ats;
68      int refcount;  #ifndef HAS_YMF262
 #ifndef USE_YMF262  
69      FM_OPL *opl;      FM_OPL *opl;
70  #endif  #endif
71  } AdlibState;  } AdlibState;
72    
73  static AdlibState adlib;  static AdlibState adlib;
74    
75    static void adlib_stop_opl_timer (AdlibState *s, size_t n)
76    {
77    #ifdef HAS_YMF262
78        YMF262TimerOver (0, n);
79    #else
80        OPLTimerOver (s->opl, n);
81    #endif
82        s->ticking[n] = 0;
83    }
84    
85    static void adlib_kill_timers (AdlibState *s)
86    {
87        size_t i;
88    
89        for (i = 0; i < 2; ++i) {
90            if (s->ticking[i]) {
91                uint64_t delta;
92    
93                delta = AUD_time_stamp_get_elapsed_usec_out (s->voice, &s->ats);
94                ldebug (
95                    "delta = %f dexp = %f expired => %d\n",
96                    delta / 1000000.0,
97                    s->dexp[i] / 1000000.0,
98                    delta >= s->dexp[i]
99                    );
100                if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
101                    adlib_stop_opl_timer (s, i);
102                    AUD_init_time_stamp_out (s->voice, &s->ats);
103                }
104            }
105        }
106    }
107    
108  static IO_WRITE_PROTO(adlib_write)  static IO_WRITE_PROTO(adlib_write)
109  {  {
110      AdlibState *s = opaque;      AdlibState *s = opaque;
111      int a = nport & 3;      int a = nport & 3;
112      int status;      int status;
113    
     s->ticks = qemu_get_clock (vm_clock);  
114      s->active = 1;      s->active = 1;
115      AUD_enable (s->voice, 1);      AUD_set_active_out (s->voice, 1);
116    
117  #ifdef USE_YMF262      adlib_kill_timers (s);
118    
119    #ifdef HAS_YMF262
120      status = YMF262Write (0, a, val);      status = YMF262Write (0, a, val);
121  #else  #else
122      status = OPLWrite (s->opl, a, val);      status = OPLWrite (s->opl, a, val);
# Line 99  static IO_READ_PROTO(adlib_read) Line 129  static IO_READ_PROTO(adlib_read)
129      uint8_t data;      uint8_t data;
130      int a = nport & 3;      int a = nport & 3;
131    
132  #ifdef USE_YMF262      adlib_kill_timers (s);
133      (void) s;  
134    #ifdef HAS_YMF262
135      data = YMF262Read (0, a);      data = YMF262Read (0, a);
136  #else  #else
137      data = OPLRead (s->opl, a);      data = OPLRead (s->opl, a);
# Line 108  static IO_READ_PROTO(adlib_read) Line 139  static IO_READ_PROTO(adlib_read)
139      return data;      return data;
140  }  }
141    
142  static void OPL_timer (void *opaque)  static void timer_handler (int c, double interval_Sec)
143  {  {
144      AdlibState *s = opaque;      AdlibState *s = &adlib;
145  #ifdef USE_YMF262      unsigned n = c & 1;
146      YMF262TimerOver (s->cparam >> 1, s->cparam & 1);  #ifdef DEBUG
147  #else      double interval;
     OPLTimerOver (s->opl, s->cparam);  
148  #endif  #endif
     qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);  
 }  
149    
 static void YMF262TimerHandler (int c, double interval_Sec)  
 {  
     AdlibState *s = &adlib;  
150      if (interval_Sec == 0.0) {      if (interval_Sec == 0.0) {
151          qemu_del_timer (s->opl_ts);          s->ticking[n] = 0;
152          return;          return;
153      }      }
154      s->cparam = c;  
155      s->interval = ticks_per_sec * interval_Sec;      s->ticking[n] = 1;
156      qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);  #ifdef DEBUG
157      small_delay ();      interval = ticks_per_sec * interval_Sec;
158        exp = qemu_get_clock (vm_clock) + interval;
159        s->exp[n] = exp;
160    #endif
161    
162        s->dexp[n] = interval_Sec * 1000000.0;
163        AUD_init_time_stamp_out (s->voice, &s->ats);
164  }  }
165    
166  static int write_audio (AdlibState *s, int samples)  static int write_audio (AdlibState *s, int samples)
167  {  {
168      int net = 0;      int net = 0;
169      int ss = samples;      int pos = s->pos;
170    
171      while (samples) {      while (samples) {
172          int nbytes = samples << SHIFT;          int nbytes, wbytes, wsampl;
173          int wbytes = AUD_write (s->voice,  
174                                  s->mixbuf + (s->pos << (SHIFT - 1)),          nbytes = samples << SHIFT;
175                                  nbytes);          wbytes = AUD_write (
176          int wsampl = wbytes >> SHIFT;              s->voice,
177          samples -= wsampl;              s->mixbuf + (pos << (SHIFT - 1)),
178          s->pos = (s->pos + wsampl) % s->samples;              nbytes
179          net += wsampl;              );
180          if (!wbytes)  
181            if (wbytes) {
182                wsampl = wbytes >> SHIFT;
183    
184                samples -= wsampl;
185                pos = (pos + wsampl) % s->samples;
186    
187                net += wsampl;
188            }
189            else {
190              break;              break;
191            }
192      }      }
193      if (net > ss) {  
         dolog ("WARNING: net > ss\n");  
     }  
194      return net;      return net;
195  }  }
196    
197  static void timer (void *opaque)  static void adlib_callback (void *opaque, int free)
198  {  {
199      AdlibState *s = opaque;      AdlibState *s = opaque;
200      int elapsed, samples, net = 0;      int samples, net = 0, to_play, written;
201    
202      if (s->refcount)      samples = free >> SHIFT;
203          dolog ("refcount=%d\n", s->refcount);      if (!(s->active && s->enabled) || !samples) {
204            return;
205        }
206    
207      s->refcount += 1;      to_play = audio_MIN (s->left, samples);
208      if (!(s->active && s->enabled))      while (to_play) {
209          goto reset;          written = write_audio (s, to_play);
210    
211      AUD_run ();          if (written) {
212                s->left -= written;
213      while (s->left) {              samples -= written;
214          int written = write_audio (s, s->left);              to_play -= written;
215          net += written;              s->pos = (s->pos + written) % s->samples;
216          if (!written)          }
217              goto reset2;          else {
218          s->left -= written;              return;
219      }          }
220      s->pos = 0;      }
   
     elapsed = AUD_calc_elapsed (s->voice);  
     if (!elapsed)  
         goto reset2;  
   
     /* elapsed = AUD_get_free (s->voice); */  
     samples = elapsed >> SHIFT;  
     if (!samples)  
         goto reset2;  
221    
222      samples = audio_MIN (samples, s->samples - s->pos);      samples = audio_MIN (samples, s->samples - s->pos);
223      if (s->left)      if (!samples) {
224          dolog ("left=%d samples=%d elapsed=%d free=%d\n",          return;
225                 s->left, samples, elapsed, AUD_get_free (s->voice));      }
   
     if (!samples)  
         goto reset2;  
226    
227  #ifdef USE_YMF262  #ifdef HAS_YMF262
228      YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);      YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
229  #else  #else
230      YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);      YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
231  #endif  #endif
232    
233      while (samples) {      while (samples) {
234          int written = write_audio (s, samples);          written = write_audio (s, samples);
235          net += written;  
236          if (!written)          if (written) {
237              break;              net += written;
238          samples -= written;              samples -= written;
239                s->pos = (s->pos + written) % s->samples;
240            }
241            else {
242                s->left = samples;
243                return;
244            }
245      }      }
     if (!samples)  
         s->pos = 0;  
     s->left = samples;  
   
 reset2:  
     AUD_adjust (s->voice, net << SHIFT);  
 reset:  
     qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);  
     s->refcount -= 1;  
246  }  }
247    
248  static void Adlib_fini (AdlibState *s)  static void Adlib_fini (AdlibState *s)
249  {  {
250  #ifdef USE_YMF262  #ifdef HAS_YMF262
251      YMF262Shutdown ();      YMF262Shutdown ();
252  #else  #else
253      if (s->opl) {      if (s->opl) {
# Line 229  static void Adlib_fini (AdlibState *s) Line 256  static void Adlib_fini (AdlibState *s)
256      }      }
257  #endif  #endif
258    
259      if (s->opl_ts)      if (s->mixbuf) {
260          qemu_free_timer (s->opl_ts);          qemu_free (s->mixbuf);
261        }
     if (s->ts)  
         qemu_free_timer (s->ts);  
   
 #define maybe_free(p) if (p) qemu_free (p)  
     maybe_free (s->mixbuf);  
 #undef maybe_free  
262    
263      s->active = 0;      s->active = 0;
264      s->enabled = 0;      s->enabled = 0;
# Line 247  void Adlib_init (void) Line 268  void Adlib_init (void)
268  {  {
269      AdlibState *s = &adlib;      AdlibState *s = &adlib;
270    
271      memset (s, 0, sizeof (*s));  #ifdef HAS_YMF262
   
 #ifdef USE_YMF262  
272      if (YMF262Init (1, 14318180, conf.freq)) {      if (YMF262Init (1, 14318180, conf.freq)) {
273          dolog ("YMF262Init %d failed\n", conf.freq);          dolog ("YMF262Init %d failed\n", conf.freq);
274          return;          return;
275      }      }
276      else {      else {
277          YMF262SetTimerHandler (0, YMF262TimerHandler, 0);          YMF262SetTimerHandler (0, timer_handler, 0);
278          s->enabled = 1;          s->enabled = 1;
279      }      }
280  #else  #else
# Line 265  void Adlib_init (void) Line 284  void Adlib_init (void)
284          return;          return;
285      }      }
286      else {      else {
287          OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);          OPLSetTimerHandler (s->opl, timer_handler, 0);
288          s->enabled = 1;          s->enabled = 1;
289      }      }
290  #endif  #endif
291    
292      s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s);      s->voice = AUD_open_out (
293      if (!s->opl_ts) {          s->voice,
294          dolog ("Can not get timer for adlib emulation\n");          "adlib",
295          Adlib_fini (s);          s,
296          return;          adlib_callback,
297      }          conf.freq,
298            SHIFT,
299      s->ts = qemu_new_timer (vm_clock, timer, s);          AUD_FMT_S16
300      if (!s->opl_ts) {          );
         dolog ("Can not get timer for adlib emulation\n");  
         Adlib_fini (s);  
         return;  
     }  
   
     s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);  
301      if (!s->voice) {      if (!s->voice) {
302          Adlib_fini (s);          Adlib_fini (s);
303          return;          return;
304      }      }
305    
306      s->bytes_per_second = conf.freq << SHIFT;      s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
     s->samples = AUD_get_buffer_size (s->voice) >> SHIFT;  
307      s->mixbuf = qemu_mallocz (s->samples << SHIFT);      s->mixbuf = qemu_mallocz (s->samples << SHIFT);
308    
309      if (!s->mixbuf) {      if (!s->mixbuf) {
# Line 300  void Adlib_init (void) Line 312  void Adlib_init (void)
312          Adlib_fini (s);          Adlib_fini (s);
313          return;          return;
314      }      }
315    
316      register_ioport_read (0x388, 4, 1, adlib_read, s);      register_ioport_read (0x388, 4, 1, adlib_read, s);
317      register_ioport_write (0x388, 4, 1, adlib_write, s);      register_ioport_write (0x388, 4, 1, adlib_write, s);
318    
# Line 308  void Adlib_init (void) Line 321  void Adlib_init (void)
321    
322      register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);      register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
323      register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);      register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
   
     qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);  
324  }  }

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.3

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26