openMSX
VLM5030.cc
Go to the documentation of this file.
1 /*
2  vlm5030.c
3 
4  VLM5030 emulator
5 
6  Written by Tatsuyuki Satoh
7  Based on TMS5220 simulator (tms5220.c)
8 
9  note:
10  memory read cycle(==sampling rate) = 122.9u(440clock)
11  interpolator (LC8109 = 2.5ms) = 20 * samples(125us)
12  frame time (20ms) = 4 * interpolator
13  9bit DAC is composed of 5bit Physical and 3bitPWM.
14 
15  todo:
16  Noise Generator circuit without 'rand()' function.
17 
18 ----------- command format (Analytical result) ----------
19 
20 1)end of speech (8bit)
21 :00000011:
22 
23 2)silent some frame (8bit)
24 :????SS01:
25 
26 SS : number of silent frames
27  00 = 2 frame
28  01 = 4 frame
29  10 = 6 frame
30  11 = 8 frame
31 
32 3)-speech frame (48bit)
33 function: 6th : 5th : 4th : 3rd : 2nd : 1st :
34 end : --- : --- : --- : --- : --- :00000011:
35 silent : --- : --- : --- : --- : --- :0000SS01:
36 speech :11111122:22233334:44455566:67778889:99AAAEEE:EEPPPPP0:
37 
38 EEEEE : energy : volume 0=off,0x1f=max
39 PPPPP : pitch : 0=noize , 1=fast,0x1f=slow
40 111111 : K1 : 48=off
41 22222 : K2 : 0=off,1=+min,0x0f=+max,0x10=off,0x11=+max,0x1f=-min
42  : 16 == special function??
43 3333 : K3 : 0=off,1=+min,0x07=+max,0x08=-max,0x0f=-min
44 4444 : K4 :
45 555 : K5 : 0=off,1=+min,0x03=+max,0x04=-max,0x07=-min
46 666 : K6 :
47 777 : K7 :
48 888 : K8 :
49 999 : K9 :
50 AAA : K10 :
51 
52  ---------- chirp table information ----------
53 
54 DAC PWM cycle == 88system clock , (11clock x 8 pattern) = 40.6KHz
55 one chirp == 5 x PWM cycle == 440systemclock(8,136Hz)
56 
57 chirp 0 : volume 10- 8 : with filter
58 chirp 1 : volume 8- 6 : with filter
59 chirp 2 : volume 6- 4 : with filter
60 chirp 3 : volume 4 : no filter ??
61 chirp 4- 5: volume 4- 2 : with filter
62 chirp 6-11: volume 2- 0 : with filter
63 chirp 12-..: vokume 0 : silent
64 
65  ---------- digial output information ----------
66  when ME pin = high , some status output to A0..15 pins
67 
68  A0..8 : DAC output value (abs)
69  A9 : DAC sign flag , L=minus,H=Plus
70  A10 : energy reload flag (pitch pulse)
71  A11..15 : unknown
72 
73  [DAC output value(signed 6bit)] = A9 ? A0..8 : -(A0..8)
74 
75 */
76 
77 #include "VLM5030.hh"
78 #include "ResampledSoundDevice.hh"
79 #include "Rom.hh"
80 #include "DeviceConfig.hh"
81 #include "XMLElement.hh"
82 #include "FileOperations.hh"
83 #include "serialize.hh"
84 #include "memory.hh"
85 #include <cstring>
86 #include <cstdlib>
87 
88 namespace openmsx {
89 
90 class VLM5030::Impl final : public ResampledSoundDevice
91 {
92 public:
93  Impl(const std::string& name, const std::string& desc,
94  const std::string& romFilename, const DeviceConfig& config);
95  ~Impl();
96 
97  void reset();
98  void writeData(byte data);
99  void writeControl(byte data, EmuTime::param time);
100  bool getBSY(EmuTime::param time);
101 
102  template<typename Archive>
103  void serialize(Archive& ar, unsigned version);
104 
105 private:
106  void setRST(bool pin);
107  void setVCU(bool pin);
108  void setST (bool pin);
109 
110  // SoundDevice
111  void generateChannels(int** bufs, unsigned num) override;
112 
113  void setupParameter(byte param);
114  int getBits(unsigned sbit, unsigned bits);
115  int parseFrame();
116 
117  std::unique_ptr<Rom> rom;
118  int address_mask;
119 
120  // state of option paramter
121  int frame_size;
122  int pitch_offset;
123 
124  // these contain data describing the current and previous voice frames
125  // these are all used to contain the current state of the sound generation
126  unsigned current_energy;
127  unsigned current_pitch;
128  int current_k[10];
129  int x[10];
130 
131  word address;
132  word vcu_addr_h;
133 
134  signed_word old_k[10];
135  signed_word new_k[10];
136  signed_word target_k[10];
137  word old_energy;
138  word new_energy;
139  word target_energy;
140  byte old_pitch;
141  byte new_pitch;
142  byte target_pitch;
143 
144  byte interp_step;
145  byte interp_count; // number of interp periods
146  byte sample_count; // sample number within interp
147  byte pitch_count;
148 
149  byte latch_data;
150  byte parameter;
151  byte phase;
152  bool pin_BSY;
153  bool pin_ST;
154  bool pin_VCU;
155  bool pin_RST;
156 };
157 
158 // interpolator per frame
159 static const int FR_SIZE = 4;
160 // samples per interpolator
161 static const int IP_SIZE_SLOWER = 240 / FR_SIZE;
162 static const int IP_SIZE_SLOW = 200 / FR_SIZE;
163 static const int IP_SIZE_NORMAL = 160 / FR_SIZE;
164 static const int IP_SIZE_FAST = 120 / FR_SIZE;
165 static const int IP_SIZE_FASTER = 80 / FR_SIZE;
166 
167 // phase value
168 enum {
176 };
177 
178 // speed parameter
179 // SPC SPB SPA
180 // 1 0 1 more slow (05h) : 42ms (150%) : 60sample
181 // 1 1 x slow (06h,07h) : 34ms (125%) : 50sample
182 // x 0 0 normal (00h,04h) : 25.6ms (100%) : 40samplme
183 // 0 0 1 fast (01h) : 20.2ms (75%) : 30sample
184 // 0 1 x more fast (02h,03h) : 12.2ms (50%) : 20sample
185 static const int VLM5030_speed_table[8] =
186 {
187  IP_SIZE_NORMAL,
188  IP_SIZE_FAST,
189  IP_SIZE_FASTER,
190  IP_SIZE_FASTER,
191  IP_SIZE_NORMAL,
192  IP_SIZE_SLOWER,
193  IP_SIZE_SLOW,
194  IP_SIZE_SLOW
195 };
196 
197 // ROM Tables
198 
199 // This is the energy lookup table
200 
201 // sampled from real chip
202 static word energytable[0x20] =
203 {
204  0, 2, 4, 6, 10, 12, 14, 18, // 0-7
205  22, 26, 30, 34, 38, 44, 48, 54, // 8-15
206  62, 68, 76, 84, 94,102,114,124, // 16-23
207  136,150,164,178,196,214,232,254 // 24-31
208 };
209 
210 // This is the pitch lookup table
211 static const byte pitchtable [0x20] =
212 {
213  1, // 0 : random mode
214  22, // 1 : start=22
215  23, 24, 25, 26, 27, 28, 29, 30, // 2- 9 : 1step
216  32, 34, 36, 38, 40, 42, 44, 46, // 10-17 : 2step
217  50, 54, 58, 62, 66, 70, 74, 78, // 18-25 : 4step
218  86, 94, 102,110,118,126 // 26-31 : 8step
219 };
220 
221 static const signed_word K1_table[] = {
222  -24898, -25672, -26446, -27091, -27736, -28252, -28768, -29155,
223  -29542, -29929, -30316, -30574, -30832, -30961, -31219, -31348,
224  -31606, -31735, -31864, -31864, -31993, -32122, -32122, -32251,
225  -32251, -32380, -32380, -32380, -32509, -32509, -32509, -32509,
226  24898, 23995, 22963, 21931, 20770, 19480, 18061, 16642,
227  15093, 13416, 11610, 9804, 7998, 6063, 3999, 1935,
228  0, -1935, -3999, -6063, -7998, -9804, -11610, -13416,
229  -15093, -16642, -18061, -19480, -20770, -21931, -22963, -23995
230 };
231 static const signed_word K2_table[] = {
232  0, -3096, -6321, -9417, -12513, -15351, -18061, -20770,
233  -23092, -25285, -27220, -28897, -30187, -31348, -32122, -32638,
234  0, 32638, 32122, 31348, 30187, 28897, 27220, 25285,
235  23092, 20770, 18061, 15351, 12513, 9417, 6321, 3096
236 };
237 static const signed_word K3_table[] = {
238  0, -3999, -8127, -12255, -16384, -20383, -24511, -28639,
239  32638, 28639, 24511, 20383, 16254, 12255, 8127, 3999
240 };
241 static const signed_word K5_table[] = {
242  0, -8127, -16384, -24511, 32638, 24511, 16254, 8127
243 };
244 
245 int VLM5030::Impl::getBits(unsigned sbit, unsigned bits)
246 {
247  unsigned offset = address + (sbit / 8);
248  unsigned data = (*rom)[(offset + 0) & address_mask] +
249  (*rom)[(offset + 1) & address_mask] * 256;
250  data >>= (sbit & 7);
251  data &= (0xFF >> (8 - bits));
252  return data;
253 }
254 
255 // get next frame
256 int VLM5030::Impl::parseFrame()
257 {
258  // remember previous frame
259  old_energy = new_energy;
260  old_pitch = new_pitch;
261  for (int i = 0; i <= 9; ++i) {
262  old_k[i] = new_k[i];
263  }
264  // command byte check
265  byte cmd = (*rom)[address & address_mask];
266  if (cmd & 0x01) {
267  // extend frame
268  new_energy = new_pitch = 0;
269  for (int i = 0; i <= 9; ++i) {
270  new_k[i] = 0;
271  }
272  ++address;
273  if (cmd & 0x02) {
274  // end of speech
275  return 0;
276  } else {
277  // silent frame
278  int nums = ((cmd >> 2) + 1) * 2;
279  return nums * FR_SIZE;
280  }
281  }
282  // pitch
283  new_pitch = (pitchtable[getBits(1, 5)] + pitch_offset) & 0xff;
284  // energy
285  new_energy = energytable[getBits(6, 5)];
286 
287  // 10 K's
288  new_k[9] = K5_table[getBits(11, 3)];
289  new_k[8] = K5_table[getBits(14, 3)];
290  new_k[7] = K5_table[getBits(17, 3)];
291  new_k[6] = K5_table[getBits(20, 3)];
292  new_k[5] = K5_table[getBits(23, 3)];
293  new_k[4] = K5_table[getBits(26, 3)];
294  new_k[3] = K3_table[getBits(29, 4)];
295  new_k[2] = K3_table[getBits(33, 4)];
296  new_k[1] = K2_table[getBits(37, 5)];
297  new_k[0] = K1_table[getBits(42, 6)];
298 
299  address += 6;
300  return FR_SIZE;
301 }
302 
303 // decode and buffering data
304 void VLM5030::Impl::generateChannels(int** bufs, unsigned length)
305 {
306  // Single channel device: replace content of bufs[0] (not add to it).
307  if (phase == PH_IDLE) {
308  bufs[0] = nullptr;
309  return;
310  }
311 
312  int buf_count = 0;
313 
314  // running
315  if (phase == PH_RUN || phase == PH_STOP) {
316  // playing speech
317  while (length > 0) {
318  int current_val;
319  // check new interpolator or new frame
320  if (sample_count == 0) {
321  if (phase == PH_STOP) {
322  phase = PH_END;
323  sample_count = 1;
324  goto phase_stop; // continue to end phase
325  }
326  sample_count = frame_size;
327  // interpolator changes
328  if (interp_count == 0) {
329  // change to new frame
330  interp_count = parseFrame(); // with change phase
331  if (interp_count == 0 ) {
332  // end mark found
333  interp_count = FR_SIZE;
334  sample_count = frame_size; // end -> stop time
335  phase = PH_STOP;
336  }
337  // Set old target as new start of frame
338  current_energy = old_energy;
339  current_pitch = old_pitch;
340  for (int i = 0; i <= 9; ++i) {
341  current_k[i] = old_k[i];
342  }
343  // is this a zero energy frame?
344  if (current_energy == 0) {
345  target_energy = 0;
346  target_pitch = current_pitch;
347  for (int i = 0; i <= 9; ++i) {
348  target_k[i] = current_k[i];
349  }
350  } else {
351  // normal frame
352  target_energy = new_energy;
353  target_pitch = new_pitch;
354  for (int i = 0; i <= 9; ++i) {
355  target_k[i] = new_k[i];
356  }
357  }
358  }
359  // next interpolator
360  // Update values based on step values 25%, 50%, 75%, 100%
361  interp_count -= interp_step;
362  // 3,2,1,0 -> 1,2,3,4
363  int interp_effect = FR_SIZE - (interp_count % FR_SIZE);
364  current_energy = old_energy + (target_energy - old_energy) * interp_effect / FR_SIZE;
365  if (old_pitch > 1) {
366  current_pitch = old_pitch + (target_pitch - old_pitch) * interp_effect / FR_SIZE;
367  }
368  for (int i = 0; i <= 9; ++i)
369  current_k[i] = old_k[i] + (target_k[i] - old_k[i]) * interp_effect / FR_SIZE;
370  }
371  // calcrate digital filter
372  if (old_energy == 0) {
373  // generate silent samples here
374  current_val = 0x00;
375  } else if (old_pitch <= 1) {
376  // generate unvoiced samples here
377  current_val = (rand() & 1) ? int(current_energy)
378  : -int(current_energy);
379  } else {
380  // generate voiced samples here
381  current_val = (pitch_count == 0) ? current_energy : 0;
382  }
383 
384  // Lattice filter here
385  int u[11];
386  u[10] = current_val;
387  for (int i = 9; i >= 0; --i) {
388  u[i] = u[i + 1] - ((current_k[i] * x[i]) / 32768);
389  }
390  for (int i = 9; i >= 1; --i) {
391  x[i] = x[i - 1] + ((current_k[i - 1] * u[i - 1]) / 32768);
392  }
393  x[0] = u[0];
394 
395  // clipping, buffering
396  if (u[0] > 511) {
397  bufs[0][buf_count] = 511 << 6;
398  } else if (u[0] < -511) {
399  bufs[0][buf_count] = -511 << 6;
400  } else {
401  bufs[0][buf_count] = (u[0] << 6);
402  }
403  ++buf_count;
404  --sample_count;
405  ++pitch_count;
406  if (pitch_count >= current_pitch) {
407  pitch_count = 0;
408  }
409  --length;
410  }
411  // return;
412  }
413 phase_stop:
414  switch (phase) {
415  case PH_SETUP:
416  if (sample_count <= length) {
417  sample_count = 0;
418  // pin_BSY = true;
419  phase = PH_WAIT;
420  } else {
421  sample_count -= length;
422  }
423  break;
424  case PH_END:
425  if (sample_count <= length) {
426  sample_count = 0;
427  pin_BSY = false;
428  phase = PH_IDLE;
429  } else {
430  sample_count -= length;
431  }
432  }
433  // silent buffering
434  while (length > 0) {
435  bufs[0][buf_count++] = 0;
436  --length;
437  }
438 }
439 
440 // setup parameteroption when RST=H
441 void VLM5030::Impl::setupParameter(byte param)
442 {
443  // latch parameter value
444  parameter = param;
445 
446  // bit 0,1 : 4800bps / 9600bps , interporator step
447  if (param & 2) { // bit 1 = 1 , 9600bps
448  interp_step = 4; // 9600bps : no interporator
449  } else if (param & 1) { // bit1 = 0 & bit0 = 1 , 4800bps
450  interp_step = 2; // 4800bps : 2 interporator
451  } else { // bit1 = bit0 = 0 : 2400bps
452  interp_step = 1; // 2400bps : 4 interporator
453  }
454 
455  // bit 3,4,5 : speed (frame size)
456  frame_size = VLM5030_speed_table[(param >> 3) & 7];
457 
458  // bit 6,7 : low / high pitch
459  if (param & 0x80) { // bit7=1 , high pitch
460  pitch_offset = -8;
461  } else if (param & 0x40) { // bit6=1 , low pitch
462  pitch_offset = 8;
463  } else {
464  pitch_offset = 0;
465  }
466 }
467 
469 {
470  phase = PH_RESET;
471  address = 0;
472  vcu_addr_h = 0;
473  pin_BSY = false;
474 
475  old_energy = old_pitch = 0;
476  new_energy = new_pitch = 0;
477  current_energy = current_pitch = 0;
478  target_energy = target_pitch = 0;
479  memset(old_k, 0, sizeof(old_k));
480  memset(new_k, 0, sizeof(new_k));
481  memset(current_k, 0, sizeof(current_k));
482  memset(target_k, 0, sizeof(target_k));
483  interp_count = sample_count = pitch_count = 0;
484  memset(x, 0, sizeof(x));
485  // reset parameters
486  setupParameter(0x00);
487 }
488 
489 // get BSY pin level
491 {
492  updateStream(time);
493  return pin_BSY;
494 }
495 
496 // latch control data
498 {
499  latch_data = data;
500 }
501 
503 {
504  updateStream(time);
505  setRST((data & 0x01) != 0);
506  setVCU((data & 0x04) != 0);
507  setST ((data & 0x02) != 0);
508 }
509 
510 // set RST pin level : reset / set table address A8-A15
511 void VLM5030::Impl::setRST(bool pin)
512 {
513  if (pin_RST) {
514  if (!pin) { // H -> L : latch parameters
515  pin_RST = false;
516  setupParameter(latch_data);
517  }
518  } else {
519  if (pin) { // L -> H : reset chip
520  pin_RST = true;
521  if (pin_BSY) {
522  reset();
523  }
524  }
525  }
526 }
527 
528 // set VCU pin level : ?? unknown
529 void VLM5030::Impl::setVCU(bool pin)
530 {
531  // direct mode / indirect mode
532  pin_VCU = pin;
533 }
534 
535 // set ST pin level : set table address A0-A7 / start speech
536 void VLM5030::Impl::setST(bool pin)
537 {
538  if (pin_ST == pin) {
539  // pin level unchanged
540  return;
541  }
542  if (!pin) {
543  // H -> L
544  pin_ST = false;
545  if (pin_VCU) {
546  // direct access mode & address High
547  vcu_addr_h = (int(latch_data) << 8) + 0x01;
548  } else {
549  // check access mode
550  if (vcu_addr_h) {
551  // direct access mode
552  address = (vcu_addr_h & 0xff00) + latch_data;
553  vcu_addr_h = 0;
554  } else {
555  // indirect access mode
556  int table = (latch_data & 0xfe) + ((int(latch_data) & 1) << 8);
557  address = (((*rom)[(table + 0) & address_mask]) << 8) |
558  (*rom)[(table + 1) & address_mask];
559  }
560  // reset process status
561  sample_count = frame_size;
562  interp_count = FR_SIZE;
563  // clear filter
564  // start after 3 sampling cycle
565  phase = PH_RUN;
566  }
567  } else {
568  // L -> H
569  pin_ST = true;
570  // setup speech, BSY on after 30ms?
571  phase = PH_SETUP;
572  sample_count = 1; // wait time for busy on
573  pin_BSY = true;
574  }
575 }
576 
577 VLM5030::Impl::Impl(const std::string& name, const std::string& desc,
578  const std::string& romFilename, const DeviceConfig& config)
579  : ResampledSoundDevice(config.getMotherBoard(), name, desc, 1)
580 {
581  XMLElement voiceROMconfig(name);
582  voiceROMconfig.addAttribute("id", "name");
583  auto& romElement = voiceROMconfig.addChild("rom");
584  romElement.addChild( // load by sha1sum
585  "sha1", "4f36d139ee4baa7d5980f765de9895570ee05f40");
586  romElement.addChild( // load by predefined filename in software rom's dir
587  "filename", FileOperations::stripExtension(romFilename) + "_voice.rom");
588  romElement.addChild( // or hardcoded filename in ditto dir
589  "filename", "keyboardmaster/voice.rom");
590  rom = make_unique<Rom>(
591  name + " ROM", "rom", DeviceConfig(config, voiceROMconfig));
592 
593  // reset input pins
594  pin_RST = pin_ST = pin_VCU = false;
595  latch_data = 0;
596 
597  reset();
598  phase = PH_IDLE;
599 
600  address_mask = rom->getSize() - 1;
601 
602  const int CLOCK_FREQ = 3579545;
603  double input = CLOCK_FREQ / 440.0;
604  setInputRate(int(input + 0.5));
605 
606  registerSound(config);
607 }
608 
610 {
611  unregisterSound();
612 }
613 
614 template<typename Archive>
615 void VLM5030::Impl::serialize(Archive& ar, unsigned /*version*/)
616 {
617  ar.serialize("address_mask", address_mask);
618  ar.serialize("frame_size", frame_size);
619  ar.serialize("pitch_offset", pitch_offset);
620  ar.serialize("current_energy", current_energy);
621  ar.serialize("current_pitch", current_pitch);
622  ar.serialize("current_k", current_k);
623  ar.serialize("x", x);
624  ar.serialize("address", address);
625  ar.serialize("vcu_addr_h", vcu_addr_h);
626  ar.serialize("old_k", old_k);
627  ar.serialize("new_k", new_k);
628  ar.serialize("target_k", target_k);
629  ar.serialize("old_energy", old_energy);
630  ar.serialize("new_energy", new_energy);
631  ar.serialize("target_energy", target_energy);
632  ar.serialize("old_pitch", old_pitch);
633  ar.serialize("new_pitch", new_pitch);
634  ar.serialize("target_pitch", target_pitch);
635  ar.serialize("interp_step", interp_step);
636  ar.serialize("interp_count", interp_count);
637  ar.serialize("sample_count", sample_count);
638  ar.serialize("pitch_count", pitch_count);
639  ar.serialize("latch_data", latch_data);
640  ar.serialize("parameter", parameter);
641  ar.serialize("phase", phase);
642  ar.serialize("pin_BSY", pin_BSY);
643  ar.serialize("pin_ST", pin_ST);
644  ar.serialize("pin_VCU", pin_VCU);
645  ar.serialize("pin_RST", pin_RST);
646 }
647 
648 
649 // class VLM5030
650 
651 VLM5030::VLM5030(const std::string& name, const std::string& desc,
652  const std::string& romFilename, const DeviceConfig& config)
653  : pimpl(make_unique<Impl>(name, desc, romFilename, config))
654 {
655 }
656 
658 {
659 }
660 
662 {
663  pimpl->reset();
664 }
665 
667 {
668  pimpl->writeData(data);
669 }
670 
672 {
673  pimpl->writeControl(data, time);
674 }
675 
677 {
678  return pimpl->getBSY(time);
679 }
680 
681 template<typename Archive>
682 void VLM5030::serialize(Archive& ar, unsigned version)
683 {
684  pimpl->serialize(ar, version);
685 }
687 
688 } // namespace openmsx
signed char offset
Definition: CPUCore.cc:252
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:281
Impl(const std::string &name, const std::string &desc, const std::string &romFilename, const DeviceConfig &config)
Definition: VLM5030.cc:577
void serialize(Archive &ar, unsigned version)
Definition: VLM5030.cc:682
void writeControl(byte data, EmuTime::param time)
set RST / VCU / ST pins
Definition: VLM5030.cc:671
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
void writeData(byte data)
Definition: VLM5030.cc:497
bool getBSY(EmuTime::param time)
Definition: VLM5030.cc:490
void writeControl(byte data, EmuTime::param time)
Definition: VLM5030.cc:502
void writeData(byte data)
latch control data
Definition: VLM5030.cc:666
void setInputRate(unsigned sampleRate)
Definition: SoundDevice.hh:79
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:7
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:38
string_ref stripExtension(string_ref path)
Returns the path without extension.
VLM5030(const std::string &name, const std::string &desc, const std::string &romFilename, const DeviceConfig &config)
Definition: VLM5030.cc:651
bool getBSY(EmuTime::param time)
get BSY pin level
Definition: VLM5030.cc:676
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:802
void addAttribute(string_ref name, string_ref value)
Definition: XMLElement.cc:57
XMLElement & addChild(string_ref name)
Definition: XMLElement.cc:29
void serialize(Archive &ar, unsigned version)
Definition: VLM5030.cc:615
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
short signed_word
16 bit signed integer
Definition: openmsx.hh:36
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:81