61 import javax.sound.sampled.AudioSystem;
62 import javax.sound.sampled.LineUnavailableException;
63 import javax.sound.sampled.SourceDataLine;
64
65 /**
66 * The software synthesizer class.
67 *
68 * @author Karl Helgason
69 */
70 public final class SoftSynthesizer implements AudioSynthesizer,
71 ReferenceCountingDevice {
72
73 protected static final class WeakAudioStream extends InputStream
74 {
75 private volatile AudioInputStream stream;
76 public SoftAudioPusher pusher = null;
77 public AudioInputStream jitter_stream = null;
78 public SourceDataLine sourceDataLine = null;
79 public volatile long silent_samples = 0;
80 private int framesize = 0;
81 private WeakReference<AudioInputStream> weak_stream_link;
82 private AudioFloatConverter converter;
83 private float[] silentbuffer = null;
84 private int samplesize;
85
86 public void setInputStream(AudioInputStream stream)
87 {
88 this.stream = stream;
89 }
90
91 public int available() throws IOException {
92 AudioInputStream local_stream = stream;
93 if(local_stream != null)
94 return local_stream.available();
95 return 0;
96 }
97
98 public int read() throws IOException {
99 byte[] b = new byte[1];
100 if (read(b) == -1)
101 return -1;
102 return b[0] & 0xFF;
103 }
104
105 public int read(byte[] b, int off, int len) throws IOException {
106 AudioInputStream local_stream = stream;
107 if(local_stream != null)
108 return local_stream.read(b, off, len);
109 else
110 {
111 int flen = len / samplesize;
112 if(silentbuffer == null || silentbuffer.length < flen)
113 silentbuffer = new float[flen];
114 converter.toByteArray(silentbuffer, flen, b, off);
115
116 silent_samples += (long)((len / framesize));
117
118 if(pusher != null)
119 if(weak_stream_link.get() == null)
120 {
121 Runnable runnable = new Runnable()
122 {
123 SoftAudioPusher _pusher = pusher;
124 AudioInputStream _jitter_stream = jitter_stream;
125 SourceDataLine _sourceDataLine = sourceDataLine;
126 public void run()
127 {
128 _pusher.stop();
129 if(_jitter_stream != null)
130 try {
131 _jitter_stream.close();
132 } catch (IOException e) {
133 e.printStackTrace();
134 }
135 if(_sourceDataLine != null)
136 _sourceDataLine.close();
137 }
138 };
139 pusher = null;
140 jitter_stream = null;
141 sourceDataLine = null;
142 new Thread(null, runnable, "Synthesizer",0,false).start();
143 }
144 return len;
145 }
146 }
147
148 public WeakAudioStream(AudioInputStream stream) {
149 this.stream = stream;
150 weak_stream_link = new WeakReference<AudioInputStream>(stream);
151 converter = AudioFloatConverter.getConverter(stream.getFormat());
152 samplesize = stream.getFormat().getFrameSize() / stream.getFormat().getChannels();
153 framesize = stream.getFormat().getFrameSize();
154 }
155
156 public AudioInputStream getAudioInputStream()
157 {
158 return new AudioInputStream(this, stream.getFormat(), AudioSystem.NOT_SPECIFIED);
159 }
160
161 public void close() throws IOException
162 {
163 AudioInputStream astream = weak_stream_link.get();
164 if(astream != null)
165 astream.close();
166 }
167 }
168
169 private static class Info extends MidiDevice.Info {
170 Info() {
171 super(INFO_NAME, INFO_VENDOR, INFO_DESCRIPTION, INFO_VERSION);
172 }
173 }
174
175 static final String INFO_NAME = "Gervill";
176 static final String INFO_VENDOR = "OpenJDK";
177 static final String INFO_DESCRIPTION = "Software MIDI Synthesizer";
178 static final String INFO_VERSION = "1.0";
179 static final MidiDevice.Info info = new Info();
180
216
217 private SoftAudioPusher pusher = null;
218 private AudioInputStream pusher_stream = null;
219
220 private float controlrate = 147f;
221
222 private boolean open = false;
223 private boolean implicitOpen = false;
224
225 private String resamplerType = "linear";
226 private SoftResampler resampler = new SoftLinearResampler();
227
228 private int number_of_midi_channels = 16;
229 private int maxpoly = 64;
230 private long latency = 200000; // 200 msec
231 private boolean jitter_correction = false;
232
233 private SoftMainMixer mainmixer;
234 private SoftVoice[] voices;
235
236 private Map<String, SoftTuning> tunings
237 = new HashMap<String, SoftTuning>();
238 private Map<String, SoftInstrument> inslist
239 = new HashMap<String, SoftInstrument>();
240 private Map<String, ModelInstrument> loadedlist
241 = new HashMap<String, ModelInstrument>();
242
243 private ArrayList<Receiver> recvslist = new ArrayList<Receiver>();
244
245 private void getBuffers(ModelInstrument instrument,
246 List<ModelByteBuffer> buffers) {
247 for (ModelPerformer performer : instrument.getPerformers()) {
248 if (performer.getOscillators() != null) {
249 for (ModelOscillator osc : performer.getOscillators()) {
250 if (osc instanceof ModelByteBufferWavetable) {
251 ModelByteBufferWavetable w = (ModelByteBufferWavetable)osc;
252 ModelByteBuffer buff = w.getBuffer();
253 if (buff != null)
254 buffers.add(buff);
255 buff = w.get8BitExtensionBuffer();
256 if (buff != null)
257 buffers.add(buff);
258 }
259 }
260 }
261 }
262 }
263
264 private boolean loadSamples(List<ModelInstrument> instruments) {
265 if (largemode)
266 return true;
267 List<ModelByteBuffer> buffers = new ArrayList<ModelByteBuffer>();
268 for (ModelInstrument instrument : instruments)
269 getBuffers(instrument, buffers);
270 try {
271 ModelByteBuffer.loadAll(buffers);
272 } catch (IOException e) {
273 return false;
274 }
275 return true;
276 }
277
278 private boolean loadInstruments(List<ModelInstrument> instruments) {
279 if (!isOpen())
280 return false;
281 if (!loadSamples(instruments))
282 return false;
283
284 synchronized (control_mutex) {
285 if (channels != null)
286 for (SoftChannel c : channels)
287 {
468 }
469
470 float getControlRate() {
471 return controlrate;
472 }
473
474 SoftVoice[] getVoices() {
475 return voices;
476 }
477
478 SoftTuning getTuning(Patch patch) {
479 String t_id = patchToString(patch);
480 SoftTuning tuning = tunings.get(t_id);
481 if (tuning == null) {
482 tuning = new SoftTuning(patch);
483 tunings.put(t_id, tuning);
484 }
485 return tuning;
486 }
487
488 public long getLatency() {
489 synchronized (control_mutex) {
490 return latency;
491 }
492 }
493
494 public AudioFormat getFormat() {
495 synchronized (control_mutex) {
496 return format;
497 }
498 }
499
500 public int getMaxPolyphony() {
501 synchronized (control_mutex) {
502 return maxpoly;
503 }
504 }
505
506 public MidiChannel[] getChannels() {
507
508 synchronized (control_mutex) {
509 // if (external_channels == null) => the synthesizer is not open,
510 // create 16 proxy channels
511 // otherwise external_channels has the same length as channels array
512 if (external_channels == null) {
513 external_channels = new SoftChannelProxy[16];
514 for (int i = 0; i < external_channels.length; i++)
515 external_channels[i] = new SoftChannelProxy();
516 }
517 MidiChannel[] ret;
518 if (isOpen())
519 ret = new MidiChannel[channels.length];
520 else
521 ret = new MidiChannel[16];
522 for (int i = 0; i < ret.length; i++)
523 ret[i] = external_channels[i];
524 return ret;
525 }
526 }
527
528 public VoiceStatus[] getVoiceStatus() {
529 if (!isOpen()) {
530 VoiceStatus[] tempVoiceStatusArray
531 = new VoiceStatus[getMaxPolyphony()];
532 for (int i = 0; i < tempVoiceStatusArray.length; i++) {
533 VoiceStatus b = new VoiceStatus();
534 b.active = false;
535 b.bank = 0;
536 b.channel = 0;
537 b.note = 0;
538 b.program = 0;
539 b.volume = 0;
540 tempVoiceStatusArray[i] = b;
541 }
542 return tempVoiceStatusArray;
543 }
544
545 synchronized (control_mutex) {
546 VoiceStatus[] tempVoiceStatusArray = new VoiceStatus[voices.length];
547 for (int i = 0; i < voices.length; i++) {
548 VoiceStatus a = voices[i];
549 VoiceStatus b = new VoiceStatus();
550 b.active = a.active;
551 b.bank = a.bank;
552 b.channel = a.channel;
553 b.note = a.note;
554 b.program = a.program;
555 b.volume = a.volume;
556 tempVoiceStatusArray[i] = b;
557 }
558 return tempVoiceStatusArray;
559 }
560 }
561
562 public boolean isSoundbankSupported(Soundbank soundbank) {
563 for (Instrument ins: soundbank.getInstruments())
564 if (!(ins instanceof ModelInstrument))
565 return false;
566 return true;
567 }
568
569 public boolean loadInstrument(Instrument instrument) {
570 if (instrument == null || (!(instrument instanceof ModelInstrument))) {
571 throw new IllegalArgumentException("Unsupported instrument: " +
572 instrument);
573 }
574 List<ModelInstrument> instruments = new ArrayList<ModelInstrument>();
575 instruments.add((ModelInstrument)instrument);
576 return loadInstruments(instruments);
577 }
578
579 public void unloadInstrument(Instrument instrument) {
580 if (instrument == null || (!(instrument instanceof ModelInstrument))) {
581 throw new IllegalArgumentException("Unsupported instrument: " +
582 instrument);
583 }
584 if (!isOpen())
585 return;
586
587 String pat = patchToString(instrument.getPatch());
588 synchronized (control_mutex) {
589 for (SoftChannel c: channels)
590 c.current_instrument = null;
591 inslist.remove(pat);
592 loadedlist.remove(pat);
593 for (int i = 0; i < channels.length; i++) {
594 channels[i].allSoundOff();
595 }
596 }
597 }
598
599 public boolean remapInstrument(Instrument from, Instrument to) {
600
601 if (from == null)
602 throw new NullPointerException();
603 if (to == null)
604 throw new NullPointerException();
605 if (!(from instanceof ModelInstrument)) {
606 throw new IllegalArgumentException("Unsupported instrument: " +
607 from.toString());
608 }
609 if (!(to instanceof ModelInstrument)) {
610 throw new IllegalArgumentException("Unsupported instrument: " +
611 to.toString());
612 }
613 if (!isOpen())
614 return false;
615
616 synchronized (control_mutex) {
617 if (!loadedlist.containsValue(to))
618 throw new IllegalArgumentException("Instrument to is not loaded.");
619 unloadInstrument(from);
620 ModelMappedInstrument mfrom = new ModelMappedInstrument(
621 (ModelInstrument)to, from.getPatch());
622 return loadInstrument(mfrom);
623 }
624 }
625
626 public Soundbank getDefaultSoundbank() {
627 synchronized (SoftSynthesizer.class) {
628 if (defaultSoundBank != null)
629 return defaultSoundBank;
630
631 List<PrivilegedAction<InputStream>> actions =
632 new ArrayList<PrivilegedAction<InputStream>>();
633
634 actions.add(new PrivilegedAction<InputStream>() {
635 public InputStream run() {
636 File javahome = new File(System.getProperties()
637 .getProperty("java.home"));
638 File libaudio = new File(new File(javahome, "lib"), "audio");
639 if (libaudio.isDirectory()) {
640 File foundfile = null;
641 File[] files = libaudio.listFiles();
642 if (files != null) {
643 for (int i = 0; i < files.length; i++) {
644 File file = files[i];
645 if (file.isFile()) {
646 String lname = file.getName().toLowerCase();
647 if (lname.endsWith(".sf2")
648 || lname.endsWith(".dls")) {
649 if (foundfile == null
650 || (file.length() > foundfile
651 .length())) {
652 foundfile = file;
653 }
654 }
655 }
656 }
657 }
658 if (foundfile != null) {
659 try {
660 return new FileInputStream(foundfile);
661 } catch (IOException e) {
662 }
663 }
664 }
665 return null;
666 }
667 });
668
669 actions.add(new PrivilegedAction<InputStream>() {
670 public InputStream run() {
671 if (System.getProperties().getProperty("os.name")
672 .startsWith("Linux")) {
673
674 File[] systemSoundFontsDir = new File[] {
675 /* Arch, Fedora, Mageia */
676 new File("/usr/share/soundfonts/"),
677 new File("/usr/local/share/soundfonts/"),
678 /* Debian, Gentoo, OpenSUSE, Ubuntu */
679 new File("/usr/share/sounds/sf2/"),
680 new File("/usr/local/share/sounds/sf2/"),
681 };
682
683 /*
684 * Look for a default.sf2
685 */
686 for (File systemSoundFontDir : systemSoundFontsDir) {
687 if (systemSoundFontDir.isDirectory()) {
688 File defaultSoundFont = new File(systemSoundFontDir, "default.sf2");
689 if (defaultSoundFont.isFile()) {
690 try {
691 return new FileInputStream(defaultSoundFont);
692 } catch (IOException e) {
693 // continue with lookup
694 }
695 }
696 }
697 }
698 }
699 return null;
700 }
701 });
702
703 actions.add(new PrivilegedAction<InputStream>() {
704 public InputStream run() {
705 if (System.getProperties().getProperty("os.name")
706 .startsWith("Windows")) {
707 File gm_dls = new File(System.getenv("SystemRoot")
708 + "\\system32\\drivers\\gm.dls");
709 if (gm_dls.isFile()) {
710 try {
711 return new FileInputStream(gm_dls);
712 } catch (IOException e) {
713 }
714 }
715 }
716 return null;
717 }
718 });
719
720 actions.add(new PrivilegedAction<InputStream>() {
721 public InputStream run() {
722 /*
723 * Try to load saved generated soundbank
724 */
725 File userhome = new File(System.getProperty("user.home"),
726 ".gervill");
727 File emg_soundbank_file = new File(userhome,
728 "soundbank-emg.sf2");
729 if (emg_soundbank_file.isFile()) {
730 try {
731 return new FileInputStream(emg_soundbank_file);
732 } catch (IOException e) {
733 }
734 }
735 return null;
736 }
737 });
738
739 for (PrivilegedAction<InputStream> action : actions) {
740 try {
781 if (emg_soundbank_file.isFile()) {
782 return null;
783 }
784 return new FileOutputStream(emg_soundbank_file);
785 } catch (final FileNotFoundException ignored) {
786 }
787 return null;
788 });
789 if (out != null) {
790 try {
791 ((SF2Soundbank) defaultSoundBank).save(out);
792 out.close();
793 } catch (final IOException ignored) {
794 }
795 }
796 }
797 }
798 return defaultSoundBank;
799 }
800
801 public Instrument[] getAvailableInstruments() {
802 Soundbank defsbk = getDefaultSoundbank();
803 if (defsbk == null)
804 return new Instrument[0];
805 Instrument[] inslist_array = defsbk.getInstruments();
806 Arrays.sort(inslist_array, new ModelInstrumentComparator());
807 return inslist_array;
808 }
809
810 public Instrument[] getLoadedInstruments() {
811 if (!isOpen())
812 return new Instrument[0];
813
814 synchronized (control_mutex) {
815 ModelInstrument[] inslist_array =
816 new ModelInstrument[loadedlist.values().size()];
817 loadedlist.values().toArray(inslist_array);
818 Arrays.sort(inslist_array, new ModelInstrumentComparator());
819 return inslist_array;
820 }
821 }
822
823 public boolean loadAllInstruments(Soundbank soundbank) {
824 List<ModelInstrument> instruments = new ArrayList<ModelInstrument>();
825 for (Instrument ins: soundbank.getInstruments()) {
826 if (ins == null || !(ins instanceof ModelInstrument)) {
827 throw new IllegalArgumentException(
828 "Unsupported instrument: " + ins);
829 }
830 instruments.add((ModelInstrument)ins);
831 }
832 return loadInstruments(instruments);
833 }
834
835 public void unloadAllInstruments(Soundbank soundbank) {
836 if (soundbank == null || !isSoundbankSupported(soundbank))
837 throw new IllegalArgumentException("Unsupported soundbank: " + soundbank);
838
839 if (!isOpen())
840 return;
841
842 for (Instrument ins: soundbank.getInstruments()) {
843 if (ins instanceof ModelInstrument) {
844 unloadInstrument(ins);
845 }
846 }
847 }
848
849 public boolean loadInstruments(Soundbank soundbank, Patch[] patchList) {
850 List<ModelInstrument> instruments = new ArrayList<ModelInstrument>();
851 for (Patch patch: patchList) {
852 Instrument ins = soundbank.getInstrument(patch);
853 if (ins == null || !(ins instanceof ModelInstrument)) {
854 throw new IllegalArgumentException(
855 "Unsupported instrument: " + ins);
856 }
857 instruments.add((ModelInstrument)ins);
858 }
859 return loadInstruments(instruments);
860 }
861
862 public void unloadInstruments(Soundbank soundbank, Patch[] patchList) {
863 if (soundbank == null || !isSoundbankSupported(soundbank))
864 throw new IllegalArgumentException("Unsupported soundbank: " + soundbank);
865
866 if (!isOpen())
867 return;
868
869 for (Patch pat: patchList) {
870 Instrument ins = soundbank.getInstrument(pat);
871 if (ins instanceof ModelInstrument) {
872 unloadInstrument(ins);
873 }
874 }
875 }
876
877 public MidiDevice.Info getDeviceInfo() {
878 return info;
879 }
880
881 private Properties getStoredProperties() {
882 return AccessController
883 .doPrivileged((PrivilegedAction<Properties>) () -> {
884 Properties p = new Properties();
885 String notePath = "/com/sun/media/sound/softsynthesizer";
886 try {
887 Preferences prefroot = Preferences.userRoot();
888 if (prefroot.nodeExists(notePath)) {
889 Preferences prefs = prefroot.node(notePath);
890 String[] prefs_keys = prefs.keys();
891 for (String prefs_key : prefs_keys) {
892 String val = prefs.get(prefs_key, null);
893 if (val != null) {
894 p.setProperty(prefs_key, val);
895 }
896 }
897 }
898 } catch (final BackingStoreException ignored) {
899 }
900 return p;
901 });
902 }
903
904 public AudioSynthesizerPropertyInfo[] getPropertyInfo(Map<String, Object> info) {
905 List<AudioSynthesizerPropertyInfo> list =
906 new ArrayList<AudioSynthesizerPropertyInfo>();
907
908 AudioSynthesizerPropertyInfo item;
909
910 // If info != null or synthesizer is closed
911 // we return how the synthesizer will be set on next open
912 // If info == null and synthesizer is open
913 // we return current synthesizer properties.
914 boolean o = info == null && open;
915
916 item = new AudioSynthesizerPropertyInfo("interpolation", o?resamplerType:"linear");
917 item.choices = new String[]{"linear", "linear1", "linear2", "cubic",
918 "lanczos", "sinc", "point"};
919 item.description = "Interpolation method";
920 list.add(item);
921
922 item = new AudioSynthesizerPropertyInfo("control rate", o?controlrate:147f);
923 item.description = "Control rate";
924 list.add(item);
925
926 item = new AudioSynthesizerPropertyInfo("format",
1041 Number n = (Number) v;
1042 if (c == Byte.class)
1043 item2.value = Byte.valueOf(n.byteValue());
1044 if (c == Short.class)
1045 item2.value = Short.valueOf(n.shortValue());
1046 if (c == Integer.class)
1047 item2.value = Integer.valueOf(n.intValue());
1048 if (c == Long.class)
1049 item2.value = Long.valueOf(n.longValue());
1050 if (c == Float.class)
1051 item2.value = Float.valueOf(n.floatValue());
1052 if (c == Double.class)
1053 item2.value = Double.valueOf(n.doubleValue());
1054 }
1055 }
1056 }
1057
1058 return items;
1059 }
1060
1061 public void open() throws MidiUnavailableException {
1062 if (isOpen()) {
1063 synchronized (control_mutex) {
1064 implicitOpen = false;
1065 }
1066 return;
1067 }
1068 open(null, null);
1069 }
1070
1071 public void open(SourceDataLine line, Map<String, Object> info) throws MidiUnavailableException {
1072 if (isOpen()) {
1073 synchronized (control_mutex) {
1074 implicitOpen = false;
1075 }
1076 return;
1077 }
1078 synchronized (control_mutex) {
1079 try {
1080 if (line != null) {
1081 // can throw IllegalArgumentException
1082 setFormat(line.getFormat());
1083 }
1084
1085 AudioInputStream ais = openStream(getFormat(), info);
1086
1087 weakstream = new WeakAudioStream(ais);
1088 ais = weakstream.getAudioInputStream();
1089
1090 if (line == null)
1145 if(weakstream != null)
1146 {
1147 weakstream.pusher = pusher;
1148 weakstream.sourceDataLine = sourceDataLine;
1149 }
1150
1151 } catch (final LineUnavailableException | SecurityException
1152 | IllegalArgumentException e) {
1153 if (isOpen()) {
1154 close();
1155 }
1156 // am: need MidiUnavailableException(Throwable) ctor!
1157 MidiUnavailableException ex = new MidiUnavailableException(
1158 "Can not open line");
1159 ex.initCause(e);
1160 throw ex;
1161 }
1162 }
1163 }
1164
1165 public AudioInputStream openStream(AudioFormat targetFormat,
1166 Map<String, Object> info) throws MidiUnavailableException {
1167
1168 if (isOpen())
1169 throw new MidiUnavailableException("Synthesizer is already open");
1170
1171 synchronized (control_mutex) {
1172
1173 gmmode = 0;
1174 voice_allocation_mode = 0;
1175
1176 processPropertyInfo(info);
1177
1178 open = true;
1179 implicitOpen = false;
1180
1181 if (targetFormat != null)
1182 setFormat(targetFormat);
1183
1184 if (load_default_soundbank)
1226 }
1227 }
1228
1229 for (int i = 0; i < channels.length; i++)
1230 external_channels[i].setChannel(channels[i]);
1231
1232 for (SoftVoice voice: getVoices())
1233 voice.resampler = resampler.openStreamer();
1234
1235 for (Receiver recv: getReceivers()) {
1236 SoftReceiver srecv = ((SoftReceiver)recv);
1237 srecv.open = open;
1238 srecv.mainmixer = mainmixer;
1239 srecv.midimessages = mainmixer.midimessages;
1240 }
1241
1242 return mainmixer.getInputStream();
1243 }
1244 }
1245
1246 public void close() {
1247
1248 if (!isOpen())
1249 return;
1250
1251 SoftAudioPusher pusher_to_be_closed = null;
1252 AudioInputStream pusher_stream_to_be_closed = null;
1253 synchronized (control_mutex) {
1254 if (pusher != null) {
1255 pusher_to_be_closed = pusher;
1256 pusher_stream_to_be_closed = pusher_stream;
1257 pusher = null;
1258 pusher_stream = null;
1259 }
1260 }
1261
1262 if (pusher_to_be_closed != null) {
1263 // Pusher must not be closed synchronized against control_mutex,
1264 // this may result in synchronized conflict between pusher
1265 // and current thread.
1284
1285 if (external_channels != null)
1286 for (int i = 0; i < external_channels.length; i++)
1287 external_channels[i].setChannel(null);
1288
1289 if (sourceDataLine != null) {
1290 sourceDataLine.close();
1291 sourceDataLine = null;
1292 }
1293
1294 inslist.clear();
1295 loadedlist.clear();
1296 tunings.clear();
1297
1298 while (recvslist.size() != 0)
1299 recvslist.get(recvslist.size() - 1).close();
1300
1301 }
1302 }
1303
1304 public boolean isOpen() {
1305 synchronized (control_mutex) {
1306 return open;
1307 }
1308 }
1309
1310 public long getMicrosecondPosition() {
1311
1312 if (!isOpen())
1313 return 0;
1314
1315 synchronized (control_mutex) {
1316 return mainmixer.getMicrosecondPosition();
1317 }
1318 }
1319
1320 public int getMaxReceivers() {
1321 return -1;
1322 }
1323
1324 public int getMaxTransmitters() {
1325 return 0;
1326 }
1327
1328 public Receiver getReceiver() throws MidiUnavailableException {
1329
1330 synchronized (control_mutex) {
1331 SoftReceiver receiver = new SoftReceiver(this);
1332 receiver.open = open;
1333 recvslist.add(receiver);
1334 return receiver;
1335 }
1336 }
1337
1338 public List<Receiver> getReceivers() {
1339
1340 synchronized (control_mutex) {
1341 ArrayList<Receiver> recvs = new ArrayList<Receiver>();
1342 recvs.addAll(recvslist);
1343 return recvs;
1344 }
1345 }
1346
1347 public Transmitter getTransmitter() throws MidiUnavailableException {
1348
1349 throw new MidiUnavailableException("No transmitter available");
1350 }
1351
1352 public List<Transmitter> getTransmitters() {
1353
1354 return new ArrayList<Transmitter>();
1355 }
1356
1357 public Receiver getReceiverReferenceCounting()
1358 throws MidiUnavailableException {
1359
1360 if (!isOpen()) {
1361 open();
1362 synchronized (control_mutex) {
1363 implicitOpen = true;
1364 }
1365 }
1366
1367 return getReceiver();
1368 }
1369
1370 public Transmitter getTransmitterReferenceCounting()
1371 throws MidiUnavailableException {
1372
1373 throw new MidiUnavailableException("No transmitter available");
1374 }
1375 }
|
61 import javax.sound.sampled.AudioSystem;
62 import javax.sound.sampled.LineUnavailableException;
63 import javax.sound.sampled.SourceDataLine;
64
65 /**
66 * The software synthesizer class.
67 *
68 * @author Karl Helgason
69 */
70 public final class SoftSynthesizer implements AudioSynthesizer,
71 ReferenceCountingDevice {
72
73 protected static final class WeakAudioStream extends InputStream
74 {
75 private volatile AudioInputStream stream;
76 public SoftAudioPusher pusher = null;
77 public AudioInputStream jitter_stream = null;
78 public SourceDataLine sourceDataLine = null;
79 public volatile long silent_samples = 0;
80 private int framesize = 0;
81 private final WeakReference<AudioInputStream> weak_stream_link;
82 private final AudioFloatConverter converter;
83 private float[] silentbuffer = null;
84 private final int samplesize;
85
86 public void setInputStream(AudioInputStream stream)
87 {
88 this.stream = stream;
89 }
90
91 @Override
92 public int available() throws IOException {
93 AudioInputStream local_stream = stream;
94 if(local_stream != null)
95 return local_stream.available();
96 return 0;
97 }
98
99 @Override
100 public int read() throws IOException {
101 byte[] b = new byte[1];
102 if (read(b) == -1)
103 return -1;
104 return b[0] & 0xFF;
105 }
106
107 @Override
108 public int read(byte[] b, int off, int len) throws IOException {
109 AudioInputStream local_stream = stream;
110 if(local_stream != null)
111 return local_stream.read(b, off, len);
112 else
113 {
114 int flen = len / samplesize;
115 if(silentbuffer == null || silentbuffer.length < flen)
116 silentbuffer = new float[flen];
117 converter.toByteArray(silentbuffer, flen, b, off);
118
119 silent_samples += (long)((len / framesize));
120
121 if(pusher != null)
122 if(weak_stream_link.get() == null)
123 {
124 Runnable runnable = new Runnable()
125 {
126 SoftAudioPusher _pusher = pusher;
127 AudioInputStream _jitter_stream = jitter_stream;
128 SourceDataLine _sourceDataLine = sourceDataLine;
129 @Override
130 public void run()
131 {
132 _pusher.stop();
133 if(_jitter_stream != null)
134 try {
135 _jitter_stream.close();
136 } catch (IOException e) {
137 e.printStackTrace();
138 }
139 if(_sourceDataLine != null)
140 _sourceDataLine.close();
141 }
142 };
143 pusher = null;
144 jitter_stream = null;
145 sourceDataLine = null;
146 new Thread(null, runnable, "Synthesizer",0,false).start();
147 }
148 return len;
149 }
150 }
151
152 public WeakAudioStream(AudioInputStream stream) {
153 this.stream = stream;
154 weak_stream_link = new WeakReference<>(stream);
155 converter = AudioFloatConverter.getConverter(stream.getFormat());
156 samplesize = stream.getFormat().getFrameSize() / stream.getFormat().getChannels();
157 framesize = stream.getFormat().getFrameSize();
158 }
159
160 public AudioInputStream getAudioInputStream()
161 {
162 return new AudioInputStream(this, stream.getFormat(), AudioSystem.NOT_SPECIFIED);
163 }
164
165 @Override
166 public void close() throws IOException
167 {
168 AudioInputStream astream = weak_stream_link.get();
169 if(astream != null)
170 astream.close();
171 }
172 }
173
174 private static class Info extends MidiDevice.Info {
175 Info() {
176 super(INFO_NAME, INFO_VENDOR, INFO_DESCRIPTION, INFO_VERSION);
177 }
178 }
179
180 static final String INFO_NAME = "Gervill";
181 static final String INFO_VENDOR = "OpenJDK";
182 static final String INFO_DESCRIPTION = "Software MIDI Synthesizer";
183 static final String INFO_VERSION = "1.0";
184 static final MidiDevice.Info info = new Info();
185
221
222 private SoftAudioPusher pusher = null;
223 private AudioInputStream pusher_stream = null;
224
225 private float controlrate = 147f;
226
227 private boolean open = false;
228 private boolean implicitOpen = false;
229
230 private String resamplerType = "linear";
231 private SoftResampler resampler = new SoftLinearResampler();
232
233 private int number_of_midi_channels = 16;
234 private int maxpoly = 64;
235 private long latency = 200000; // 200 msec
236 private boolean jitter_correction = false;
237
238 private SoftMainMixer mainmixer;
239 private SoftVoice[] voices;
240
241 private final Map<String, SoftTuning> tunings = new HashMap<>();
242 private final Map<String, SoftInstrument> inslist = new HashMap<>();
243 private final Map<String, ModelInstrument> loadedlist = new HashMap<>();
244 private final ArrayList<Receiver> recvslist = new ArrayList<>();
245
246 private void getBuffers(ModelInstrument instrument,
247 List<ModelByteBuffer> buffers) {
248 for (ModelPerformer performer : instrument.getPerformers()) {
249 if (performer.getOscillators() != null) {
250 for (ModelOscillator osc : performer.getOscillators()) {
251 if (osc instanceof ModelByteBufferWavetable) {
252 ModelByteBufferWavetable w = (ModelByteBufferWavetable)osc;
253 ModelByteBuffer buff = w.getBuffer();
254 if (buff != null)
255 buffers.add(buff);
256 buff = w.get8BitExtensionBuffer();
257 if (buff != null)
258 buffers.add(buff);
259 }
260 }
261 }
262 }
263 }
264
265 private boolean loadSamples(List<ModelInstrument> instruments) {
266 if (largemode)
267 return true;
268 List<ModelByteBuffer> buffers = new ArrayList<>();
269 for (ModelInstrument instrument : instruments)
270 getBuffers(instrument, buffers);
271 try {
272 ModelByteBuffer.loadAll(buffers);
273 } catch (IOException e) {
274 return false;
275 }
276 return true;
277 }
278
279 private boolean loadInstruments(List<ModelInstrument> instruments) {
280 if (!isOpen())
281 return false;
282 if (!loadSamples(instruments))
283 return false;
284
285 synchronized (control_mutex) {
286 if (channels != null)
287 for (SoftChannel c : channels)
288 {
469 }
470
471 float getControlRate() {
472 return controlrate;
473 }
474
475 SoftVoice[] getVoices() {
476 return voices;
477 }
478
479 SoftTuning getTuning(Patch patch) {
480 String t_id = patchToString(patch);
481 SoftTuning tuning = tunings.get(t_id);
482 if (tuning == null) {
483 tuning = new SoftTuning(patch);
484 tunings.put(t_id, tuning);
485 }
486 return tuning;
487 }
488
489 @Override
490 public long getLatency() {
491 synchronized (control_mutex) {
492 return latency;
493 }
494 }
495
496 @Override
497 public AudioFormat getFormat() {
498 synchronized (control_mutex) {
499 return format;
500 }
501 }
502
503 @Override
504 public int getMaxPolyphony() {
505 synchronized (control_mutex) {
506 return maxpoly;
507 }
508 }
509
510 @Override
511 public MidiChannel[] getChannels() {
512
513 synchronized (control_mutex) {
514 // if (external_channels == null) => the synthesizer is not open,
515 // create 16 proxy channels
516 // otherwise external_channels has the same length as channels array
517 if (external_channels == null) {
518 external_channels = new SoftChannelProxy[16];
519 for (int i = 0; i < external_channels.length; i++)
520 external_channels[i] = new SoftChannelProxy();
521 }
522 MidiChannel[] ret;
523 if (isOpen())
524 ret = new MidiChannel[channels.length];
525 else
526 ret = new MidiChannel[16];
527 for (int i = 0; i < ret.length; i++)
528 ret[i] = external_channels[i];
529 return ret;
530 }
531 }
532
533 @Override
534 public VoiceStatus[] getVoiceStatus() {
535 if (!isOpen()) {
536 VoiceStatus[] tempVoiceStatusArray
537 = new VoiceStatus[getMaxPolyphony()];
538 for (int i = 0; i < tempVoiceStatusArray.length; i++) {
539 VoiceStatus b = new VoiceStatus();
540 b.active = false;
541 b.bank = 0;
542 b.channel = 0;
543 b.note = 0;
544 b.program = 0;
545 b.volume = 0;
546 tempVoiceStatusArray[i] = b;
547 }
548 return tempVoiceStatusArray;
549 }
550
551 synchronized (control_mutex) {
552 VoiceStatus[] tempVoiceStatusArray = new VoiceStatus[voices.length];
553 for (int i = 0; i < voices.length; i++) {
554 VoiceStatus a = voices[i];
555 VoiceStatus b = new VoiceStatus();
556 b.active = a.active;
557 b.bank = a.bank;
558 b.channel = a.channel;
559 b.note = a.note;
560 b.program = a.program;
561 b.volume = a.volume;
562 tempVoiceStatusArray[i] = b;
563 }
564 return tempVoiceStatusArray;
565 }
566 }
567
568 @Override
569 public boolean isSoundbankSupported(Soundbank soundbank) {
570 for (Instrument ins: soundbank.getInstruments())
571 if (!(ins instanceof ModelInstrument))
572 return false;
573 return true;
574 }
575
576 @Override
577 public boolean loadInstrument(Instrument instrument) {
578 if (instrument == null || (!(instrument instanceof ModelInstrument))) {
579 throw new IllegalArgumentException("Unsupported instrument: " +
580 instrument);
581 }
582 List<ModelInstrument> instruments = new ArrayList<>();
583 instruments.add((ModelInstrument)instrument);
584 return loadInstruments(instruments);
585 }
586
587 @Override
588 public void unloadInstrument(Instrument instrument) {
589 if (instrument == null || (!(instrument instanceof ModelInstrument))) {
590 throw new IllegalArgumentException("Unsupported instrument: " +
591 instrument);
592 }
593 if (!isOpen())
594 return;
595
596 String pat = patchToString(instrument.getPatch());
597 synchronized (control_mutex) {
598 for (SoftChannel c: channels)
599 c.current_instrument = null;
600 inslist.remove(pat);
601 loadedlist.remove(pat);
602 for (int i = 0; i < channels.length; i++) {
603 channels[i].allSoundOff();
604 }
605 }
606 }
607
608 @Override
609 public boolean remapInstrument(Instrument from, Instrument to) {
610
611 if (from == null)
612 throw new NullPointerException();
613 if (to == null)
614 throw new NullPointerException();
615 if (!(from instanceof ModelInstrument)) {
616 throw new IllegalArgumentException("Unsupported instrument: " +
617 from.toString());
618 }
619 if (!(to instanceof ModelInstrument)) {
620 throw new IllegalArgumentException("Unsupported instrument: " +
621 to.toString());
622 }
623 if (!isOpen())
624 return false;
625
626 synchronized (control_mutex) {
627 if (!loadedlist.containsValue(to))
628 throw new IllegalArgumentException("Instrument to is not loaded.");
629 unloadInstrument(from);
630 ModelMappedInstrument mfrom = new ModelMappedInstrument(
631 (ModelInstrument)to, from.getPatch());
632 return loadInstrument(mfrom);
633 }
634 }
635
636 @Override
637 public Soundbank getDefaultSoundbank() {
638 synchronized (SoftSynthesizer.class) {
639 if (defaultSoundBank != null)
640 return defaultSoundBank;
641
642 List<PrivilegedAction<InputStream>> actions = new ArrayList<>();
643
644 actions.add(new PrivilegedAction<InputStream>() {
645 @Override
646 public InputStream run() {
647 File javahome = new File(System.getProperties()
648 .getProperty("java.home"));
649 File libaudio = new File(new File(javahome, "lib"), "audio");
650 if (libaudio.isDirectory()) {
651 File foundfile = null;
652 File[] files = libaudio.listFiles();
653 if (files != null) {
654 for (int i = 0; i < files.length; i++) {
655 File file = files[i];
656 if (file.isFile()) {
657 String lname = file.getName().toLowerCase();
658 if (lname.endsWith(".sf2")
659 || lname.endsWith(".dls")) {
660 if (foundfile == null
661 || (file.length() > foundfile
662 .length())) {
663 foundfile = file;
664 }
665 }
666 }
667 }
668 }
669 if (foundfile != null) {
670 try {
671 return new FileInputStream(foundfile);
672 } catch (IOException e) {
673 }
674 }
675 }
676 return null;
677 }
678 });
679
680 actions.add(new PrivilegedAction<InputStream>() {
681 @Override
682 public InputStream run() {
683 if (System.getProperties().getProperty("os.name")
684 .startsWith("Linux")) {
685
686 File[] systemSoundFontsDir = new File[] {
687 /* Arch, Fedora, Mageia */
688 new File("/usr/share/soundfonts/"),
689 new File("/usr/local/share/soundfonts/"),
690 /* Debian, Gentoo, OpenSUSE, Ubuntu */
691 new File("/usr/share/sounds/sf2/"),
692 new File("/usr/local/share/sounds/sf2/"),
693 };
694
695 /*
696 * Look for a default.sf2
697 */
698 for (File systemSoundFontDir : systemSoundFontsDir) {
699 if (systemSoundFontDir.isDirectory()) {
700 File defaultSoundFont = new File(systemSoundFontDir, "default.sf2");
701 if (defaultSoundFont.isFile()) {
702 try {
703 return new FileInputStream(defaultSoundFont);
704 } catch (IOException e) {
705 // continue with lookup
706 }
707 }
708 }
709 }
710 }
711 return null;
712 }
713 });
714
715 actions.add(new PrivilegedAction<InputStream>() {
716 @Override
717 public InputStream run() {
718 if (System.getProperties().getProperty("os.name")
719 .startsWith("Windows")) {
720 File gm_dls = new File(System.getenv("SystemRoot")
721 + "\\system32\\drivers\\gm.dls");
722 if (gm_dls.isFile()) {
723 try {
724 return new FileInputStream(gm_dls);
725 } catch (IOException e) {
726 }
727 }
728 }
729 return null;
730 }
731 });
732
733 actions.add(new PrivilegedAction<InputStream>() {
734 @Override
735 public InputStream run() {
736 /*
737 * Try to load saved generated soundbank
738 */
739 File userhome = new File(System.getProperty("user.home"),
740 ".gervill");
741 File emg_soundbank_file = new File(userhome,
742 "soundbank-emg.sf2");
743 if (emg_soundbank_file.isFile()) {
744 try {
745 return new FileInputStream(emg_soundbank_file);
746 } catch (IOException e) {
747 }
748 }
749 return null;
750 }
751 });
752
753 for (PrivilegedAction<InputStream> action : actions) {
754 try {
795 if (emg_soundbank_file.isFile()) {
796 return null;
797 }
798 return new FileOutputStream(emg_soundbank_file);
799 } catch (final FileNotFoundException ignored) {
800 }
801 return null;
802 });
803 if (out != null) {
804 try {
805 ((SF2Soundbank) defaultSoundBank).save(out);
806 out.close();
807 } catch (final IOException ignored) {
808 }
809 }
810 }
811 }
812 return defaultSoundBank;
813 }
814
815 @Override
816 public Instrument[] getAvailableInstruments() {
817 Soundbank defsbk = getDefaultSoundbank();
818 if (defsbk == null)
819 return new Instrument[0];
820 Instrument[] inslist_array = defsbk.getInstruments();
821 Arrays.sort(inslist_array, new ModelInstrumentComparator());
822 return inslist_array;
823 }
824
825 @Override
826 public Instrument[] getLoadedInstruments() {
827 if (!isOpen())
828 return new Instrument[0];
829
830 synchronized (control_mutex) {
831 ModelInstrument[] inslist_array =
832 new ModelInstrument[loadedlist.values().size()];
833 loadedlist.values().toArray(inslist_array);
834 Arrays.sort(inslist_array, new ModelInstrumentComparator());
835 return inslist_array;
836 }
837 }
838
839 @Override
840 public boolean loadAllInstruments(Soundbank soundbank) {
841 List<ModelInstrument> instruments = new ArrayList<>();
842 for (Instrument ins: soundbank.getInstruments()) {
843 if (ins == null || !(ins instanceof ModelInstrument)) {
844 throw new IllegalArgumentException(
845 "Unsupported instrument: " + ins);
846 }
847 instruments.add((ModelInstrument)ins);
848 }
849 return loadInstruments(instruments);
850 }
851
852 @Override
853 public void unloadAllInstruments(Soundbank soundbank) {
854 if (soundbank == null || !isSoundbankSupported(soundbank))
855 throw new IllegalArgumentException("Unsupported soundbank: " + soundbank);
856
857 if (!isOpen())
858 return;
859
860 for (Instrument ins: soundbank.getInstruments()) {
861 if (ins instanceof ModelInstrument) {
862 unloadInstrument(ins);
863 }
864 }
865 }
866
867 @Override
868 public boolean loadInstruments(Soundbank soundbank, Patch[] patchList) {
869 List<ModelInstrument> instruments = new ArrayList<>();
870 for (Patch patch: patchList) {
871 Instrument ins = soundbank.getInstrument(patch);
872 if (ins == null || !(ins instanceof ModelInstrument)) {
873 throw new IllegalArgumentException(
874 "Unsupported instrument: " + ins);
875 }
876 instruments.add((ModelInstrument)ins);
877 }
878 return loadInstruments(instruments);
879 }
880
881 @Override
882 public void unloadInstruments(Soundbank soundbank, Patch[] patchList) {
883 if (soundbank == null || !isSoundbankSupported(soundbank))
884 throw new IllegalArgumentException("Unsupported soundbank: " + soundbank);
885
886 if (!isOpen())
887 return;
888
889 for (Patch pat: patchList) {
890 Instrument ins = soundbank.getInstrument(pat);
891 if (ins instanceof ModelInstrument) {
892 unloadInstrument(ins);
893 }
894 }
895 }
896
897 @Override
898 public MidiDevice.Info getDeviceInfo() {
899 return info;
900 }
901
902 private Properties getStoredProperties() {
903 return AccessController
904 .doPrivileged((PrivilegedAction<Properties>) () -> {
905 Properties p = new Properties();
906 String notePath = "/com/sun/media/sound/softsynthesizer";
907 try {
908 Preferences prefroot = Preferences.userRoot();
909 if (prefroot.nodeExists(notePath)) {
910 Preferences prefs = prefroot.node(notePath);
911 String[] prefs_keys = prefs.keys();
912 for (String prefs_key : prefs_keys) {
913 String val = prefs.get(prefs_key, null);
914 if (val != null) {
915 p.setProperty(prefs_key, val);
916 }
917 }
918 }
919 } catch (final BackingStoreException ignored) {
920 }
921 return p;
922 });
923 }
924
925 @Override
926 public AudioSynthesizerPropertyInfo[] getPropertyInfo(Map<String, Object> info) {
927 List<AudioSynthesizerPropertyInfo> list = new ArrayList<>();
928
929 AudioSynthesizerPropertyInfo item;
930
931 // If info != null or synthesizer is closed
932 // we return how the synthesizer will be set on next open
933 // If info == null and synthesizer is open
934 // we return current synthesizer properties.
935 boolean o = info == null && open;
936
937 item = new AudioSynthesizerPropertyInfo("interpolation", o?resamplerType:"linear");
938 item.choices = new String[]{"linear", "linear1", "linear2", "cubic",
939 "lanczos", "sinc", "point"};
940 item.description = "Interpolation method";
941 list.add(item);
942
943 item = new AudioSynthesizerPropertyInfo("control rate", o?controlrate:147f);
944 item.description = "Control rate";
945 list.add(item);
946
947 item = new AudioSynthesizerPropertyInfo("format",
1062 Number n = (Number) v;
1063 if (c == Byte.class)
1064 item2.value = Byte.valueOf(n.byteValue());
1065 if (c == Short.class)
1066 item2.value = Short.valueOf(n.shortValue());
1067 if (c == Integer.class)
1068 item2.value = Integer.valueOf(n.intValue());
1069 if (c == Long.class)
1070 item2.value = Long.valueOf(n.longValue());
1071 if (c == Float.class)
1072 item2.value = Float.valueOf(n.floatValue());
1073 if (c == Double.class)
1074 item2.value = Double.valueOf(n.doubleValue());
1075 }
1076 }
1077 }
1078
1079 return items;
1080 }
1081
1082 @Override
1083 public void open() throws MidiUnavailableException {
1084 if (isOpen()) {
1085 synchronized (control_mutex) {
1086 implicitOpen = false;
1087 }
1088 return;
1089 }
1090 open(null, null);
1091 }
1092
1093 @Override
1094 public void open(SourceDataLine line, Map<String, Object> info) throws MidiUnavailableException {
1095 if (isOpen()) {
1096 synchronized (control_mutex) {
1097 implicitOpen = false;
1098 }
1099 return;
1100 }
1101 synchronized (control_mutex) {
1102 try {
1103 if (line != null) {
1104 // can throw IllegalArgumentException
1105 setFormat(line.getFormat());
1106 }
1107
1108 AudioInputStream ais = openStream(getFormat(), info);
1109
1110 weakstream = new WeakAudioStream(ais);
1111 ais = weakstream.getAudioInputStream();
1112
1113 if (line == null)
1168 if(weakstream != null)
1169 {
1170 weakstream.pusher = pusher;
1171 weakstream.sourceDataLine = sourceDataLine;
1172 }
1173
1174 } catch (final LineUnavailableException | SecurityException
1175 | IllegalArgumentException e) {
1176 if (isOpen()) {
1177 close();
1178 }
1179 // am: need MidiUnavailableException(Throwable) ctor!
1180 MidiUnavailableException ex = new MidiUnavailableException(
1181 "Can not open line");
1182 ex.initCause(e);
1183 throw ex;
1184 }
1185 }
1186 }
1187
1188 @Override
1189 public AudioInputStream openStream(AudioFormat targetFormat,
1190 Map<String, Object> info) throws MidiUnavailableException {
1191
1192 if (isOpen())
1193 throw new MidiUnavailableException("Synthesizer is already open");
1194
1195 synchronized (control_mutex) {
1196
1197 gmmode = 0;
1198 voice_allocation_mode = 0;
1199
1200 processPropertyInfo(info);
1201
1202 open = true;
1203 implicitOpen = false;
1204
1205 if (targetFormat != null)
1206 setFormat(targetFormat);
1207
1208 if (load_default_soundbank)
1250 }
1251 }
1252
1253 for (int i = 0; i < channels.length; i++)
1254 external_channels[i].setChannel(channels[i]);
1255
1256 for (SoftVoice voice: getVoices())
1257 voice.resampler = resampler.openStreamer();
1258
1259 for (Receiver recv: getReceivers()) {
1260 SoftReceiver srecv = ((SoftReceiver)recv);
1261 srecv.open = open;
1262 srecv.mainmixer = mainmixer;
1263 srecv.midimessages = mainmixer.midimessages;
1264 }
1265
1266 return mainmixer.getInputStream();
1267 }
1268 }
1269
1270 @Override
1271 public void close() {
1272
1273 if (!isOpen())
1274 return;
1275
1276 SoftAudioPusher pusher_to_be_closed = null;
1277 AudioInputStream pusher_stream_to_be_closed = null;
1278 synchronized (control_mutex) {
1279 if (pusher != null) {
1280 pusher_to_be_closed = pusher;
1281 pusher_stream_to_be_closed = pusher_stream;
1282 pusher = null;
1283 pusher_stream = null;
1284 }
1285 }
1286
1287 if (pusher_to_be_closed != null) {
1288 // Pusher must not be closed synchronized against control_mutex,
1289 // this may result in synchronized conflict between pusher
1290 // and current thread.
1309
1310 if (external_channels != null)
1311 for (int i = 0; i < external_channels.length; i++)
1312 external_channels[i].setChannel(null);
1313
1314 if (sourceDataLine != null) {
1315 sourceDataLine.close();
1316 sourceDataLine = null;
1317 }
1318
1319 inslist.clear();
1320 loadedlist.clear();
1321 tunings.clear();
1322
1323 while (recvslist.size() != 0)
1324 recvslist.get(recvslist.size() - 1).close();
1325
1326 }
1327 }
1328
1329 @Override
1330 public boolean isOpen() {
1331 synchronized (control_mutex) {
1332 return open;
1333 }
1334 }
1335
1336 @Override
1337 public long getMicrosecondPosition() {
1338
1339 if (!isOpen())
1340 return 0;
1341
1342 synchronized (control_mutex) {
1343 return mainmixer.getMicrosecondPosition();
1344 }
1345 }
1346
1347 @Override
1348 public int getMaxReceivers() {
1349 return -1;
1350 }
1351
1352 @Override
1353 public int getMaxTransmitters() {
1354 return 0;
1355 }
1356
1357 @Override
1358 public Receiver getReceiver() throws MidiUnavailableException {
1359
1360 synchronized (control_mutex) {
1361 SoftReceiver receiver = new SoftReceiver(this);
1362 receiver.open = open;
1363 recvslist.add(receiver);
1364 return receiver;
1365 }
1366 }
1367
1368 @Override
1369 public List<Receiver> getReceivers() {
1370
1371 synchronized (control_mutex) {
1372 ArrayList<Receiver> recvs = new ArrayList<>();
1373 recvs.addAll(recvslist);
1374 return recvs;
1375 }
1376 }
1377
1378 @Override
1379 public Transmitter getTransmitter() throws MidiUnavailableException {
1380
1381 throw new MidiUnavailableException("No transmitter available");
1382 }
1383
1384 @Override
1385 public List<Transmitter> getTransmitters() {
1386
1387 return new ArrayList<>();
1388 }
1389
1390 @Override
1391 public Receiver getReceiverReferenceCounting()
1392 throws MidiUnavailableException {
1393
1394 if (!isOpen()) {
1395 open();
1396 synchronized (control_mutex) {
1397 implicitOpen = true;
1398 }
1399 }
1400
1401 return getReceiver();
1402 }
1403
1404 @Override
1405 public Transmitter getTransmitterReferenceCounting()
1406 throws MidiUnavailableException {
1407
1408 throw new MidiUnavailableException("No transmitter available");
1409 }
1410 }
|