1 package org.rpi.player;
2
3 import java.util.Collections;
4 import java.util.Observable;
5 import java.util.Observer;
6 import java.util.Random;
7 import java.util.concurrent.CopyOnWriteArrayList;
8
9 import org.apache.log4j.Logger;
10 import org.rpi.config.Config;
11 import org.rpi.mpdplayer.MPDPlayerController;
12 import org.rpi.mplayer.MPlayerController;
13 import org.rpi.player.events.EventBase;
14 import org.rpi.player.events.EventFinishedCurrentTrack;
15 import org.rpi.player.events.EventMuteChanged;
16 import org.rpi.player.events.EventPlayListPlayingTrackID;
17 import org.rpi.player.events.EventPlayListStatusChanged;
18 import org.rpi.player.events.EventPlayListUpdateShuffle;
19 import org.rpi.player.events.EventRadioPlayName;
20 import org.rpi.player.events.EventRadioPlayingTrackID;
21 import org.rpi.player.events.EventRadioStatusChanged;
22 import org.rpi.player.events.EventRequestVolumeDec;
23 import org.rpi.player.events.EventRequestVolumeInc;
24 import org.rpi.player.events.EventStandbyChanged;
25 import org.rpi.player.events.EventStatusChanged;
26 import org.rpi.player.events.EventTimeUpdate;
27 import org.rpi.player.events.EventTrackChanged;
28 import org.rpi.player.events.EventUpdateTrackMetaText;
29 import org.rpi.player.events.EventVolumeChanged;
30 import org.rpi.player.observers.*;
31 import org.rpi.player.observers.ObservableTime;
32 import org.rpi.playlist.CustomTrack;
33 import org.rpi.radio.CustomChannel;
34
35
36
37 public class PlayManager implements Observer {
38
39 private CustomTrack current_track = null;
40 private CopyOnWriteArrayList<CustomTrack> tracks = new CopyOnWriteArrayList<CustomTrack>();
41 private CopyOnWriteArrayList<String> shuffled_tracks = new CopyOnWriteArrayList<String>();
42
43 private static Logger log = Logger.getLogger(PlayManager.class);
44 private IPlayerController mPlayer = null;
45
46 private boolean repeatPlayList = false;
47 private boolean shuffle = false;
48 private boolean bPaused;
49 private boolean standby = true;
50
51
52 private long volume = 100;
53 private long mplayer_volume = 100;
54 private boolean bMute;
55 private boolean bExternalVolume = false;
56
57
58 private ObservableTime obsvTime = new ObservableTime();
59 private ObservableInfo obsvInfo = new ObservableInfo();
60 private ObservableVolume obsvVolume = new ObservableVolume();
61 private ObservableRadio obsvRadio = new ObservableRadio();
62 private ObservablePlayList obsvPlayList = new ObservablePlayList();
63 private ObservableProduct obsvProduct = new ObservableProduct();
64 private ObservableAVTransport obsvAVTransport = new ObservableAVTransport();
65 private String status = "";
66
67 private static PlayManager instance = null;
68
69
70
71
72
73
74 public static PlayManager getInstance() {
75 if (instance == null) {
76 instance = new PlayManager();
77 }
78 return instance;
79 }
80
81
82
83
84 private PlayManager() {
85 if (Config.player.equalsIgnoreCase("MPD")) {
86 log.debug("MPD Player Selected");
87 mPlayer = new MPDPlayerController();
88 } else {
89 log.debug("MPlayer Selected");
90 mPlayer = new MPlayerController();
91 }
92 mPlayer.addObserver(this);
93 }
94
95
96
97
98
99
100 private void playThis(CustomTrack t) {
101 if (t != null) {
102 current_track = t;
103 long v = mplayer_volume;
104 if (!isUseExternalVolume())
105 v = volume;
106 mPlayer.playThis(t, v, bMute);
107 }
108 }
109
110
111
112
113
114
115
116 public CustomTrack getNextTrack(int offset) {
117 if (shuffle) {
118 return getRandomTrack(offset);
119 } else {
120 return getTrack(offset);
121 }
122 }
123
124
125
126
127
128
129
130 private CustomTrack getRandomTrack(int offset) {
131 if (current_track != null) {
132 if (!(current_track instanceof CustomChannel)) {
133 try {
134 int i = 0;
135 for (String t : getShuffledTracks()) {
136 if (t.equalsIgnoreCase("" + current_track.getId())) {
137 break;
138 }
139 i++;
140 }
141 if (getShuffledTracks().size() > i + offset) {
142 if (i + offset >= 0) {
143 String track_id = getShuffledTracks().get(i + offset);
144 CustomTrack newTrack = getTrackFromId(Integer.parseInt(track_id));
145 log.debug("Returning Next Shuffled Track: " + newTrack.getUri());
146 return (newTrack);
147 }
148 } else {
149 log.info("We have Reached the End of the PlayList");
150 if (isRepeatPlayList()) {
151 log.info("Repeat Playlsit is Set so start again...");
152 shuffleTracks();
153 if (getShuffledTracks().size() > 0) {
154 String track_id = getShuffledTracks().get(0);
155 CustomTrack newTrack = getTrackFromId(Integer.parseInt(track_id));
156 return newTrack;
157 }
158 } else {
159 return null;
160 }
161 }
162 } catch (Exception e) {
163 log.error("Error GetNextTrack: ", e);
164 }
165 } else {
166
167 }
168 }
169 if (shuffled_tracks.size() > 0) {
170 String id = shuffled_tracks.get(0);
171 CustomTrack t = getTrackFromId(Integer.parseInt(id));
172 return t;
173 }
174 return null;
175 }
176
177
178
179
180
181
182
183 private CustomTrack getTrack(int offset) {
184 if (current_track != null)
185 try {
186 log.debug("Getting Next Track, CurrentTrack is: " + current_track.getUri());
187 int i = 0;
188 for (CustomTrack t : getTracks()) {
189 if (current_track.getId() == t.getId()) {
190 break;
191 }
192 i++;
193 }
194 if (getTracks().size() > i + offset) {
195 if (i + offset >= 0) {
196 CustomTrack newTrack = getTracks().get(i + offset);
197 log.debug("Returning Next Track: " + newTrack.getUri());
198 return (newTrack);
199 }
200 } else {
201 log.info("We have Reached the End of the PlayList");
202 if (isRepeatPlayList()) {
203 log.info("Repeat Playlsit is Set so start again...");
204 if (getTracks().size() > 0) {
205 CustomTrack newTrack = getTracks().get(0);
206 return newTrack;
207 }
208 }
209 }
210 } catch (Exception e) {
211 log.error("Error GetNextTrack: ", e);
212 }
213 else {
214 if (tracks.size() > 0) {
215 return tracks.get(0);
216 }
217 }
218 return null;
219 }
220
221
222
223
224
225
226
227 public CustomTrack getTrackFromIndex(int index) {
228 log.debug("GetTrackFromIndex: " + index);
229 try {
230 return tracks.get(index);
231 } catch (Exception e) {
232 log.error("Error gettingTrackFromId: ", e);
233 }
234 return null;
235 }
236
237
238
239
240
241
242
243 public CustomTrack getTrackFromId(int id) {
244 log.debug("GetTrakcFromId: " + id);
245 for (CustomTrack t : tracks) {
246 if (t.getId() == id) {
247 return t;
248 }
249 }
250 return null;
251 }
252
253 public synchronized boolean isStandby() {
254 return standby;
255 }
256
257 public synchronized void setStandby(boolean standby) {
258 this.standby = standby;
259 EventStandbyChanged ev = new EventStandbyChanged();
260 ev.setStandby(standby);
261 obsvProduct.notifyChange(ev);
262 if (standby)
263 stop();
264 }
265
266
267
268
269
270 public boolean isRepeatPlayList() {
271 return repeatPlayList;
272 }
273
274
275
276
277
278 public void setRepeatPlayList(boolean repeatPlayList) {
279 this.repeatPlayList = repeatPlayList;
280 }
281
282
283
284
285 public boolean isShuffle() {
286 return shuffle;
287 }
288
289
290
291
292
293 public void setShuffle(boolean shuffle) {
294 if (shuffle)
295 shuffleTracks();
296 this.shuffle = shuffle;
297 }
298
299 public void updateShuffle(boolean shuffle) {
300 setShuffle(shuffle);
301 EventPlayListUpdateShuffle ev = new EventPlayListUpdateShuffle();
302 ev.setShuffle(shuffle);
303 obsvPlayList.notifyChange(ev);
304 }
305
306
307
308
309
310
311
312 private void addAsNextShuffleTrack(CustomTrack t) {
313 int this_track = getTrackIndexShuffled(t.getId());
314 if (this_track >= 0) {
315 shuffled_tracks.remove(this_track);
316 }
317 if (current_track != null) {
318 if (!(current_track instanceof CustomChannel)) {
319 int index = getTrackIndexShuffled(current_track.getId());
320 if (index >= 0) {
321 shuffled_tracks.add(index + 1, "" + t.getId());
322 return;
323 }
324 }
325 }
326 shuffled_tracks.add(0, "" + t.getId());
327
328 }
329
330
331
332
333 private void shuffleTracks() {
334 shuffled_tracks.clear();
335 for (CustomTrack t : tracks) {
336 shuffled_tracks.add("" + t.getId());
337 }
338 long seed = System.nanoTime();
339 Collections.shuffle(shuffled_tracks, new Random(seed));
340 if (current_track != null) {
341 if (current_track instanceof CustomChannel)
342 return;
343 log.debug("We have shuffled so set the current track to index ZERO");
344 int index = getTrackIndexShuffled(current_track.getId());
345 shuffled_tracks.remove(index);
346 shuffled_tracks.add(0, "" + current_track.getId());
347 }
348 }
349
350
351
352
353
354
355
356
357 private int generateRandonNumber(int min, int max) {
358 long seed = System.nanoTime();
359 Random rand = new Random(seed);
360 int limit = (max - min) + min;
361 if (limit == 0)
362 limit = 1;
363 int res = rand.nextInt(limit);
364 log.debug("Random Number in range " + min + " - " + max + " = " + res);
365 return res;
366 }
367
368
369
370
371 private synchronized CopyOnWriteArrayList<CustomTrack> getTracks() {
372 return tracks;
373 }
374
375
376
377
378 private synchronized CopyOnWriteArrayList<String> getShuffledTracks() {
379 return shuffled_tracks;
380 }
381
382
383
384
385
386
387
388 public synchronized void setTracks(CopyOnWriteArrayList<CustomTrack> tracks) {
389 this.tracks = (CopyOnWriteArrayList<CustomTrack>) tracks.clone();
390 }
391
392 public synchronized void setCurrentTrack(CustomTrack track) {
393 current_track = track;
394 log.debug("Current Track Id: " + track.getId());
395 }
396
397
398
399
400
401 public synchronized CustomTrack getCurrentTrack() {
402 return current_track;
403 }
404
405
406
407
408 public synchronized void deletedAllTracks() {
409 tracks.clear();
410 shuffled_tracks.clear();
411 }
412
413 public synchronized void deletedTrack(long iD) {
414 int index = getTrackIndex(iD);
415 if (index >= 0)
416 tracks.remove(index);
417 if (shuffled_tracks.contains("" + iD)) {
418 shuffled_tracks.remove("" + iD);
419 }
420 }
421
422
423
424
425
426
427 public synchronized void pause(boolean bPause) {
428 if (mPlayer.isPlaying()) {
429 mPlayer.pause(bPause);
430 setPaused(bPause);
431 setStatus("Paused");
432 }
433 }
434
435
436
437
438
439 private void setPaused(boolean paused) {
440 bPaused = paused;
441 }
442
443
444
445
446
447 private boolean isPaused() {
448 return bPaused;
449 }
450
451
452
453
454 public synchronized void stop() {
455 if (mPlayer.isPlaying()) {
456 mPlayer.stop();
457 }
458 }
459
460
461
462
463
464
465
466
467 public synchronized void playIndex(long index) {
468 CustomTrack t = getTrackFromIndex((int) index);
469 if (shuffle) {
470 if (!mPlayer.isPlaying()) {
471 shuffleTracks();
472 }
473 addAsNextShuffleTrack(t);
474 }
475 if (t != null) {
476 playThis(t);
477 } else {
478 log.debug("Next Track was NULL");
479 }
480 }
481
482 public synchronized void playTrackId(long id)
483 {
484 CustomTrack t = getTrackFromId((int)id);
485 if(t !=null)
486 {
487 playThis(t);
488 }
489 else {
490 log.debug("Next Track was NULL");
491 }
492 }
493
494
495
496
497
498 public synchronized void play() {
499 if (isPaused()) {
500 if (mPlayer.isPlaying()) {
501 mPlayer.resume();
502 }
503 setStatus("Playing");
504 setPaused(false);
505 } else {
506
507 if(current_track ==null)
508 {
509 log.debug("CurrentTrack was NULL");
510 }
511 if (shuffle)
512 shuffleTracks();
513 CustomTrack t = getNextTrack(1);
514 if (t != null) {
515 playThis(t);
516 }
517
518
519
520
521
522 }
523 }
524
525
526
527
528
529
530 public synchronized void playFile(CustomChannel c) {
531 log.debug("Play Radio Id: " + c.getId());
532 playThis(c);
533 }
534
535 public synchronized void playAV(CustomTrack c)
536 {
537 log.debug("Play AV Track : " + c.getUri());
538 playThis(c);
539 }
540
541
542
543
544
545
546 public synchronized void playRadio(String name) {
547 EventRadioPlayName ev = new EventRadioPlayName();
548 ev.setName(name);
549 obsvRadio.notifyChange(ev);
550 }
551
552
553
554
555 public synchronized void nextTrack() {
556 CustomTrack t = getNextTrack(1);
557 if (t != null) {
558 playThis(t);
559 }
560 }
561
562
563
564
565 public synchronized void previousTrack() {
566 if (current_track != null) {
567 if (current_track.getTime() > 0 && current_track.getTime() >= 5) {
568 playThis(current_track);
569 return;
570 }
571
572 }
573 CustomTrack t = getNextTrack(-1);
574 if (t != null) {
575 playThis(t);
576 }
577 }
578
579
580
581
582
583
584
585 public synchronized void seekAbsolute(long seconds) {
586 if (mPlayer.isPlaying()) {
587 mPlayer.seekAbsolute(seconds);
588 }
589 }
590
591
592
593
594
595
596
597
598 public synchronized void setVolume(long volume) {
599 if (!bMute) {
600 if(this.volume <0 ||volume > 100)
601 {
602 log.debug("Volume is less than Zero, assume the DAC doesn't support Hardware Volume Control");
603 return;
604 }
605 this.volume = volume;
606 log.debug("Set Volume");
607 EventVolumeChanged ev = new EventVolumeChanged();
608 ev.setVolume(volume);
609 obsvVolume.notifyChange(ev);
610 {
611 if (mPlayer.isActive()) {
612 if (!isUseExternalVolume())
613 mPlayer.setVolume(volume);
614 }
615 }
616 }
617 }
618
619
620
621
622
623
624 public synchronized void setMute(boolean mute) {
625 if(volume< 0)
626 {
627 log.debug("Volume is less than Zero, assume the DAC doesn't support Hardware Volume Control");
628 return;
629 }
630 this.bMute = mute;
631 EventMuteChanged em = new EventMuteChanged();
632 em.setMute(mute);
633 obsvProduct.notifyChange(em);
634 obsvVolume.notifyChange(em);
635
636
637 if (mPlayer.isActive()) {
638 if (!isUseExternalVolume())
639 mPlayer.setMute(mute);
640 }
641 bMute = mute;
642 }
643
644
645
646
647
648
649
650 public synchronized void insertTrack(long aAfterId, CustomTrack track) {
651 insertAfterTrack(aAfterId, track);
652 if (shuffle) {
653 insertAfterShuffleTrack(aAfterId, track);
654 }
655 }
656
657
658
659
660
661
662
663 private void insertAfterTrack(long aAfterId, CustomTrack track) {
664 int index = 0;
665 if (aAfterId != 0)
666 index = getTrackIndex(aAfterId) + 1;
667 tracks.add(index, track);
668 }
669
670
671
672
673
674
675
676 private void insertAfterShuffleTrack(long aAfterId, CustomTrack track) {
677 int index = 0;
678 if (aAfterId != 0)
679 index = getTrackIndexShuffled(aAfterId);
680 if (current_track != null) {
681 int current_index = getTrackIndexShuffled(current_track.getId());
682 if (current_index == index) {
683 shuffled_tracks.add(index + 1, "" + track.getId());
684 } else {
685 int random = generateRandonNumber(index, shuffled_tracks.size());
686 shuffled_tracks.add(random, "" + track.getId());
687 }
688 } else {
689 if (aAfterId != 0) {
690 int random = generateRandonNumber(index, shuffled_tracks.size());
691 shuffled_tracks.add(random, "" + track.getId());
692 } else {
693 shuffled_tracks.add(index, "" + track.getId());
694 }
695 }
696 }
697
698
699
700
701
702
703
704 private int getTrackIndex(long aAfterId) {
705 int i = 0;
706 for (CustomTrack t : getTracks()) {
707 if (aAfterId == t.getId()) {
708 return i;
709 }
710 i++;
711 }
712 return -99;
713 }
714
715
716
717
718
719
720
721 private int getTrackIndexShuffled(long aAfterId) {
722 int i = 0;
723 for (String t : getShuffledTracks()) {
724 if (t.equalsIgnoreCase("" + aAfterId)) {
725 return i;
726 }
727 i++;
728 }
729 return -99;
730 }
731
732
733
734
735 public synchronized void deleteAllTracks() {
736 deletedAllTracks();
737 if (!(getCurrentTrack() instanceof CustomChannel)) {
738 current_track = null;
739 if (mPlayer.isPlaying()) {
740 mPlayer.stop();
741 }
742 }
743
744 }
745
746
747
748
749
750
751 public synchronized void DeleteTrack(long iD) {
752 deletedTrack(iD);
753 CustomTrack t = getCurrentTrack();
754 if (t != null)
755 if (t.getId() == iD && !(t instanceof CustomChannel)) {
756 if (mPlayer.isPlaying()) {
757 mPlayer.stop();
758 }
759 }
760 }
761
762
763
764
765 public synchronized void destroy() {
766 log.debug("Start of destroy");
767 if (mPlayer.isActive()) {
768 log.debug("Attempt to Destroy MPlayer");
769 mPlayer.destroy();
770 }
771 }
772
773
774
775
776
777
778 public synchronized void setStatus(String status) {
779 log.debug("SetStatus: " + status);
780 this.status = status;
781 EventTrackChanged ev = new EventTrackChanged();
782 if (status.equalsIgnoreCase("PLAYING")) {
783
784 if (current_track != null) {
785 playingTrack(current_track.getId());
786 ev.setTrack(current_track);
787 try {
788 obsvInfo.notifyChange(ev);
789 } catch (Exception e) {
790 log.error("Error Notify: " + e);
791 }
792 }
793
794 }
795 if (current_track instanceof CustomChannel) {
796 EventRadioStatusChanged evr = new EventRadioStatusChanged();
797 evr.setStatus(status);
798 obsvRadio.notifyChange(evr);
799 } else {
800 EventPlayListStatusChanged evr = new EventPlayListStatusChanged();
801 evr.setStatus(status);
802 obsvPlayList.notifyChange(evr);
803 }
804 }
805
806
807
808
809
810
811 public synchronized void playingTrack(int iD) {
812 if (current_track instanceof CustomChannel) {
813 EventRadioPlayingTrackID evrp = new EventRadioPlayingTrackID();
814 evrp.setId(iD);
815 obsvRadio.notifyChange(evrp);
816 } else {
817 EventPlayListPlayingTrackID evrp = new EventPlayListPlayingTrackID();
818 evrp.setId(iD);
819 obsvPlayList.notifyChange(evrp);
820 }
821 }
822
823
824
825
826
827
828 public synchronized long incVolume() {
829 EventRequestVolumeInc ev = new EventRequestVolumeInc();
830 obsvVolume.notifyChange(ev);
831 if (volume < 100 ) {
832 long v = volume;
833 v++;
834 setVolume(v);
835 }
836 return volume;
837 }
838
839
840
841
842
843
844 public synchronized long decVolume() {
845 EventRequestVolumeDec ev = new EventRequestVolumeDec();
846 obsvVolume.notifyChange(ev);
847 if (volume > 0) {
848 long v = volume;
849 v--;
850 setVolume(v);
851 }
852 return volume;
853 }
854
855
856
857
858
859
860 public synchronized long getVolume() {
861 return volume;
862 }
863
864 @Override
865 public void update(Observable paramObservable, Object obj) {
866 EventBase e = (EventBase) obj;
867 switch (e.getType()) {
868 case EVENTFINISHEDCURRENTTRACK:
869 try {
870 EventFinishedCurrentTrack evct = (EventFinishedCurrentTrack) e;
871 if (evct.isQuit()) {
872 log.debug("Track was Stopped, do not select Next Track");
873 } else {
874 log.debug("Track Stopped, get Next Track");
875 CustomTrack t = getNextTrack(1);
876 if (t != null) {
877 playThis(t);
878 }
879 }
880 } catch (Exception etf) {
881 log.error("Error EVENTFINISHEDCURRENTTRACK", etf);
882 }
883 break;
884 case EVENTCURRENTTRACKFINISHING:
885 log.debug("Current Track is going to finish, get NextTrack and PreLoad");
886 CustomTrack t = getNextTrack(1);
887 if (t != null) {
888 mPlayer.preLoadTrack(t);
889 }
890 break;
891 case EVENTPLAYLISTPLAYINGTRACKID:
892 obsvPlayList.notifyChange(e);
893 break;
894 case EVENTTIMEUPDATED:
895 try {
896 obsvTime.notifyChange(e);
897 if (current_track != null) {
898 EventTimeUpdate ed = (EventTimeUpdate) e;
899 current_track.setTime(ed.getTime());
900 }
901 } catch (Exception etu) {
902 log.error("Error EVENTTIMEUPDATED", etu);
903 }
904 break;
905 case EVENTSTATUSCHANGED:
906 try {
907 EventStatusChanged es = (EventStatusChanged) e;
908 log.debug("EventStatusChanged: " + es.getStatus());
909 setStatus(es.getStatus());
910 obsvAVTransport.notifyChange(es);
911 } catch (Exception esc) {
912 log.error("Error EVENTSTATUSCHANGED", esc);
913 }
914 break;
915 case EVENTDURATIONUPDATE:
916 try {
917 obsvTime.notifyChange(e);
918 } catch (Exception edu) {
919 log.error("Error EVENTDURATIONUPDATE", edu);
920 }
921 break;
922 case EVENTUPDATETRACKINFO:
923 try {
924 obsvInfo.notifyChange(e);
925 } catch (Exception eut) {
926 log.error("Error EVENTUPDATETRACKINFO", eut);
927 }
928 break;
929 case EVENTUPDATETRACKMETATEXT:
930 try {
931 EventUpdateTrackMetaText etm = (EventUpdateTrackMetaText) e;
932 if (current_track != null) {
933 if (current_track instanceof CustomChannel) {
934 if(current_track.isICYReverse())
935 {
936 String title = etm.getArtist();
937 String artist = etm.getTitle();
938 etm.setTitle(title);
939 etm.setArtist(artist);
940 }
941 String metatext = current_track.updateTrack(etm.getTitle(), etm.getArtist());
942 etm.setMetaText(metatext);
943 obsvInfo.notifyChange(etm);
944 }
945 }
946 } catch (Exception etm) {
947 log.error("Error EVENTUPDATETRACKMETATEXT", etm);
948 }
949 break;
950 case EVENTLOADED:
951 try {
952 log.debug("Track Loaded");
953 } catch (Exception etl) {
954 log.error("Error EVENTLOADED", etl);
955 }
956 break;
957 case EVENTTRACKCHANGED:
958 EventTrackChanged etc = (EventTrackChanged) e;
959 current_track = etc.getTrack();
960 break;
961 case EVENTVOLUMECHANGED:
962 try {
963 EventVolumeChanged ev = (EventVolumeChanged) e;
964 volume = ev.getVolume();
965 obsvVolume.notifyChange(e);
966 } catch (Exception ex) {
967
968 }
969 break;
970
971
972
973
974
975
976
977 }
978 }
979
980
981
982
983
984
985 public synchronized void observTimeEvents(Observer o) {
986 obsvTime.addObserver(o);
987 }
988
989
990
991
992
993
994 public synchronized void observInfoEvents(Observer o) {
995 obsvInfo.addObserver(o);
996 }
997
998
999
1000
1001
1002
1003 public synchronized void observVolumeEvents(Observer o) {
1004 obsvVolume.addObserver(o);
1005 }
1006
1007
1008
1009
1010
1011
1012 public synchronized void observPlayListEvents(Observer o) {
1013 obsvPlayList.addObserver(o);
1014 }
1015
1016
1017
1018
1019
1020
1021 public synchronized void observeProductEvents(Observer o) {
1022 obsvProduct.addObserver(o);
1023 }
1024
1025
1026
1027
1028
1029
1030 public synchronized void observRadioEvents(Observer o) {
1031 obsvRadio.addObserver(o);
1032 }
1033
1034 public void observAVEvnts(Observer o) {
1035 obsvAVTransport.addObserver(o);
1036
1037 }
1038
1039 public synchronized boolean isUseExternalVolume() {
1040 return bExternalVolume;
1041 }
1042
1043 public synchronized void setUseExternalVolume(boolean bExternalVolume) {
1044 this.bExternalVolume = bExternalVolume;
1045 }
1046
1047 public synchronized void toggleMute() {
1048 setMute(!bMute);
1049 }
1050
1051 public synchronized boolean getMute() {
1052 return bMute;
1053 }
1054
1055 public void pause() {
1056 pause(!bPaused);
1057 }
1058
1059
1060
1061 }