getPadDataView();
41 |
42 | /**
43 | * Dispatched the the selected bank has changed in the {@link IPadModel}.
44 | *
45 | * This also implicitly says the selected data has changed, but its up to
46 | * the views to update based on this event.
47 | *
48 | * The {@link OnPadModelSelectedDataChange} event is the explicit change of
49 | * the localIndex, either by user action or programmaticly.
50 | */
51 | public static class OnPadModelSelectedBankChange {
52 | public OnPadModelSelectedBankChange() {
53 | }
54 | }
55 |
56 | public static class OnPadModelSelectedDataChange {
57 |
58 | private PadData data;
59 |
60 | private List view;
61 |
62 | public final PadData getData() {
63 | return data;
64 | }
65 |
66 | public List getView() {
67 | return view;
68 | }
69 |
70 | public OnPadModelSelectedDataChange(PadData data, List view) {
71 | this.data = data;
72 | this.view = view;
73 | }
74 |
75 | }
76 |
77 | public static class OnPadModelSelectedFunctionChange {
78 |
79 | private PadFunction function;
80 |
81 | public PadFunction getFunction() {
82 | return function;
83 | }
84 |
85 | public OnPadModelSelectedFunctionChange(PadFunction function) {
86 | this.function = function;
87 | }
88 | }
89 |
90 | //--------------------------------------------------------------------------
91 | // Enum API
92 | //--------------------------------------------------------------------------
93 |
94 | public enum PadFunction {
95 |
96 | PATTERN(0),
97 |
98 | ASSIGN(1);
99 |
100 | private int value;
101 |
102 | public final int getValue() {
103 | return value;
104 | }
105 |
106 | PadFunction(int value) {
107 | this.value = value;
108 | }
109 |
110 | public static PadFunction fromInt(int value) {
111 | for (PadFunction function : values()) {
112 | if (function.getValue() == value)
113 | return function;
114 | }
115 | return null;
116 | }
117 | }
118 |
119 | public enum PadDataState {
120 | IDLE,
121 |
122 | SELECTED,
123 |
124 | QUEUED,
125 |
126 | UNQUEUED;
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/CausticLiveCore/src/main/java/com/teotigraphix/causticlive/model/PadMap.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import java.util.ArrayList;
5 | import java.util.Collection;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.TreeMap;
9 |
10 | import com.teotigraphix.caustk.controller.ICaustkController;
11 | import com.teotigraphix.caustk.service.ISerialize;
12 |
13 | /*
14 |
15 | The PadMap holds all banks and patterns of the pad view.
16 |
17 | The PadData is the sate of the IPadModel that gets serialized.
18 |
19 |
20 |
21 | */
22 |
23 | public class PadMap implements ISerialize {
24 |
25 | private Map map = new TreeMap();
26 |
27 | private transient ICaustkController controller;
28 |
29 | public PadMap(ICaustkController controller) {
30 | this.controller = controller;
31 | }
32 |
33 | @Override
34 | public void sleep() {
35 | }
36 |
37 | @Override
38 | public void wakeup(ICaustkController controller) {
39 | this.controller = controller;
40 | for (PadData data : map.values()) {
41 | data.wakeup(controller);
42 | }
43 | }
44 |
45 | /**
46 | * Initializes the pad map with all banks and pads.
47 | *
48 | * This method is not called when deserializing, since the data already
49 | * exists.
50 | *
51 | * Only needed when creating new songs or projects.
52 | *
53 | * @param banks The number of bank buttons.
54 | * @param pads The number of pad buttons.
55 | */
56 | public void initialize(int banks, int pads) {
57 | int index = 0;
58 | for (int bank = 0; bank < banks; bank++) {
59 | for (int localIndex = 0; localIndex < pads; localIndex++) {
60 | PadData data = new PadData(index, bank, localIndex);
61 | data.wakeup(controller);
62 | map.put(index, data);
63 | index++;
64 | }
65 | }
66 | }
67 |
68 | /**
69 | * Creates a channel, if one exists, the {@link PadChannel} will be
70 | * returned.
71 | *
72 | * @param bank
73 | * @param localIndex
74 | * @param toneIndex The index of the channel (0-13) for 14 machines in the
75 | * core.
76 | */
77 | public PadChannel createChannel(int bank, int localIndex, int toneIndex) {
78 | PadData pad = getPad(bank, localIndex);
79 | PadChannel channel = pad.createChannel(toneIndex);
80 | return channel;
81 | }
82 |
83 | /**
84 | * Returns the {@link PadData} at the index within the whole model.
85 | *
86 | * @param index The full pad index bank * localIndex.
87 | */
88 | public PadData getPad(int index) {
89 | return map.get(index);
90 | }
91 |
92 | /**
93 | * Returns the {@link PadData} at the localIndex within a bank.
94 | *
95 | * @param bank The bank index.
96 | * @param localIndex The local index.
97 | */
98 | public PadData getPad(int bank, int localIndex) {
99 | for (PadData data : map.values()) {
100 | if (data.getBank() == bank && data.getLocalIndex() == localIndex)
101 | return data;
102 | }
103 | return null;
104 | }
105 |
106 | public List getPads(int bank) {
107 | List result = new ArrayList();
108 | for (PadData data : map.values()) {
109 | if (data.getBank() == bank)
110 | result.add(data);
111 | }
112 | return result;
113 | }
114 |
115 | public Collection getPads() {
116 | return map.values();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/java/com/teotigraphix/causticlive/config/ApplicationModule.java:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////////////////////////////
2 | // Copyright 2013 Michael Schmalle - Teoti Graphix, LLC
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License
15 | //
16 | // Author: Michael Schmalle, Principal Architect
17 | // mschmalle at teotigraphix dot com
18 | ////////////////////////////////////////////////////////////////////////////////
19 |
20 | package com.teotigraphix.causticlive.config;
21 |
22 | import java.io.File;
23 | import java.util.ResourceBundle;
24 |
25 | import com.google.inject.Singleton;
26 | import com.google.inject.name.Names;
27 | import com.teotigraphix.caustic.config.JavaFXRuntimeModule;
28 | import com.teotigraphix.causticlive.CausticLiveApplication;
29 | import com.teotigraphix.causticlive.model.ChannelModel;
30 | import com.teotigraphix.causticlive.model.IChannelModel;
31 | import com.teotigraphix.causticlive.model.IPadMapModel;
32 | import com.teotigraphix.causticlive.model.IPadModel;
33 | import com.teotigraphix.causticlive.model.ISoundModel;
34 | import com.teotigraphix.causticlive.model.PadMapModel;
35 | import com.teotigraphix.causticlive.model.PadModel;
36 | import com.teotigraphix.causticlive.model.SoundModel;
37 | import com.teotigraphix.caustk.application.CaustkConfigurationBase;
38 | import com.teotigraphix.caustk.application.ICaustkConfiguration;
39 | import com.teotigraphix.caustk.controller.ICaustkController;
40 | import com.teotigraphix.caustk.sound.DesktopSoundGenerator;
41 | import com.teotigraphix.caustk.sound.ISoundGenerator;
42 | import com.teotigraphix.caustk.utils.RuntimeUtils;
43 |
44 | public class ApplicationModule extends JavaFXRuntimeModule {
45 |
46 | @Override
47 | protected void configureApplicationRequirements() {
48 | // Config
49 | bind(ICaustkConfiguration.class).to(ApplicationConfiguration.class).in(Singleton.class);
50 |
51 | // Binds our resource bundle that contains localized Strings
52 | bind(ResourceBundle.class).annotatedWith(Names.named("resources")).toInstance(
53 | ResourceBundle.getBundle(CausticLiveApplication.class.getName()));
54 |
55 | // Application
56 | bind(IPadModel.class).to(PadModel.class).in(Singleton.class);
57 | bind(IPadMapModel.class).to(PadMapModel.class).in(Singleton.class);
58 | bind(ISoundModel.class).to(SoundModel.class).in(Singleton.class);
59 | bind(IChannelModel.class).to(ChannelModel.class).in(Singleton.class);
60 | }
61 |
62 | public static class ApplicationConfiguration extends CaustkConfigurationBase {
63 |
64 | private DesktopSoundGenerator generator;
65 |
66 | @Override
67 | public String getApplicationId() {
68 | return "causticlive";
69 | }
70 |
71 | @Override
72 | public void setCausticStorage(File value) {
73 | super.setCausticStorage(value);
74 | RuntimeUtils.STORAGE_ROOT = value.getAbsolutePath();
75 | }
76 |
77 | public ApplicationConfiguration() {
78 | generator = new DesktopSoundGenerator();
79 | }
80 |
81 | @Override
82 | public ISoundGenerator createSoundGenerator(ICaustkController controller) {
83 | return new DesktopSoundGenerator();
84 | }
85 |
86 | @Override
87 | public void setSoundGenerator(ISoundGenerator soundGenerator) {
88 | // TODO Auto-generated method stub
89 |
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/CausticLiveCore/src/main/java/com/teotigraphix/causticlive/model/PadModel.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import com.google.inject.Inject;
9 | import com.google.inject.Singleton;
10 | import com.teotigraphix.caustic.model.ICaustkModelState;
11 | import com.teotigraphix.caustic.model.ModelBase;
12 | import com.teotigraphix.caustk.controller.ICaustkController;
13 |
14 | @Singleton
15 | public class PadModel extends ModelBase implements IPadModel {
16 |
17 | @Inject
18 | IPadMapModel padMapModel;
19 |
20 | @Override
21 | protected PadModelState getState() {
22 | return (PadModelState)super.getState();
23 | }
24 |
25 | @Override
26 | public PadData getLocalData(int index) {
27 | return padMapModel.getPad(getState().getSelectedBank(), index);
28 | }
29 |
30 | @Override
31 | public PadData getSelectedData() {
32 | return padMapModel.getPad(getState().getSelectedBank(), getState().getSelectedIndex());
33 | }
34 |
35 | /**
36 | * Returns the {@link PadData} instances that are in the selected bank.
37 | */
38 | @Override
39 | public List getPadDataView() {
40 | return padMapModel.getPads(getState().getSelectedBank());
41 | }
42 |
43 | public PadFunction getSelectedFunction() {
44 | return getState().getSelectedFunction();
45 | }
46 |
47 | public void setSelectedFunction(PadFunction value) {
48 | getState().setSelectedFunction(value);
49 | trigger(new OnPadModelSelectedFunctionChange(value));
50 | }
51 |
52 | public PadModel() {
53 | setStateFactory(PadModelState.class);
54 | }
55 |
56 | @Override
57 | protected void configState(ICaustkModelState state) {
58 | getState().selectedIndex.put(0, 0);
59 | getState().selectedIndex.put(1, 0);
60 | getState().selectedIndex.put(2, 0);
61 | getState().selectedIndex.put(3, 0);
62 | }
63 |
64 | @Override
65 | public void onRegister() {
66 | }
67 |
68 | @Override
69 | public void onShow() {
70 | setSelectedFunction(getSelectedFunction());
71 | select(getState().getSelectedBank(), getState().getSelectedIndex());
72 | }
73 |
74 | @Override
75 | public void select(int bank) {
76 | getState().setSelectedBank(bank);
77 | //getDispatcher().trigger(
78 | // new OnPadModelSelectedDataChange(getSelectedData(), getPadDataView()));
79 | trigger(new OnPadModelSelectedBankChange());
80 | }
81 |
82 | @Override
83 | public void select(int bank, int localIndex) {
84 | getState().setSelectedBank(bank);
85 | getState().setSelectedIndex(localIndex);
86 | getDispatcher().trigger(
87 | new OnPadModelSelectedDataChange(getSelectedData(), getPadDataView()));
88 | }
89 |
90 | public static class PadModelState implements ICaustkModelState {
91 |
92 | int selectedBank = 0;
93 |
94 | public int getSelectedBank() {
95 | return selectedBank;
96 | }
97 |
98 | public void setSelectedBank(int value) {
99 | selectedBank = value;
100 | }
101 |
102 | Map selectedIndex = new HashMap();
103 |
104 | public int getSelectedIndex() {
105 | return selectedIndex.get(selectedBank);
106 | }
107 |
108 | public void setSelectedIndex(int value) {
109 | selectedIndex.put(selectedBank, value);
110 | }
111 |
112 | private PadFunction selectedFunction = PadFunction.PATTERN;
113 |
114 | public PadFunction getSelectedFunction() {
115 | return selectedFunction;
116 | }
117 |
118 | public void setSelectedFunction(PadFunction value) {
119 | selectedFunction = value;
120 | }
121 |
122 | public PadModelState() {
123 | }
124 |
125 | @Override
126 | public void sleep() {
127 | }
128 |
129 | @Override
130 | public void wakeup(ICaustkController controller) {
131 | }
132 |
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/test/java/com/teotigraphix/causticlive/model/PadDataTest.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import static org.junit.Assert.assertEquals;
5 | import static org.junit.Assert.assertSame;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 |
10 | import org.junit.Test;
11 |
12 | import com.teotigraphix.caustk.CaustkTestBase;
13 | import com.teotigraphix.caustk.core.CausticException;
14 | import com.teotigraphix.caustk.library.Library;
15 | import com.teotigraphix.caustk.library.LibraryPhrase;
16 | import com.teotigraphix.caustk.sequencer.ISystemSequencer.SequencerMode;
17 | import com.teotigraphix.caustk.sequencer.Track;
18 | import com.teotigraphix.caustk.sequencer.TrackSong;
19 | import com.teotigraphix.caustk.sound.ISoundSource;
20 | import com.teotigraphix.caustk.tone.BeatboxTone;
21 | import com.teotigraphix.caustk.tone.ModularTone;
22 | import com.teotigraphix.caustk.tone.PCMSynthTone;
23 | import com.teotigraphix.caustk.tone.PadSynthTone;
24 | import com.teotigraphix.caustk.tone.SubSynthTone;
25 | import com.teotigraphix.caustk.tone.Tone;
26 |
27 | public class PadDataTest extends CaustkTestBase {
28 |
29 | private ISoundSource soundSource;
30 |
31 | private Tone track0;
32 |
33 | private Tone track3;
34 |
35 | private Tone track5;
36 |
37 | private Tone track8;
38 |
39 | private Tone track13;
40 |
41 | private PadMap map;
42 |
43 | private Library library;
44 |
45 | @Override
46 | protected void start() throws CausticException, IOException {
47 | soundSource = controller.getSoundSource();
48 |
49 | track0 = soundSource.createTone(0, "track0", SubSynthTone.class);
50 | track3 = soundSource.createTone(3, "track3", PCMSynthTone.class);
51 | track5 = soundSource.createTone(5, "track5", ModularTone.class);
52 | track8 = soundSource.createTone(8, "track8", BeatboxTone.class);
53 | track13 = soundSource.createTone(13, "track13", PadSynthTone.class);
54 |
55 | map = new PadMap(controller);
56 | map.initialize(4, 16);
57 |
58 | File libraryFile = new File("src/test/resources/libraries/PadDataTest");
59 | library = controller.getLibraryManager().loadLibrary(libraryFile);
60 |
61 | }
62 |
63 | @Override
64 | protected void end() {
65 |
66 | }
67 |
68 | /*
69 | * - Load library
70 | * - Put model into ASSIGN state
71 | * - Select Pad
72 | * - Handle button action
73 | * - Show pad assignment popup
74 | * - popup.setPadData(padMap.getData(bank, localIndex)
75 | *
76 | *
77 | *
78 | *
79 | */
80 |
81 | @Test
82 | public void test_setPhrase() throws IOException, CausticException {
83 | LibraryPhrase libraryPhrase = library.getPhrases().get(0);
84 | PadData pad = map.getPad(3, 4);
85 | // get the channel for the PadSynth
86 | PadChannel channel = pad.getChannel(track13.getIndex());
87 |
88 | channel.assignPhrase(libraryPhrase);
89 |
90 | File songFile = new File("C:/Users/Work/Documents/git/CausticLive/CausticLiveFX/src/test/resources/PadDataTestSong.ctks");
91 | TrackSong song = controller.getSongManager().create(songFile);
92 | song.setNumTracks(14);
93 | Track track = song.getTrack(13);
94 | track.addPhrase(10, channel.getChannelPhrase());
95 | controller.getSongManager().save();
96 |
97 | // assert the trackItemId is the same as the channel phraseId
98 | assertEquals(track.getTrackItem(0).getPhraseId(), channel.getChannelPhrase().getId());
99 |
100 | String prettyString1 = controller.getSerializeService().toPrettyString(pad);
101 | String prettyString2 = controller.getSerializeService().toPrettyString(song);
102 |
103 | controller.getSystemSequencer().play(SequencerMode.SONG);
104 | }
105 |
106 | @Test
107 | public void test_getChannel() {
108 |
109 | PadData pad = map.getPad(3, 4);
110 | // get the channel for the PadSynth
111 | PadChannel channel = pad.getChannel(track13.getIndex());
112 |
113 | assertSame(track13, channel.getTone());
114 | assertEquals(3, channel.getBankIndex());
115 | assertEquals(4, channel.getPatternIndex());
116 | assertEquals(13, channel.getIndex());
117 |
118 | ///--------------------------
119 |
120 | String string = controller.getSerializeService().toPrettyString(map);
121 |
122 | PadMap map2 = controller.getSerializeService().fromString(string, PadMap.class);
123 | String string2 = controller.getSerializeService().toPrettyString(map2);
124 | assertEquals(string, string2);
125 |
126 | PadChannel channel2 = map2.getPad(0, 0).getChannel(13);
127 |
128 | assertSame(track13, channel2.getTone());
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/CausticLiveCore/src/main/java/com/teotigraphix/causticlive/model/PadData.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.TreeMap;
8 |
9 | import com.teotigraphix.causticlive.model.IPadModel.PadDataState;
10 | import com.teotigraphix.caustk.controller.ICaustkController;
11 | import com.teotigraphix.caustk.service.ISerialize;
12 |
13 | public class PadData implements ISerialize {
14 |
15 | private transient ICaustkController controller;
16 |
17 | private Map map = new TreeMap();
18 |
19 | //----------------------------------
20 | // index
21 | //----------------------------------
22 |
23 | private int index;
24 |
25 | public final int getIndex() {
26 | return index;
27 | }
28 |
29 | //----------------------------------
30 | // bank
31 | //----------------------------------
32 |
33 | private int bank;
34 |
35 | public final int getBank() {
36 | return bank;
37 | }
38 |
39 | //----------------------------------
40 | // localIndex
41 | //----------------------------------
42 |
43 | private int localIndex;
44 |
45 | public final int getLocalIndex() {
46 | return localIndex;
47 | }
48 |
49 | //----------------------------------
50 | // state
51 | //----------------------------------
52 |
53 | private PadDataState state = PadDataState.IDLE;
54 |
55 | public final PadDataState getState() {
56 | return state;
57 | }
58 |
59 | public void setState(PadDataState value) {
60 | state = value;
61 | }
62 |
63 | //----------------------------------
64 | // state
65 | //----------------------------------
66 |
67 | private Integer viewChannel = null;
68 |
69 | /**
70 | * Returns the {@link PadChannel} index that is considered the top view.
71 | */
72 | public int getViewChannel() {
73 | if (viewChannel == null) {
74 | for (PadChannel channel : map.values()) {
75 | return channel.getIndex();
76 | }
77 | viewChannel = 0;
78 | }
79 | return viewChannel;
80 | }
81 |
82 | public void setViewChannel(int viewChannel) {
83 | this.viewChannel = viewChannel;
84 | }
85 |
86 | //--------------------------------------------------------------------------
87 | // Constructor
88 | //--------------------------------------------------------------------------
89 |
90 | public PadData(int index, int bank, int localIndex) {
91 | this.index = index;
92 | this.bank = bank;
93 | this.localIndex = localIndex;
94 | }
95 |
96 | /**
97 | * Creates a channel, if one exists, the {@link PadChannel} will be
98 | * returned.
99 | *
100 | * @param index The index of the channel (0-13) for 14 machines in the core.
101 | */
102 | PadChannel createChannel(int toneIndex) {
103 | PadChannel channel = new PadChannel(toneIndex);
104 | channel.setPadData(this);
105 | channel.wakeup(controller);
106 | map.put(toneIndex, channel);
107 | return channel;
108 | }
109 |
110 | /**
111 | * Returns whether the pad contains a {@link PadChannel} at the tone index.
112 | *
113 | * @param toneIndex The toneIndex to test.
114 | */
115 | public boolean hasChannel(int toneIndex) {
116 | return map.containsKey(toneIndex);
117 | }
118 |
119 | /**
120 | * Returns a {@link PadChannel} at the toneIndex, will return
121 | * null if the channel has not been created.
122 | *
123 | * @param toneIndex The toneIndex to retrieve.
124 | */
125 | public PadChannel findChannel(int toneIndex) {
126 | return map.get(toneIndex);
127 | }
128 |
129 | /**
130 | * Returns a {@link PadChannel} at the tone index, this method will create
131 | * the channel if it does not exist.
132 | *
133 | * @param toneIndex The toneIndex to retrieve.
134 | */
135 | public PadChannel getChannel(int toneIndex) {
136 | PadChannel channel = map.get(toneIndex);
137 | if (channel == null) {
138 | channel = createChannel(toneIndex);
139 | }
140 | return channel;
141 | }
142 |
143 | @Override
144 | public void sleep() {
145 | }
146 |
147 | @Override
148 | public void wakeup(ICaustkController controller) {
149 | this.controller = controller;
150 | for (PadChannel channel : map.values()) {
151 | channel.setPadData(this);
152 | channel.wakeup(controller);
153 | }
154 | }
155 |
156 | @Override
157 | public String toString() {
158 | return "Data[" + bank + "," + localIndex + "]";
159 | }
160 |
161 | public List getChannels() {
162 | return new ArrayList<>(map.values());
163 | }
164 |
165 | public boolean hasChannels() {
166 | return map.size() > 0;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/resources/com/teotigraphix/causticlive/view/AssignmentScreen.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/resources/com/teotigraphix/causticlive/view/MachineScreen.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/java/com/teotigraphix/causticlive/mediator/main/ToolBarMediator.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.mediator.main;
3 |
4 | import java.io.File;
5 |
6 | import javafx.beans.value.ChangeListener;
7 | import javafx.beans.value.ObservableValue;
8 | import javafx.event.ActionEvent;
9 | import javafx.event.EventHandler;
10 | import javafx.scene.Node;
11 | import javafx.scene.control.Button;
12 | import javafx.scene.control.ToggleButton;
13 | import javafx.scene.input.MouseEvent;
14 | import javafx.scene.layout.Pane;
15 | import javafx.stage.DirectoryChooser;
16 |
17 | import com.google.inject.Inject;
18 | import com.teotigraphix.caustic.mediator.DesktopMediatorBase;
19 | import com.teotigraphix.caustic.screen.IScreenManager;
20 | import com.teotigraphix.caustic.utils.FileUtil;
21 | import com.teotigraphix.causticlive.model.IPadModel;
22 | import com.teotigraphix.causticlive.model.ISoundModel;
23 | import com.teotigraphix.causticlive.screen.MachineScreenView;
24 |
25 | public class ToolBarMediator extends DesktopMediatorBase {
26 |
27 | private Button machineViewButton;
28 |
29 | private Pane machineToggleButtons;
30 |
31 | //
32 |
33 | @Inject
34 | IScreenManager screenManager;
35 |
36 | @Inject
37 | ISoundModel soundModel;
38 |
39 | @Inject
40 | IPadModel padModel;
41 |
42 | //private boolean reseting;
43 |
44 | private Button loadButton;
45 |
46 | @Override
47 | public void create(Pane root) {
48 |
49 | loadButton = (Button)root.lookup("#loadButton");
50 | loadButton.setOnAction(new EventHandler() {
51 | @Override
52 | public void handle(ActionEvent event) {
53 | onLoadClick();
54 | }
55 | });
56 |
57 | machineViewButton = (Button)root.lookup("#machineViewButton");
58 | machineViewButton.setOnAction(new EventHandler() {
59 | @Override
60 | public void handle(ActionEvent event) {
61 | onMachineViewClick();
62 | }
63 | });
64 |
65 | // selectors on the main screen for the current assignment tone
66 | machineToggleButtons = (Pane)root.lookup("#machineToggleButtons");
67 | for (Node node : machineToggleButtons.getChildren()) {
68 | final ToggleButton button = (ToggleButton)node;
69 | button.getProperties().put("index", machineToggleButtons.getChildren().indexOf(button));
70 |
71 | button.selectedProperty().addListener(new ChangeListener() {
72 | @Override
73 | public void changed(ObservableValue extends Boolean> observable,
74 | Boolean oldValue, Boolean newValue) {
75 | onMachineButtonSelected(button);
76 | }
77 | });
78 |
79 | // XXX have to add KeyEvent for space, enter
80 | button.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler() {
81 | @Override
82 | public void handle(MouseEvent event) {
83 | if (button.isSelected())
84 | event.consume();
85 | }
86 | });
87 | }
88 |
89 | }
90 |
91 | protected void onLoadClick() {
92 | System.out.println("onLoadClick");
93 | // FileChooser chooser = FileUtil.createDefaultFileChooser(RuntimeUtils
94 | // .getCausticSongsDirectory().getAbsolutePath(), "Caustic song file", "*.caustic");
95 | // File causticFile = chooser.showOpenDialog(null);
96 | //
97 | // // if no current library open error dialog
98 | // Library library = getController().getLibraryManager().getSelectedLibrary();
99 | // try {
100 | // getController().getLibraryManager().importSong(library, causticFile);
101 | // } catch (IOException e) {
102 | // e.printStackTrace();
103 | // } catch (CausticException e) {
104 | // e.printStackTrace();
105 | // }
106 |
107 | DirectoryChooser chooser = FileUtil.createDefaultDirectoryChooser(null,
108 | "Choose library directory");
109 | File file = chooser.showDialog(null);
110 | if (file == null)
111 | return;
112 | soundModel.loadLibrary(file);
113 | }
114 |
115 | @Override
116 | public void onRegister() {
117 |
118 | }
119 |
120 | @Override
121 | protected void registerObservers() {
122 | super.registerObservers();
123 |
124 | // padModel.getDispatcher().register(OnPadModelAssignmentIndexChange.class,
125 | // new EventObserver() {
126 | // @Override
127 | // public void trigger(OnPadModelAssignmentIndexChange object) {
128 | // if (object.isNoSelection()) {
129 | // phraseList.setDisable(true);
130 | // return;
131 | // } else {
132 | // phraseList.setDisable(false);
133 | // onPadModelAssignmentIndexChange();
134 | // }
135 | // }
136 | // });
137 |
138 | }
139 |
140 | protected void onPadModelAssignmentIndexChange() {
141 | // if (soundModel.getLibrary() == null)
142 | // return;
143 | //
144 | // PadData data = padModel.getSelectedAssignmentData();
145 | // int toneIndex = data.getToneIndex();
146 | // //reseting = true;
147 | // for (Node node : machineToggleButtons.getChildren()) {
148 | // ToggleButton button = (ToggleButton)node;
149 | // button.disarm();
150 | // button.setSelected(false);
151 | // button.arm();
152 | // }
153 | // //reseting = false;
154 | //
155 | // LibraryPhrase phrase = soundModel.getLibrary().findPhraseById(data.getPhraseId());
156 | // if (phrase != null) {
157 | // phraseList.getSelectionModel().select(phrase);
158 | // phraseList.scrollTo(phraseList.getItems().indexOf(phrase));
159 | // }
160 | //
161 | // if (toneIndex != -1) {
162 | // ToggleButton button = (ToggleButton)machineToggleButtons.getChildren().get(toneIndex);
163 | // button.disarm();
164 | // button.setSelected(true);
165 | // button.arm();
166 | // }
167 | }
168 |
169 | protected void onMachineButtonSelected(ToggleButton button) {
170 | // if (reseting)
171 | // return;
172 | // // set the tone index
173 | // // this has to update the pad text view
174 | // int toneIndex = (int)button.getProperties().get("index");
175 | // padModel.setAssignmentToneIndex(toneIndex);
176 | }
177 |
178 | protected void onMachineViewClick() {
179 | screenManager.showPopUp(MachineScreenView.class);
180 | }
181 |
182 | }
183 |
--------------------------------------------------------------------------------
/CausticLiveCore/src/main/java/com/teotigraphix/causticlive/model/PadChannel.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import java.util.UUID;
5 |
6 | import com.teotigraphix.caustk.controller.ICaustkController;
7 | import com.teotigraphix.caustk.library.LibraryPhrase;
8 | import com.teotigraphix.caustk.pattern.PatternUtils;
9 | import com.teotigraphix.caustk.sequencer.ChannelPhrase;
10 | import com.teotigraphix.caustk.service.ISerialize;
11 | import com.teotigraphix.caustk.tone.Tone;
12 |
13 | /*
14 |
15 | 14 machines
16 |
17 | 14 * 4 * 16 = 896 possible patterns
18 |
19 | A channel is basically a concrete pattern_sequencer location within a machine.
20 |
21 | 64 possible pattern locations in a machine, we have 64 pads
22 | - each PadData bank & localIndex DIRECTLY corresponds to the machines pattern bank
23 |
24 | - A channel is assigned a phraseId
25 |
26 | Channel;
27 | - tone index
28 | - sequencer bank index
29 | - sequencer pattern index
30 |
31 | */
32 |
33 | public class PadChannel implements ISerialize {
34 |
35 | private transient ICaustkController controller;
36 |
37 | //----------------------------------
38 | // padData
39 | //----------------------------------
40 |
41 | private transient PadData padData;
42 |
43 | /**
44 | * Returns the owner.
45 | */
46 | public PadData getPadData() {
47 | return padData;
48 | }
49 |
50 | public void setPadData(PadData padData) {
51 | this.padData = padData;
52 | }
53 |
54 | //----------------------------------
55 | // currentBeat
56 | //----------------------------------
57 |
58 | private int currentBeat;
59 |
60 | public int getCurrentBeat() {
61 | return currentBeat;
62 | }
63 |
64 | public void setCurrentBeat(int value) {
65 | currentBeat = value;
66 | }
67 |
68 | //----------------------------------
69 | // bankIndex
70 | //----------------------------------
71 |
72 | /**
73 | * Returns the pattern sequencer bank index this channel is assigned to.
74 | */
75 | public final int getBankIndex() {
76 | return padData.getBank();
77 | }
78 |
79 | //----------------------------------
80 | // patternIndex
81 | //----------------------------------
82 |
83 | /**
84 | * Returns the pattern sequencer pattern index this channel is assigned to.
85 | */
86 | public final int getPatternIndex() {
87 | return padData.getLocalIndex();
88 | }
89 |
90 | //----------------------------------
91 | // enabled
92 | //----------------------------------
93 |
94 | private boolean enabled;
95 |
96 | public boolean isEnabled() {
97 | return enabled;
98 | }
99 |
100 | public void setEnabled(boolean enabled) {
101 | this.enabled = enabled;
102 | }
103 |
104 | private int index;
105 |
106 | /**
107 | * Returns the index of the channel within it's {@link PadData} stack.
108 | *
109 | * This index represents the tone index assigned to the channel. Which is a
110 | * machine in the rack.
111 | */
112 | public int getIndex() {
113 | return index;
114 | }
115 |
116 | //----------------------------------
117 | // libraryId
118 | //----------------------------------
119 |
120 | private UUID ownerLibraryId;
121 |
122 | public UUID getOwnerLibraryId() {
123 | return ownerLibraryId;
124 | }
125 |
126 | //----------------------------------
127 | // phraseId
128 | //----------------------------------
129 |
130 | private UUID ownerPhraseId;
131 |
132 | public UUID getOwnerPhraseId() {
133 | return ownerPhraseId;
134 | }
135 |
136 | private ChannelPhrase channelPhrase;
137 |
138 | public ChannelPhrase getChannelPhrase() {
139 | return channelPhrase;
140 | }
141 |
142 | //----------------------------------
143 | // loopEnabled
144 | //----------------------------------
145 |
146 | private boolean loopEnabled = true;
147 |
148 | public boolean isLoopEnabled() {
149 | return loopEnabled;
150 | }
151 |
152 | public void setLoopEnabled(boolean value) {
153 | loopEnabled = value;
154 | }
155 |
156 | //--------------------------------------------------------------------------
157 | // Constructor
158 | //--------------------------------------------------------------------------
159 |
160 | /**
161 | * @param index The tone index.
162 | */
163 | public PadChannel(int index) {
164 | this.index = index;
165 | }
166 |
167 | @Override
168 | public void sleep() {
169 | }
170 |
171 | @Override
172 | public void wakeup(ICaustkController controller) {
173 | this.controller = controller;
174 |
175 | if (channelPhrase != null) {
176 | assignNoteData(channelPhrase);
177 | }
178 | }
179 |
180 | public Tone getTone() {
181 | return controller.getSoundSource().getTone(index);
182 | }
183 |
184 | public void assignPhrase(LibraryPhrase phrase) {
185 | if (phrase == null) {
186 | clearPhrase();
187 | return;
188 | }
189 | // save original the libraryId and phraseId if ever this
190 | // channel wants to try and reset its phrase data
191 | // but we copy the whole thing so there is no dependencies
192 | // between this project and library since they could get disconnected
193 | ownerLibraryId = phrase.getLibrary().getId();
194 | ownerPhraseId = phrase.getId();
195 | LibraryPhrase newPhrase = controller.getSerializeService()
196 | .copy(phrase, LibraryPhrase.class);
197 |
198 | // update the decoration to hold our position
199 | // the original ID from the library can always get the original bank/pattern locations
200 | newPhrase.setId(UUID.randomUUID());
201 | newPhrase.setBankIndex(getBankIndex());
202 | newPhrase.setPatternIndex(getPatternIndex());
203 |
204 | channelPhrase = new ChannelPhrase(newPhrase);
205 |
206 | assignNoteData(channelPhrase);
207 | }
208 |
209 | private void assignNoteData(ChannelPhrase channelPhrase) {
210 | if (getTone() == null)
211 | return;
212 | String data = channelPhrase.getNoteData();
213 | int oldBank = getTone().getPatternSequencer().getSelectedBank();
214 | int oldPattern = getTone().getPatternSequencer().getSelectedIndex();
215 | getTone().getPatternSequencer().setSelectedPattern(getBankIndex(), getPatternIndex());
216 | getTone().getPatternSequencer().setLength(channelPhrase.getLength());
217 | getTone().getPatternSequencer().initializeData(data);
218 | getTone().getPatternSequencer().setSelectedPattern(oldBank, oldPattern);
219 | }
220 |
221 | private void clearPhrase() {
222 | ownerLibraryId = null;
223 | ownerPhraseId = null;
224 | if (channelPhrase != null) {
225 | // remove note data from machines pattern sequencer
226 | getTone().getPatternSequencer().clearIndex(getBankIndex(), getPatternIndex());
227 | }
228 | channelPhrase = null;
229 | }
230 |
231 | public String getPatternName() {
232 | return PatternUtils.toString(getBankIndex(), getPatternIndex());
233 | }
234 |
235 | @Override
236 | public String toString() {
237 | return "Channel[" + getIndex() + PatternUtils.toString(getBankIndex(), getPatternIndex())
238 | + "]";
239 | }
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/test/java/com/teotigraphix/causticlive/model/SongPlayerTest.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import static org.junit.Assert.*;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 |
9 | import org.junit.Test;
10 |
11 | import com.teotigraphix.causticlive.model.IPadModel.PadDataState;
12 | import com.teotigraphix.caustk.CaustkTestBase;
13 | import com.teotigraphix.caustk.core.CausticException;
14 | import com.teotigraphix.caustk.core.osc.RackMessage;
15 | import com.teotigraphix.caustk.library.Library;
16 | import com.teotigraphix.caustk.sequencer.TrackSong;
17 | import com.teotigraphix.caustk.utils.RuntimeUtils;
18 |
19 | public class SongPlayerTest extends CaustkTestBase {
20 |
21 | private SongPlayer player;
22 |
23 | private TrackSong trackSong;
24 |
25 | private MockPadMapModel model;
26 |
27 | private PadData A01;
28 |
29 | private PadData A02;
30 |
31 | private PadData B04;
32 |
33 | private PadData D12;
34 |
35 | private Library library;
36 |
37 | @Override
38 | protected void start() throws CausticException, IOException {
39 |
40 | File libraryFile = new File("src/test/resources/libraries/PadDataTest");
41 | library = controller.getLibraryManager().loadLibrary(libraryFile);
42 |
43 | controller.getSoundSource().createScene(library.getScenes().get(1));
44 |
45 | model = new MockPadMapModel(controller, 4, 16);
46 |
47 | trackSong = controller.getSongManager().create(new File("SongPlayerTestSong.ctks"));
48 | trackSong.setNumTracks(14);
49 |
50 | player = new SongPlayer(controller);
51 | player.setPadMap(model);
52 | player.setSong(trackSong);
53 |
54 | A01 = player.getPadMap().getPad(0, 0);
55 | A01.getChannel(0).setLoopEnabled(false);
56 | A01.getChannel(0).assignPhrase(library.getPhrases().get(0));
57 |
58 | A02 = player.getPadMap().getPad(0, 1);
59 | A02.getChannel(1).assignPhrase(library.findPhrasesByTag("length-2").get(0));
60 |
61 | B04 = player.getPadMap().getPad(1, 3);
62 | B04.getChannel(2).assignPhrase(library.getPhrases().get(2));
63 |
64 | D12 = player.getPadMap().getPad(3, 11);
65 | D12.getChannel(3).assignPhrase(library.getPhrases().get(3));
66 | }
67 |
68 | @Override
69 | protected void end() {
70 | player = null;
71 | }
72 |
73 | @Test
74 | public void test_init() throws CausticException {
75 | // we have to do this so the nextBeat() puts the beat at 0
76 | player.play();
77 |
78 | int numMeasures = 120;
79 | int beat = 0;
80 | for (int i = 0; i < numMeasures; i++) {
81 | for (int j = 0; j < 4; j++) {
82 | playAssert(i, beat);
83 | beat++;
84 | }
85 | }
86 | }
87 |
88 | // there was a bug where turning
89 | @Test
90 | public void test_queue_double_loop_on_off_on() throws CausticException {
91 | //int len1 = A01.getChannel(0).getChannelPhrase().getLength();
92 | //int len2 = A02.getChannel(1).getChannelPhrase().getLength();
93 | player.queue(A02); // length 1
94 | player.queue(B04); // length 2
95 | player.play();
96 | nextMeasure(1);
97 | assertQueuedSize(0);
98 | assertPlayQueueSize(2);
99 | nextMeasure(2);
100 | nextMeasure(3); // In the middle of A02
101 | player.unqueue(A02);
102 | assertQueuedSize(0);
103 | assertPlayQueueSize(2);
104 | nextMeasure(4); // A02 removed
105 | assertQueuedSize(0);
106 | //assertPlayQueueSize(1);
107 |
108 | // XXX THIS IS THE BUG, adding A02 back off it's beat
109 | player.queue(A02);
110 | nextMeasure(5);
111 | // instead of waiting for another measure since it's length is 2, the palyer inserts
112 | // a full two measure phrase in the middle of the existing, thus exception is thrown
113 | nextMeasure(6);
114 | nextMeasure(7);
115 | nextMeasure(8);
116 | RackMessage.SAVE_SONG.send(controller, "TestSeq");
117 | }
118 |
119 | @Test
120 | public void test_queue_single_loop_on_off() throws CausticException {
121 | A01.getChannel(0).setLoopEnabled(true);
122 | player.queue(A01);
123 | player.play(); // /caustic/sequencer/pattern_event 1 0 0 1 1
124 | nextMeasure(1); // /caustic/sequencer/pattern_event 1 1 0 1 2
125 | assertPosition(0, 3);
126 |
127 | assertQueuedSize(0);
128 | assertPlayQueueSize(1);
129 | player.unqueue(A01); // just dosn't add the pattern
130 | nextMeasure(2);
131 | assertPosition(1, 7);
132 |
133 | assertQueuedSize(0);
134 | assertPlayQueueSize(0);
135 |
136 | // put it back on
137 | player.queue(A01); // Queue:Data[0,1]
138 | assertQueuedSize(1);
139 | assertPlayQueueSize(0);
140 | nextMeasure(3); // /caustic/sequencer/pattern_event 1 2 0 1 3
141 | assertPosition(2, 11);
142 |
143 | assertQueuedSize(0);
144 | assertPlayQueueSize(1);
145 | }
146 |
147 | @Test
148 | public void test_queue_single_loop() throws CausticException {
149 | player.queue(A02);
150 | assertQueuedSize(1);
151 | assertPlayQueueSize(0);
152 | player.play();
153 | assertQueuedSize(0);
154 | assertPlayQueueSize(1);
155 | assertEquals(PadDataState.SELECTED, A02.getState());
156 | nextMeasure(1);
157 | // The channel loops, still in play queue
158 | assertQueuedSize(0);
159 | assertPlayQueueSize(1);
160 | assertEquals(PadDataState.SELECTED, A02.getState());
161 | nextMeasure(2); // /caustic/sequencer/pattern_event 1 2 0 1 3
162 | nextMeasure(3); // /caustic/sequencer/pattern_event 1 3 0 1 4
163 | nextMeasure(4); // /caustic/sequencer/pattern_event 1 4 0 1 5
164 | nextMeasure(5); // /caustic/sequencer/pattern_event 1 5 0 1 6
165 | assertQueuedSize(0);
166 | assertPlayQueueSize(1);
167 | assertEquals(PadDataState.SELECTED, A02.getState());
168 | }
169 |
170 | /**
171 | * @param measure The exact measure in the Caustic sequencer,
172 | */
173 | private void nextMeasure(int measure) {
174 | for (int i = 0; i < 4; i++) {
175 | player.beatChange(measure - 1, i);
176 | }
177 | }
178 |
179 | @Test
180 | public void test_queue_single_oneshot() throws CausticException {
181 | assertQueuedSize(0);
182 | assertPlayQueueSize(0);
183 |
184 | // we are not playing yet
185 | player.queue(A01);
186 |
187 | assertQueuedSize(1);
188 | assertPlayQueueSize(0);
189 |
190 | player.play();
191 |
192 | assertQueuedSize(0);
193 | assertPlayQueueSize(1);
194 | assertEquals(PadDataState.SELECTED, A01.getState());
195 |
196 | // run to the next measure
197 | nextMeasure(1);
198 | assertEquals(3, trackSong.getCurrentBeat());
199 |
200 | // The channel does not loop, one shot, so it was taken out
201 | assertQueuedSize(0);
202 | assertPlayQueueSize(0);
203 | assertEquals(PadDataState.IDLE, A01.getState());
204 | }
205 |
206 | private void assertQueuedSize(int size) {
207 | assertEquals(size, player.getQueued().size());
208 | }
209 |
210 | private void assertPlayQueueSize(int size) {
211 | assertEquals(size, player.getPlayQueue().size());
212 | }
213 |
214 | private void assertPosition(int measure, int beat) {
215 | assertEquals(beat, trackSong.getCurrentBeat());
216 | assertEquals(measure, trackSong.getCurrentMeasure());
217 | }
218 |
219 | private void playAssert(int measure, int beat) {
220 | player.beatChange(measure, beat);
221 | assertEquals(beat, trackSong.getCurrentBeat());
222 | assertEquals(measure, trackSong.getCurrentMeasure());
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/java/com/teotigraphix/causticlive/mediator/assignment/AssignmentControlsMediator.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.mediator.assignment;
3 |
4 | import java.util.List;
5 |
6 | import javafx.beans.value.ChangeListener;
7 | import javafx.beans.value.ObservableValue;
8 | import javafx.collections.FXCollections;
9 | import javafx.collections.ObservableList;
10 | import javafx.event.ActionEvent;
11 | import javafx.event.EventHandler;
12 | import javafx.scene.Node;
13 | import javafx.scene.control.Button;
14 | import javafx.scene.control.ListView;
15 | import javafx.scene.control.ToggleButton;
16 | import javafx.scene.input.MouseEvent;
17 | import javafx.scene.layout.Pane;
18 |
19 | import org.androidtransfuse.event.EventObserver;
20 |
21 | import com.google.inject.Inject;
22 | import com.google.inject.Singleton;
23 | import com.teotigraphix.caustic.mediator.DesktopMediatorBase;
24 | import com.teotigraphix.caustic.screen.IScreenManager;
25 | import com.teotigraphix.causticlive.model.IChannelModel;
26 | import com.teotigraphix.causticlive.model.IPadModel;
27 | import com.teotigraphix.causticlive.model.ISoundModel;
28 | import com.teotigraphix.causticlive.model.IChannelModel.OnChannelModelSelectedChannelChange;
29 | import com.teotigraphix.causticlive.model.ISoundModel.OnSoundModelLibraryImportComplete;
30 | import com.teotigraphix.causticlive.model.PadChannel;
31 | import com.teotigraphix.causticlive.model.PadData;
32 | import com.teotigraphix.causticlive.screen.AssignmentScreenView;
33 | import com.teotigraphix.causticlive.utils.ImageUtils;
34 | import com.teotigraphix.caustk.core.CtkDebug;
35 | import com.teotigraphix.caustk.library.ILibraryManager.OnLibraryManagerSelectedLibraryChange;
36 | import com.teotigraphix.caustk.library.Library;
37 | import com.teotigraphix.caustk.library.LibraryPhrase;
38 | import com.teotigraphix.caustk.pattern.PatternUtils;
39 | import com.teotigraphix.caustk.tone.Tone;
40 |
41 | @Singleton
42 | public class AssignmentControlsMediator extends DesktopMediatorBase {
43 |
44 | private Button backButton;
45 |
46 | @Inject
47 | ISoundModel soundModel;
48 |
49 | @Inject
50 | IPadModel padModel;
51 |
52 | @Inject
53 | IScreenManager screenManager;
54 |
55 | @Inject
56 | IChannelModel channelModel;
57 |
58 | private ListView phraseList;
59 |
60 | private Pane channelGroup;
61 |
62 | public AssignmentControlsMediator() {
63 | }
64 |
65 | @SuppressWarnings("unchecked")
66 | @Override
67 | public void create(Pane root) {
68 | root.visibleProperty().addListener(new ChangeListener() {
69 | @Override
70 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue,
71 | Boolean newValue) {
72 | if (observable.getValue()) {
73 | onShow();
74 | } else {
75 | onHide();
76 | }
77 | }
78 | });
79 |
80 | backButton = (Button)root.lookup("#backButton");
81 | backButton.setOnAction(new EventHandler() {
82 | @Override
83 | public void handle(ActionEvent event) {
84 | onBackClick();
85 | }
86 | });
87 |
88 | channelGroup = (Pane)root.lookup("#channelGroup");
89 |
90 | phraseList = (ListView)root.lookup("#phraseList");
91 | //phraseList.setDisable(true);
92 |
93 | // Grid
94 | int index = 0;
95 | for (Node node : channelGroup.getChildren()) {
96 | final ToggleButton button = (ToggleButton)node;
97 | button.getProperties().put("index", index);
98 | button.selectedProperty().addListener(new ChangeListener() {
99 | @Override
100 | public void changed(ObservableValue extends Boolean> observable,
101 | Boolean oldValue, Boolean newValue) {
102 | onChannelGroupSelected(button);
103 | }
104 | });
105 | // XXX have to add KeyEvent for space, enter
106 | button.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler() {
107 | @Override
108 | public void handle(MouseEvent event) {
109 | if (button.isSelected())
110 | event.consume();
111 | }
112 | });
113 | //button.setDisable(true);
114 | index++;
115 | }
116 | }
117 |
118 | protected void onMachineGroupSelected(ToggleButton button) {
119 | // Assign machine to channel
120 | //PadChannel channel = channelModel.getSelectedChannel();
121 | //channel.setEnabled(false);
122 | //onShow();
123 | }
124 |
125 | protected void onChannelGroupSelected(ToggleButton button) {
126 | if (!button.isSelected())
127 | return;
128 |
129 | int toneIndex = (int)button.getProperties().get("index");
130 | PadData selectedData = padModel.getSelectedData();
131 | PadChannel channel = selectedData.getChannel(toneIndex);
132 | channelModel.setSelectedChannel(channel);
133 | }
134 |
135 | protected void onHide() {
136 | CtkDebug.view("Hide");
137 | }
138 |
139 | protected void onShow() {
140 |
141 | // update controls based on selectedData
142 | PadData selectedData = padModel.getSelectedData();
143 | CtkDebug.view("Show;" + selectedData);
144 |
145 | List channleView = channelModel.getChannleView();
146 | // update each
147 | int toneIndex = 0;
148 | for (Node node : channelGroup.getChildren()) {
149 | ToggleButton button = (ToggleButton)node;
150 | PadChannel channel = selectedData.findChannel(toneIndex);
151 |
152 | Tone tone = getController().getSoundSource().getTone(toneIndex);
153 | if (tone != null) {
154 | // update image
155 | ImageUtils.assignMachineIcon(tone, button);
156 |
157 | if (channel != null) {
158 |
159 | // update label
160 | button.setText(tone.getName()
161 | + " "
162 | + PatternUtils.toString(channel.getBankIndex(),
163 | channel.getPatternIndex()));
164 | } else {
165 | // button.setGraphic(null);
166 | button.setText("Unassigned");
167 | }
168 | } else {
169 | button.setText("Empty");
170 | //button.setDisable(true);
171 | }
172 |
173 | toneIndex++;
174 | }
175 | }
176 |
177 | @Override
178 | protected void registerObservers() {
179 | super.registerObservers();
180 |
181 | channelModel.getDispatcher().register(OnChannelModelSelectedChannelChange.class,
182 | new EventObserver() {
183 | @Override
184 | public void trigger(OnChannelModelSelectedChannelChange object) {
185 | onChannelModelSelectedChannelChange(object.getChannel());
186 | }
187 | });
188 |
189 | soundModel.getDispatcher().register(OnSoundModelLibraryImportComplete.class,
190 | new EventObserver() {
191 | @Override
192 | public void trigger(OnSoundModelLibraryImportComplete object) {
193 | fillPhraseList();
194 | }
195 | });
196 |
197 | getController().getDispatcher().register(OnLibraryManagerSelectedLibraryChange.class,
198 | new EventObserver() {
199 | @Override
200 | public void trigger(OnLibraryManagerSelectedLibraryChange object) {
201 | fillPhraseList();
202 | }
203 | });
204 | }
205 |
206 | protected void onChannelModelSelectedChannelChange(PadChannel channel) {
207 |
208 | // update phrase selectedItem
209 |
210 | }
211 |
212 | @Override
213 | public void onRegister() {
214 | phraseList.getSelectionModel().selectedItemProperty()
215 | .addListener(new ChangeListener() {
216 | @Override
217 | public void changed(ObservableValue extends LibraryPhrase> value,
218 | LibraryPhrase arg1, LibraryPhrase arg2) {
219 | LibraryPhrase libraryPhrase = value.getValue();
220 | onPhraseListselectedItemChange(libraryPhrase);
221 | }
222 | });
223 |
224 | fillPhraseList();
225 | }
226 |
227 | private void fillPhraseList() {
228 | Library library = getController().getLibraryManager().getSelectedLibrary();
229 | if (library == null)
230 | return;
231 | List phrases = library.getPhrases();
232 | ObservableList items1 = FXCollections.observableArrayList(phrases);
233 | phraseList.setItems(items1);
234 | }
235 |
236 | protected void onBackClick() {
237 | screenManager.hidePopUp(AssignmentScreenView.class);
238 | }
239 |
240 | protected void onPhraseListselectedItemChange(LibraryPhrase libraryPhrase) {
241 | PadChannel channel = channelModel.getSelectedChannel();
242 | if (channel == null)
243 | return;
244 |
245 | channelModel.assignPhrase(channel, libraryPhrase);
246 |
247 | onShow();
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/java/com/teotigraphix/causticlive/mediator/machine/MachineScreenMediator.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.mediator.machine;
3 |
4 | import java.util.List;
5 |
6 | import javafx.beans.value.ChangeListener;
7 | import javafx.beans.value.ObservableValue;
8 | import javafx.collections.FXCollections;
9 | import javafx.collections.ObservableList;
10 | import javafx.event.ActionEvent;
11 | import javafx.event.EventHandler;
12 | import javafx.scene.Node;
13 | import javafx.scene.control.Button;
14 | import javafx.scene.control.ListView;
15 | import javafx.scene.control.ToggleButton;
16 | import javafx.scene.input.MouseEvent;
17 | import javafx.scene.layout.Pane;
18 |
19 | import org.androidtransfuse.event.EventObserver;
20 |
21 | import com.google.inject.Inject;
22 | import com.google.inject.Singleton;
23 | import com.teotigraphix.caustic.mediator.DesktopMediatorBase;
24 | import com.teotigraphix.caustic.screen.IScreenManager;
25 | import com.teotigraphix.causticlive.model.ISoundModel;
26 | import com.teotigraphix.causticlive.model.SoundModel.OnSoundModelLibrarySceneChange;
27 | import com.teotigraphix.causticlive.model.SoundModel.OnSoundModelSelectedToneChange;
28 | import com.teotigraphix.causticlive.model.SoundModel.ToneData;
29 | import com.teotigraphix.causticlive.screen.MachineScreenView;
30 | import com.teotigraphix.causticlive.utils.ImageUtils;
31 | import com.teotigraphix.caustk.library.ILibraryManager.OnLibraryManagerSelectedLibraryChange;
32 | import com.teotigraphix.caustk.library.Library;
33 | import com.teotigraphix.caustk.library.LibraryPatch;
34 | import com.teotigraphix.caustk.library.LibraryScene;
35 | import com.teotigraphix.caustk.tone.Tone;
36 |
37 | @Singleton
38 | public class MachineScreenMediator extends DesktopMediatorBase {
39 |
40 | private Button backButton;
41 |
42 | private Button loadSceneButton;
43 |
44 | private ListView sceneList;
45 |
46 | private ListView patchList;
47 |
48 | private Pane machineButtonPane;
49 |
50 | @Inject
51 | ISoundModel soundModel;
52 |
53 | @Inject
54 | IScreenManager screenManager;
55 |
56 | @SuppressWarnings("unchecked")
57 | @Override
58 | public void create(Pane root) {
59 |
60 | machineButtonPane = (Pane)root.lookup("#machineButtonPane");
61 |
62 | backButton = (Button)root.lookup("#backButton");
63 | backButton.setOnAction(new EventHandler() {
64 | @Override
65 | public void handle(ActionEvent event) {
66 | onBackClick();
67 | }
68 | });
69 |
70 | loadSceneButton = (Button)root.lookup("#loadSceneButton");
71 | loadSceneButton.setOnAction(new EventHandler() {
72 | @Override
73 | public void handle(ActionEvent event) {
74 | onLoadScene();
75 | }
76 | });
77 |
78 | sceneList = (ListView)root.lookup("#sceneList");
79 |
80 | patchList = (ListView)root.lookup("#patchList");
81 |
82 | patchList.getSelectionModel().selectedItemProperty()
83 | .addListener(new ChangeListener() {
84 | @Override
85 | public void changed(ObservableValue extends LibraryPatch> value,
86 | LibraryPatch arg1, LibraryPatch arg2) {
87 | LibraryPatch libraryPatch = value.getValue();
88 | if (libraryPatch != null) {
89 | boolean success = soundModel.setPatch(soundModel.getSelectedToneData(),
90 | libraryPatch);
91 | if (!success) {
92 | patchList.getSelectionModel().clearSelection(
93 | patchList.getSelectionModel().getSelectedIndex());
94 | }
95 | }
96 | }
97 | });
98 |
99 | int index = 0;
100 | for (Node node : machineButtonPane.getChildren()) {
101 | final ToggleButton button = (ToggleButton)node;
102 | button.getProperties().put("index", index);
103 | button.selectedProperty().addListener(new ChangeListener() {
104 | @Override
105 | public void changed(ObservableValue extends Boolean> observable,
106 | Boolean oldValue, Boolean newValue) {
107 | onMachineButtonSelected(button);
108 | }
109 | });
110 | // XXX have to add KeyEvent for space, enter
111 | button.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler() {
112 | @Override
113 | public void handle(MouseEvent event) {
114 | if (button.isSelected())
115 | event.consume();
116 | }
117 | });
118 | button.setDisable(true);
119 | index++;
120 | }
121 |
122 | }
123 |
124 | protected void onLoadScene() {
125 | LibraryScene item = sceneList.getSelectionModel().getSelectedItem();
126 | if (item == null) {
127 | return;
128 | }
129 |
130 | soundModel.loadScene(item, true);
131 | }
132 |
133 | protected void onBackClick() {
134 | screenManager.hidePopUp(MachineScreenView.class);
135 | }
136 |
137 | @Override
138 | public void onRegister() {
139 | fillLists();
140 | }
141 |
142 | protected void onMachineButtonSelected(ToggleButton button) {
143 | int toneIndex = (int)button.getProperties().get("index");
144 |
145 | if (button.isSelected()) {
146 | soundModel.setSelectedTone(toneIndex);
147 | }
148 | }
149 |
150 | @Override
151 | protected void registerObservers() {
152 | super.registerObservers();
153 |
154 | // SoundModel
155 |
156 | // OnSoundModelSceneLoad
157 | soundModel.getDispatcher().register(OnSoundModelLibrarySceneChange.class,
158 | new EventObserver() {
159 | @Override
160 | public void trigger(OnSoundModelLibrarySceneChange object) {
161 | updateMachineButtons();
162 | }
163 | });
164 |
165 | // OnSoundModelSelectedToneChange
166 | soundModel.getDispatcher().register(OnSoundModelSelectedToneChange.class,
167 | new EventObserver() {
168 | @Override
169 | public void trigger(OnSoundModelSelectedToneChange object) {
170 | updateSelectedTone(object);
171 | }
172 | });
173 |
174 | // OnSoundModelLibraryImportComplete
175 | // soundModel.getDispatcher().register(OnSoundModelLibraryImportComplete.class,
176 | // new EventObserver() {
177 | // @Override
178 | // public void trigger(OnSoundModelLibraryImportComplete object) {
179 | // fillLists();
180 | // }
181 | // });
182 |
183 | getController().getDispatcher().register(OnLibraryManagerSelectedLibraryChange.class,
184 | new EventObserver() {
185 | @Override
186 | public void trigger(OnLibraryManagerSelectedLibraryChange object) {
187 | fillLists();
188 | }
189 | });
190 | }
191 |
192 | protected void fillLists() {
193 | Library library = getController().getLibraryManager().getSelectedLibrary();
194 | if (library == null)
195 | return;
196 |
197 | List phrases = library.getScenes();
198 | ObservableList items1 = FXCollections.observableArrayList(phrases);
199 | sceneList.setItems(items1);
200 |
201 | List patches = library.getPatches();
202 | ObservableList items2 = FXCollections.observableArrayList(patches);
203 | patchList.setItems(items2);
204 | }
205 |
206 | protected void updateSelectedTone(OnSoundModelSelectedToneChange object) {
207 | // set the selection in the patch list
208 | ToneData data = object.getTone();
209 | // clear selection
210 | if (data == null)
211 | return;
212 |
213 | // update tone button
214 | // XXX this is causing recursion
215 | ToggleButton button = (ToggleButton)machineButtonPane.getChildren()
216 | .get(data.getToneIndex());
217 | if (!button.isSelected())
218 | button.setSelected(true);
219 | if (soundModel.getLibrary() == null)
220 | return;
221 | LibraryPatch libraryPatch = soundModel.getLibrary().findPatchById(data.getPatchId());
222 | if (libraryPatch == null) {
223 | patchList.getSelectionModel().clearSelection();
224 | return;
225 | }
226 |
227 | patchList.getSelectionModel().select(libraryPatch);
228 | }
229 |
230 | /**
231 | * Updates the machine button when a new {@link LibraryScene} is loaded.
232 | */
233 | protected void updateMachineButtons() {
234 | sceneList.getSelectionModel().select(soundModel.getLibraryScene());
235 |
236 | for (Node node : machineButtonPane.getChildren()) {
237 | ToggleButton button = (ToggleButton)node;
238 | String name = "";
239 | button.setDisable(true);
240 | Tone tone = getController().getSoundSource().getTone(
241 | (int)button.getProperties().get("index"));
242 | if (tone != null) {
243 | name = tone.getName();
244 | button.setDisable(false);
245 | ImageUtils.assignMachineIcon(tone, button);
246 | }
247 | button.setText(name);
248 |
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/CausticLiveCore/src/main/java/com/teotigraphix/causticlive/model/SongPlayer.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import com.teotigraphix.causticlive.model.IPadModel.PadDataState;
8 | import com.teotigraphix.caustk.controller.ICaustkController;
9 | import com.teotigraphix.caustk.core.CausticException;
10 | import com.teotigraphix.caustk.core.CtkDebug;
11 | import com.teotigraphix.caustk.sequencer.ChannelPhrase;
12 | import com.teotigraphix.caustk.sequencer.Track;
13 | import com.teotigraphix.caustk.sequencer.TrackItem;
14 | import com.teotigraphix.caustk.sequencer.TrackSong;
15 | import com.teotigraphix.caustk.sequencer.ISystemSequencer.SequencerMode;
16 | import com.teotigraphix.caustk.tone.Tone;
17 |
18 | public class SongPlayer {
19 |
20 | private List flushedQueue = new ArrayList();
21 |
22 | private List playQueue = new ArrayList();
23 |
24 | List getPlayQueue() {
25 | return playQueue;
26 | }
27 |
28 | private List queued = new ArrayList();
29 |
30 | List getQueued() {
31 | return queued;
32 | }
33 |
34 | private TrackSong song;
35 |
36 | private ICaustkController controller;
37 |
38 | private IPadMap padMap;
39 |
40 | private int currentLocalBeat;
41 |
42 | public IPadMap getPadMap() {
43 | return padMap;
44 | }
45 |
46 | public void setPadMap(IPadMap value) {
47 | padMap = value;
48 | }
49 |
50 | public TrackSong getSong() {
51 | return song;
52 | }
53 |
54 | public void setSong(TrackSong value) {
55 | song = value;
56 | }
57 |
58 | public SongPlayer(ICaustkController controller) {
59 | this.controller = controller;
60 | }
61 |
62 | public void beatChange(int measure, int beat) {
63 | getSong().nextBeat();
64 |
65 | //CtkDebug.model(">> Beat:" + beat + ", Measure:" + measure);
66 |
67 | currentLocalBeat = beat % 4;
68 |
69 | // start new measure
70 | if (currentLocalBeat == 0) {
71 | for (PadData data : flushedQueue) {
72 | if (data.getState() != PadDataState.QUEUED) {
73 | data.setState(PadDataState.IDLE);
74 | PadChannel channel = data.getChannel(data.getViewChannel());
75 | channel.setCurrentBeat(0);
76 | }
77 |
78 | for (PadChannel channel : data.getChannels()) {
79 | Tone tone = controller.getSoundSource().getTone(channel.getIndex());
80 | tone.setMuted(true);
81 | }
82 | }
83 |
84 | flushedQueue.clear();
85 |
86 | for (PadData data : playQueue) {
87 | for (PadChannel channel : data.getChannels()) {
88 | Tone tone = controller.getSoundSource().getTone(channel.getIndex());
89 | tone.setMuted(false);
90 | }
91 | }
92 |
93 | //CtkDebug.model(">> Remainder:" + remainder);
94 | lockAndExtendPlayingTracks();
95 | }
96 |
97 | // last beat in measure
98 | if (currentLocalBeat == 3) {
99 | queueTracks();
100 | }
101 |
102 | for (PadData data : playQueue) {
103 | PadChannel channel = data.getChannel(data.getViewChannel());
104 | //int calcBeatInMeasure = channel.getChannelPhrase().getLength();
105 | //Track track = getSong().getTrack(channel.getIndex());
106 | //TrackItem trackItem = track.getTrackItem(measure);
107 | //if (trackItem != null) {
108 | //int beats = trackItem.getStartMeasure() * 4;
109 | //beats = beat - beats;
110 | //int currentLocalBeat
111 | //.setCurrentBeat(beat);
112 | channel.setCurrentBeat(channel.getCurrentBeat() + 1);
113 | //}
114 | }
115 | }
116 |
117 | private void queueTracks() {
118 | final int currentBeat = getSong().getCurrentBeat();
119 | final int currentMeasure = getSong().getCurrentMeasure();
120 |
121 | //CtkDebug.log("Setup queued [" + currentBeat + "," + currentMeasure + "]");
122 |
123 | ArrayList copied = new ArrayList<>(queued);
124 | // loop through the queue and add queued items
125 | for (PadData data : copied) {
126 | if (data.getState() == PadDataState.QUEUED) {
127 | // add to sequencer
128 | for (PadChannel channel : data.getChannels()) {
129 | Track track = song.getTrack(channel.getIndex());
130 | addPhraseAt(track, currentMeasure + 1, channel.getChannelPhrase());
131 | }
132 | startPlaying(data);
133 | } else if (data.getState() == PadDataState.SELECTED) {
134 |
135 | }
136 | }
137 | }
138 |
139 | public boolean queue(PadData data) {
140 | if (currentLocalBeat == 3)
141 | return false;
142 |
143 | if (!queued.contains(data)) {
144 | CtkDebug.log("Queue:" + data);
145 | data.setState(PadDataState.QUEUED);
146 | queued.add(data);
147 | }
148 | return true;
149 | }
150 |
151 | public boolean unqueue(PadData data) {
152 | if (currentLocalBeat == 3)
153 | return false;
154 | if (playQueue.contains(data)) {
155 | if (data.getState() == PadDataState.QUEUED) {
156 | data.setState(PadDataState.IDLE);
157 | playQueue.remove(data);
158 | } else {
159 | data.setState(PadDataState.UNQUEUED);
160 | }
161 | } else {
162 | queued.remove(data);
163 | }
164 |
165 | return true;
166 | }
167 |
168 | public void play() throws CausticException {
169 | getSong().rewind();
170 |
171 | ArrayList copied = new ArrayList<>(queued);
172 | for (PadData data : copied) {
173 | if (data.getState() == PadDataState.QUEUED) {
174 | // add to sequencer
175 | for (PadChannel channel : data.getChannels()) {
176 | Track track = getSong().getTrack(channel.getIndex());
177 | addPhraseAt(track, 0, channel.getChannelPhrase());
178 | }
179 | startPlaying(data);
180 | } else if (data.getState() == PadDataState.SELECTED) {
181 |
182 | }
183 | }
184 | //getSong().play();
185 | controller.getSystemSequencer().play(SequencerMode.SONG);
186 | }
187 |
188 | private void lockAndExtendPlayingTracks() {
189 | final int beat = getSong().getCurrentBeat();
190 | final int currentMeasure = getSong().getCurrentMeasure();
191 | final int isNewMeasure = beat % 4;
192 |
193 | // from here on, we have everything correct with current beat and measure
194 | // the TrackSong's cursor is correct.
195 | // Check to see if there are any tracks that are in their last beat
196 | // All tracks in the last beat get extended their length
197 |
198 | for (Track track : getSong().getTracks()) {
199 |
200 | // First try an find a track item at the current measure
201 | List list = track.getItemsOnMeasure(currentMeasure);
202 | for (TrackItem item : list) {
203 | //String name = PatternUtils.toString(item.getBankIndex(), item.getPatternIndex());
204 |
205 | //int numPhraseMeasures = item.getNumMeasures();
206 | //int numBeatsInPhrase = (4 * numPhraseMeasures);
207 | //int startMeasure = beat % numBeatsInPhrase;
208 |
209 | //CtkDebug.model("XXX:" + startMeasure);
210 | // If the current beat is the last beat in the phrase
211 | if (item.getEndMeasure() == currentMeasure + 1) {
212 | //if (startMeasure == numBeatsInPhrase - 1) {
213 |
214 | PadData data = padMap.getPad(item.getBankIndex(), item.getPatternIndex());
215 |
216 | for (PadChannel channel : data.getChannels()) {
217 |
218 | if (channel.isLoopEnabled()) {
219 |
220 | if (data.getState() == PadDataState.SELECTED) {
221 | // add the phrase at the very next measure
222 | addPhraseAt(track, currentMeasure + 1, channel.getChannelPhrase());
223 | channel.setCurrentBeat(0);
224 | } else if (data.getState() == PadDataState.UNQUEUED) {
225 | // remove the item from the que
226 | stopPlaying(data);
227 | }
228 |
229 | } else {
230 | // oneshot remove
231 | stopPlaying(data);
232 | // set this here since turning off is immediate
233 | data.setState(PadDataState.IDLE);
234 | }
235 |
236 | }
237 |
238 | //CtkDebug.model("Lock:" + beat);
239 | }
240 | }
241 | }
242 | }
243 |
244 | private void addPhraseAt(Track track, int start, ChannelPhrase phrase) {
245 | try {
246 | track.addPhraseAt(start, 1, phrase);
247 | } catch (CausticException e) {
248 | e.printStackTrace();
249 | }
250 | }
251 |
252 | private void startPlaying(PadData data) {
253 | data.setState(PadDataState.SELECTED);
254 | queued.remove(data);
255 | playQueue.add(data);
256 | }
257 |
258 | private void stopPlaying(PadData data) {
259 | flushedQueue.add(data);
260 | playQueue.remove(data);
261 | // things will get set to idel in flush if not queued again
262 | // data.setState(PadDataState.IDLE);
263 | }
264 |
265 | //--------------------------------------------------------------------------
266 |
267 | public static void _addChannelTracks(PadData data, TrackSong song, int currentMeasure) {
268 | for (PadChannel channel : data.getChannels()) {
269 | Track track = song.getTrack(channel.getIndex());
270 | try {
271 | track.addPhraseAt(currentMeasure, 1, channel.getChannelPhrase());
272 | } catch (CausticException e) {
273 | e.printStackTrace();
274 | }
275 | }
276 | }
277 |
278 | }
279 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/resources/com/teotigraphix/causticlive/view/MainScreeen.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/java/com/teotigraphix/causticlive/mediator/main/PadMediator.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.mediator.main;
3 |
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import javafx.application.Platform;
8 | import javafx.beans.value.ChangeListener;
9 | import javafx.beans.value.ObservableValue;
10 | import javafx.collections.ObservableList;
11 | import javafx.event.ActionEvent;
12 | import javafx.event.EventHandler;
13 | import javafx.geometry.Point2D;
14 | import javafx.scene.Node;
15 | import javafx.scene.control.Button;
16 | import javafx.scene.control.ToggleButton;
17 | import javafx.scene.layout.Pane;
18 | import javafx.scene.text.Text;
19 |
20 | import org.androidtransfuse.event.EventObserver;
21 |
22 | import com.google.inject.Inject;
23 | import com.teotigraphix.caustic.mediator.DesktopMediatorBase;
24 | import com.teotigraphix.caustic.screen.IScreenManager;
25 | import com.teotigraphix.caustic.utils.UIUtils;
26 | import com.teotigraphix.causticlive.model.IPadMapModel;
27 | import com.teotigraphix.causticlive.model.IPadModel;
28 | import com.teotigraphix.causticlive.model.IPadModel.OnPadModelSelectedBankChange;
29 | import com.teotigraphix.causticlive.model.IPadModel.OnPadModelSelectedDataChange;
30 | import com.teotigraphix.causticlive.model.IPadModel.OnPadModelSelectedFunctionChange;
31 | import com.teotigraphix.causticlive.model.IPadModel.PadDataState;
32 | import com.teotigraphix.causticlive.model.IPadModel.PadFunction;
33 | import com.teotigraphix.causticlive.model.ISoundModel;
34 | import com.teotigraphix.causticlive.model.ISoundModel.OnSoundModelRefresh;
35 | import com.teotigraphix.causticlive.model.PadData;
36 | import com.teotigraphix.causticlive.model.SoundModel.OnSoundModelLibrarySceneChange;
37 | import com.teotigraphix.causticlive.screen.AssignmentScreenView;
38 | import com.teotigraphix.caustk.core.CtkDebug;
39 | import com.teotigraphix.caustk.tone.Tone;
40 |
41 | public class PadMediator extends DesktopMediatorBase {
42 |
43 | private List pads = new ArrayList<>();
44 |
45 | private Pane padSelectionOverlay;
46 |
47 | @Inject
48 | IPadModel padModel;
49 |
50 | @Inject
51 | IPadMapModel padMapModel;
52 |
53 | @Inject
54 | ISoundModel soundModel;
55 |
56 | private Pane padButtonPane;
57 |
58 | private Pane padFocusOverlay;
59 |
60 | private Pane extrasPane;
61 |
62 | public void layoutFocusOverlay(ToggleButton button) {
63 | if (button == null)
64 | return;
65 |
66 | Point2D point = new Point2D(button.getLayoutX(), button.getLayoutY());
67 | point = button.getParent().localToScene(point);
68 |
69 | UIUtils.layout(padFocusOverlay, point.getX(), point.getY(), button.getPrefWidth(),
70 | button.getPrefHeight());
71 | }
72 |
73 | @Override
74 | public void create(Pane root) {
75 |
76 | padFocusOverlay = (Pane)root.lookup("#padFocusOverlay");
77 | padFocusOverlay.setVisible(false);
78 |
79 | padSelectionOverlay = (Pane)root.lookup("#padSelectionOverlay");
80 | padSelectionOverlay.setDisable(true);
81 |
82 | padButtonPane = (Pane)root.lookup("#padButtonPane");
83 | extrasPane = (Pane)root.lookup("#extrasPane");
84 |
85 | int index = 0;
86 | ObservableList children = padButtonPane.getChildrenUnmodifiable();
87 | for (Node node : children) {
88 | final ToggleButton button = (ToggleButton)node;
89 | button.getProperties().put("index", index);
90 |
91 | Text text = new Text("Test");
92 | text.setLayoutX(button.getLayoutX() + 10);
93 | text.setLayoutY(button.getLayoutY() + 20);
94 | extrasPane.getChildren().add(text);
95 | button.getProperties().put("text", text);
96 |
97 | button.selectedProperty().addListener(new ChangeListener() {
98 | @Override
99 | public void changed(ObservableValue extends Boolean> observable,
100 | Boolean oldValue, Boolean newValue) {
101 | onPadButtonSelected(button);
102 | }
103 | });
104 | pads.add(button);
105 | index++;
106 | }
107 |
108 | //------------------------------
109 | // padSelectionOverlay
110 | index = 0;
111 | for (Node node : padSelectionOverlay.getChildren()) {
112 | final int i = index;
113 | final Button button = (Button)node;
114 | button.getProperties().put("index", index);
115 | button.setOnAction(new EventHandler() {
116 | @Override
117 | public void handle(ActionEvent event) {
118 | padModel.select(padModel.getSelectedData().getBank(), i);
119 | }
120 | });
121 | index++;
122 | }
123 |
124 | setEnabled(false);
125 | }
126 |
127 | private void setEnabled(boolean enabled) {
128 | ObservableList children = padButtonPane.getChildren();
129 | for (Node node : children) {
130 | node.setDisable(!enabled);
131 | }
132 | }
133 |
134 | protected void onPadButtonSelected(ToggleButton button) {
135 | if (updating)
136 | return;
137 |
138 | CtkDebug.ui("Pad pressed " + button + ":" + button.isSelected());
139 | //padModel.select((int)button.getProperties().get("index"), button.isSelected());
140 | int index = (int)button.getProperties().get("index");
141 | PadData data = padModel.getLocalData(index);
142 |
143 | //button.disarm();
144 | if (button.isSelected()) {
145 | //if (data.getState() != PadDataState.QUEUED) {
146 | soundModel.queue(data);
147 | //}
148 | } else {
149 | //if (data.getState() != PadDataState.UNQUEUED) {
150 | soundModel.unqueue(data);
151 | //}
152 | }
153 | //button.arm();
154 | // if (button.isSelected()) {
155 | // data.setState(PadDataState.SELECTED);
156 | // if (data.hasChannels()) {
157 | // for (PadChannel channel : data.getChannels()) {
158 | // int toneIndex = channel.getIndex();
159 | // Tone tone = getController().getSoundSource().getTone(toneIndex);
160 | // PatternSequencerComponent component = tone.getPatternSequencer();
161 | // component.setSelectedPattern(channel.getBankIndex(), channel.getPatternIndex());
162 | // }
163 | // }
164 | // } else {
165 | // data.setState(PadDataState.IDLE);
166 | // if (data.hasChannels()) {
167 | // for (PadChannel channel : data.getChannels()) {
168 | // int toneIndex = channel.getIndex();
169 | // Tone tone = getController().getSoundSource().getTone(toneIndex);
170 | // PatternSequencerComponent component = tone.getPatternSequencer();
171 | // component.setSelectedPattern(3, 15);
172 | // }
173 | // }
174 | // }
175 | }
176 |
177 | @Override
178 | protected void registerObservers() {
179 | super.registerObservers();
180 |
181 | // OnSoundModelRefresh
182 | soundModel.getDispatcher().register(OnSoundModelRefresh.class,
183 | new EventObserver() {
184 | @Override
185 | public void trigger(OnSoundModelRefresh object) {
186 | Platform.runLater(new Runnable() {
187 | @Override
188 | public void run() {
189 | updatePadView(padModel.getPadDataView());
190 | }
191 | });
192 | }
193 | });
194 |
195 | // OnPadModelSelectedDataChange
196 | padModel.getDispatcher().register(OnPadModelSelectedDataChange.class,
197 | new EventObserver() {
198 | @Override
199 | public void trigger(OnPadModelSelectedDataChange object) {
200 | onPadModelSelectedDataChange();
201 | }
202 | });
203 |
204 | padModel.getDispatcher().register(OnPadModelSelectedBankChange.class,
205 | new EventObserver() {
206 | @Override
207 | public void trigger(OnPadModelSelectedBankChange object) {
208 | ToggleButton button = getButtonForData(padModel.getSelectedData());
209 | layoutFocusOverlay(button);
210 | updatePadView(padModel.getPadDataView());
211 | }
212 | });
213 |
214 | // OnPadModelSelectedFunctionChange
215 | padModel.getDispatcher().register(OnPadModelSelectedFunctionChange.class,
216 | new EventObserver() {
217 | @Override
218 | public void trigger(OnPadModelSelectedFunctionChange object) {
219 | if (padModel.getSelectedFunction() == PadFunction.ASSIGN) {
220 | padSelectionOverlay.setVisible(true);
221 | padFocusOverlay.setVisible(padModel.getSelectedData() != null);
222 | } else if (padModel.getSelectedFunction() == PadFunction.PATTERN) {
223 | padSelectionOverlay.setVisible(false);
224 | padFocusOverlay.setVisible(false);
225 | }
226 | updatePadView(padModel.getPadDataView());
227 | }
228 | });
229 |
230 | // OnSoundModelLibrarySceneChange
231 | soundModel.getDispatcher().register(OnSoundModelLibrarySceneChange.class,
232 | new EventObserver() {
233 | @Override
234 | public void trigger(OnSoundModelLibrarySceneChange object) {
235 | padSelectionOverlay.setDisable(false);
236 | updateEnabledForActivePads();
237 | }
238 | });
239 | }
240 |
241 | protected void updateEnabledForActivePads() {
242 | if (!padModel.isInitialized())
243 | return;
244 |
245 | // loop through all the 16 pads, get the data for each and
246 | // enable ONLY if the tone index and phrase have been assigned
247 | ObservableList children = padButtonPane.getChildren();
248 | for (Node node : children) {
249 | boolean disabled = true;
250 | int localIndex = (int)node.getProperties().get("index");
251 | PadData data = padModel.getPadDataView().get(localIndex);
252 | if (data.hasChannels()) {
253 | disabled = false;
254 | }
255 | node.setDisable(disabled);
256 | }
257 |
258 | }
259 |
260 | @Inject
261 | IScreenManager screenManager;
262 |
263 | private boolean updating;
264 |
265 | protected void onPadModelSelectedDataChange() {
266 | // the current PadData for the assignment pad pressed
267 | PadData data = padModel.getSelectedData();
268 | if (data == null) {
269 | padFocusOverlay.setVisible(false);
270 | return;
271 | }
272 |
273 | CtkDebug.view("onPadModelAssignmentIndexChange() " + data.getBank() + " "
274 | + data.getLocalIndex());
275 |
276 | screenManager.showPopUp(AssignmentScreenView.class);
277 |
278 | // CtkDebug.view("onPadModelAssignmentIndexChange() " + data.getBank() + " "
279 | // + data.getLocalIndex());
280 | ToggleButton button = getButtonForData(data);
281 | layoutFocusOverlay(button);
282 | padFocusOverlay.setVisible(true);
283 | }
284 |
285 | private ToggleButton getButtonForData(PadData data) {
286 | for (ToggleButton button : pads) {
287 | int index = (int)button.getProperties().get("index");
288 | if (index == data.getLocalIndex()) {
289 | return button;
290 | }
291 | }
292 | return null;
293 | }
294 |
295 | @Override
296 | public void onRegister() {
297 | }
298 |
299 | protected void updatePadView(List view) {
300 | updating = true;
301 | for (PadData data : view) {
302 | int localIndex = data.getLocalIndex();
303 | ToggleButton padButton = pads.get(localIndex);
304 | Button overlayButton = (Button)padSelectionOverlay.getChildren().get(localIndex);
305 | padButton.setDisable(!data.hasChannels());
306 | if (data.getState() == PadDataState.IDLE) {
307 | padButton.setSelected(false);
308 | } else if (data.getState() == PadDataState.SELECTED) {
309 | padButton.setSelected(true);
310 | }
311 | Text t = (Text)padButton.getProperties().get("text");
312 | t.setText("Beat: " + data.getChannel(data.getViewChannel()).getCurrentBeat());
313 | String text = getButtonText(data);
314 | padButton.setText(text + "-" + data.getState());
315 | overlayButton.setText(text);
316 | }
317 | updating = false;
318 | }
319 |
320 | private String getButtonText(PadData data) {
321 | if (!data.hasChannels())
322 | return "Unassigned";
323 | int viewChannel = data.getViewChannel();
324 | Tone tone = getController().getSoundSource().getTone(viewChannel);
325 | StringBuilder sb = new StringBuilder();
326 | //sb.append(tone.getName());
327 | //sb.append(" ");
328 | //sb.append(data.getChannel(viewChannel).getPatternName());
329 | return sb.toString();
330 | }
331 | }
332 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/main/java/com/teotigraphix/causticlive/model/SoundModel.java:
--------------------------------------------------------------------------------
1 |
2 | package com.teotigraphix.causticlive.model;
3 |
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.util.ArrayList;
7 | import java.util.HashMap;
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.UUID;
11 |
12 | import com.google.inject.Inject;
13 | import com.google.inject.Singleton;
14 | import com.teotigraphix.caustic.model.ICaustkModelState;
15 | import com.teotigraphix.caustic.model.ModelBase;
16 | import com.teotigraphix.causticlive.mediator.SongMediator;
17 | import com.teotigraphix.causticlive.model.IPadModel.PadDataState;
18 | import com.teotigraphix.caustk.controller.ICaustkController;
19 | import com.teotigraphix.caustk.core.CausticException;
20 | import com.teotigraphix.caustk.core.CtkDebug;
21 | import com.teotigraphix.caustk.core.osc.SequencerMessage;
22 | import com.teotigraphix.caustk.library.Library;
23 | import com.teotigraphix.caustk.library.LibraryPatch;
24 | import com.teotigraphix.caustk.library.LibraryScene;
25 | import com.teotigraphix.caustk.sequencer.TrackSong;
26 | import com.teotigraphix.caustk.tone.Tone;
27 | import com.teotigraphix.caustk.tone.components.SynthComponent;
28 |
29 | @Singleton
30 | public class SoundModel extends ModelBase implements ISoundModel {
31 |
32 | SongPlayer songPlayer;
33 |
34 | @Inject
35 | IPadMapModel padMapModel;
36 |
37 | public void beatChange(int measure, int beat) {
38 | songPlayer.beatChange(measure, beat);
39 | trigger(new OnSoundModelRefresh(songPlayer.getSong().getCurrentBeat()));
40 | }
41 |
42 | public void measureChange(int measure) {
43 | //CtkDebug.model(">>>>>> Measure:" + measure);
44 | }
45 |
46 | @Override
47 | public void play() throws CausticException {
48 | getController().getSongSequencer().setLoopPoints(0, 1000);
49 | songPlayer.play();
50 | }
51 |
52 | @Override
53 | public void stop() {
54 | getController().getSystemSequencer().stop();
55 | //getController().getSongSequencer().playPosition(0);
56 | }
57 |
58 | @Override
59 | public void queue(PadData data) {
60 | songPlayer.queue(data);
61 | }
62 |
63 | @Override
64 | public void unqueue(PadData data) {
65 | songPlayer.unqueue(data);
66 | }
67 |
68 | @Inject
69 | SongMediator songMediator;
70 |
71 | @Inject
72 | IPadModel padModel;
73 |
74 | @Override
75 | protected SoundModelState getState() {
76 | return (SoundModelState)super.getState();
77 | }
78 |
79 | public TrackSong getSong() {
80 | return getController().getSongManager().getTrackSong();
81 | }
82 |
83 | //--------------------------------------------------------------------------
84 | // Property API
85 | //--------------------------------------------------------------------------
86 |
87 | @Override
88 | public int getCurrentMeasure() {
89 | return getSong().getCurrentMeasure();
90 | }
91 |
92 | @Override
93 | public int getCurrentBeat() {
94 | return getSong().getCurrentBeat();
95 | }
96 |
97 | //----------------------------------
98 | // Library
99 | //----------------------------------
100 |
101 | @Override
102 | public final Library getLibrary() {
103 | return getController().getLibraryManager().getSelectedLibrary();
104 | }
105 |
106 | @Override
107 | public void loadLibrary(File file) {
108 | Library library = getController().getLibraryManager().loadLibrary(file);
109 | if (library != null) {
110 | getController().getLibraryManager().setSelectedLibrary(library);
111 | getState().setSelectedLibrary(library.getDirectory());
112 | }
113 | }
114 |
115 | //----------------------------------
116 | // libraryScene
117 | //----------------------------------
118 |
119 | private LibraryScene libraryScene;
120 |
121 | public LibraryScene getLibraryScene() {
122 | return libraryScene;
123 | }
124 |
125 | public void setLibraryScene(LibraryScene libraryScene) {
126 | this.libraryScene = libraryScene;
127 | getState().setSelectedScene(libraryScene.getId());
128 | trigger(new OnSoundModelLibrarySceneChange());
129 |
130 | }
131 |
132 | //--------------------------------------------------------------------------
133 | // Constructor
134 | //--------------------------------------------------------------------------
135 |
136 | public SoundModel() {
137 | setStateFactory(SoundModelState.class);
138 | }
139 |
140 | //--------------------------------------------------------------------------
141 | // Public API
142 | //--------------------------------------------------------------------------
143 |
144 | //--------------------------------------------------------------------------
145 | // Public Overrides
146 | //--------------------------------------------------------------------------
147 |
148 | @Override
149 | protected void initalizeState() {
150 | File file = getState().getSelectedSong();
151 | if (file == null) {
152 | file = getSong().getFile();
153 | CtkDebug.model("Load song: " + file.getPath());
154 | getState().setSelectedSong(file);
155 | }
156 | // init song stuff
157 | try {
158 | getController().getSongManager().load(file);
159 | } catch (IOException e) {
160 | e.printStackTrace();
161 | }
162 |
163 | TrackSong song = getSong();
164 | song.setNumTracks(14);
165 |
166 | // Belongs in state?
167 | songPlayer = new SongPlayer(getController());
168 | songPlayer.setPadMap(padMapModel);
169 | songPlayer.setSong(song);
170 |
171 | SequencerMessage.SONG_END_MODE.send(getController(), 0);
172 | }
173 |
174 | @Override
175 | public void onRegister() {
176 | }
177 |
178 | @Override
179 | public void onShow() {
180 |
181 | for (PadData data : padMapModel.getPads()) {
182 | if (data.getState() == PadDataState.SELECTED) {
183 | songPlayer.queue(data);
184 | }
185 | // XXX might have to reset other than selected from the saved state
186 | }
187 |
188 | Integer selectedTone = getState().getSelectedTone();
189 | if (selectedTone != null)
190 | setSelectedTone(selectedTone);
191 | }
192 |
193 | private void loadPatches() {
194 | Library library = getLibrary();
195 | if (library == null)
196 | return;
197 |
198 | for (ToneData data : getState().getTones()) {
199 | UUID patchId = data.getPatchId();
200 | LibraryPatch libraryPatch = library.findPatchById(patchId);
201 | if (libraryPatch != null)
202 | setPatch(data, libraryPatch);
203 | }
204 | }
205 |
206 | @Override
207 | public void loadScene(LibraryScene libraryScene, boolean reset) {
208 | // Load the scene to create the machines
209 | getController().getSoundSource().clearAndReset();
210 |
211 | if (reset) {
212 | getState().clear();
213 | }
214 | try {
215 | getController().getSoundSource().createScene(libraryScene);
216 | } catch (CausticException e) {
217 | e.printStackTrace();
218 | }
219 |
220 | if (reset) {
221 | for (Tone tone : getController().getSoundSource().getTones()) {
222 | ToneData data = new ToneData(tone.getIndex());
223 | getState().putTone(tone.getIndex(), data);
224 | }
225 | }
226 |
227 | setLibraryScene(libraryScene);
228 |
229 | //setSelectedTone(-1);
230 | }
231 |
232 | /**
233 | * @see SoundModel#loadScene(LibraryScene)
234 | * @see SoundModel#getDispatcher()
235 | */
236 | public static class OnSoundModelLibrarySceneChange {
237 |
238 | }
239 |
240 | private int selectedTone;
241 |
242 | @Override
243 | public final int getSelectedTone() {
244 | return selectedTone;
245 | }
246 |
247 | @Override
248 | public ToneData getSelectedToneData() {
249 | return getState().getToneData(selectedTone);
250 | }
251 |
252 | /**
253 | * @see OnSoundModelSelectedToneChange
254 | * @param value
255 | */
256 | @Override
257 | public final void setSelectedTone(int value) {
258 | // clear the selection
259 | if (value == -1) {
260 | selectedTone = -1;
261 | trigger(new OnSoundModelSelectedToneChange(null, null));
262 | return;
263 | }
264 | if (value == selectedTone)
265 | return;
266 | ToneData oldTone = getState().getToneData(selectedTone);
267 | selectedTone = value;
268 | ToneData tone = getState().getToneData(selectedTone);
269 | getState().setSelectedTone(selectedTone);
270 | trigger(new OnSoundModelSelectedToneChange(tone, oldTone));
271 | }
272 |
273 | public static class SoundModelState implements ICaustkModelState {
274 |
275 | @Inject
276 | private transient ISoundModel soundModel;
277 |
278 | private Map toneMap = new HashMap();
279 |
280 | public List getTones() {
281 | return new ArrayList<>(toneMap.values());
282 | }
283 |
284 | private File selectedSong;
285 |
286 | public File getSelectedSong() {
287 | return selectedSong;
288 | }
289 |
290 | public void setSelectedSong(File valeu) {
291 | selectedSong = valeu;
292 | }
293 |
294 | private File selectedLibrary;
295 |
296 | public File getSelectedLibrary() {
297 | return selectedLibrary;
298 | }
299 |
300 | public void setSelectedLibrary(File selectedLibrary) {
301 | this.selectedLibrary = selectedLibrary;
302 | }
303 |
304 | private UUID selectedScene;
305 |
306 | public UUID getSelectedScene() {
307 | return selectedScene;
308 | }
309 |
310 | public void putTone(int index, ToneData data) {
311 | toneMap.put(index, data);
312 | }
313 |
314 | public void setSelectedScene(UUID selectedScene) {
315 | this.selectedScene = selectedScene;
316 | }
317 |
318 | private int selectedTone;
319 |
320 | public int getSelectedTone() {
321 | return selectedTone;
322 | }
323 |
324 | public void setSelectedTone(int selectedTone) {
325 | this.selectedTone = selectedTone;
326 | }
327 |
328 | /**
329 | * Clears the tone map.
330 | */
331 | public void clear() {
332 | toneMap.clear();
333 | }
334 |
335 | public ToneData getToneData(int toneIndex) {
336 | return toneMap.get(toneIndex);
337 | }
338 |
339 | @Override
340 | public void sleep() {
341 | }
342 |
343 | @Override
344 | public void wakeup(ICaustkController controller) {
345 | File file = getSelectedLibrary();
346 | if (file != null) {
347 | soundModel.loadLibrary(file);
348 | }
349 |
350 | // // XXX get rid of this
351 | // Integer index = getController().getProjectManager().getProject()
352 | // .getInteger("assignmentIndex");
353 | // if (index == null)
354 | // index = 0;
355 | //
356 | // padModel.setAssignmentIndex(index);
357 |
358 | UUID sceneId = getSelectedScene();
359 | if (sceneId != null) {
360 | Library library = controller.getLibraryManager().getSelectedLibrary();
361 | if (library != null) {
362 | LibraryScene scene = library.findSceneById(sceneId);
363 | if (scene != null) {
364 | soundModel.loadScene(scene, false);
365 | }
366 | }
367 | }
368 |
369 | // load patches
370 | ((SoundModel)soundModel).loadPatches();
371 | }
372 |
373 | }
374 |
375 | public static class ToneData {
376 |
377 | private int toneIndex;
378 |
379 | public final int getToneIndex() {
380 | return toneIndex;
381 | }
382 |
383 | private UUID patchId;
384 |
385 | public final UUID getPatchId() {
386 | return patchId;
387 | }
388 |
389 | void setPatchId(UUID value) {
390 | patchId = value;
391 | }
392 |
393 | public ToneData(int toneIndex) {
394 | this.toneIndex = toneIndex;
395 | }
396 |
397 | @Override
398 | public String toString() {
399 | return "[" + toneIndex + "]" + patchId;
400 | }
401 | }
402 |
403 | public static class OnSoundModelSelectedToneChange {
404 |
405 | private ToneData tone;
406 |
407 | private ToneData oldTone;
408 |
409 | public final ToneData getTone() {
410 | return tone;
411 | }
412 |
413 | public final ToneData getOldTone() {
414 | return oldTone;
415 | }
416 |
417 | public OnSoundModelSelectedToneChange(ToneData tone, ToneData oldTone) {
418 | this.tone = tone;
419 | this.oldTone = oldTone;
420 | }
421 |
422 | }
423 |
424 | @Override
425 | public boolean setPatch(ToneData data, LibraryPatch patch) {
426 | if (data == null)
427 | return false;
428 |
429 | Tone tone = getController().getSoundSource().getTone(data.getToneIndex());
430 |
431 | if (tone.getToneType() != patch.getToneType())
432 | return false;
433 |
434 | data.setPatchId(patch.getId());
435 |
436 | SynthComponent component = tone.getComponent(SynthComponent.class);
437 | File presetFile = patch.getPresetFile();
438 | File fullPresetFile = getLibrary().getPresetFile(presetFile);
439 | component.loadPreset(fullPresetFile.getPath());
440 |
441 | return true;
442 | }
443 |
444 | }
445 |
--------------------------------------------------------------------------------
/CausticLiveFX/src/test/resources/libraries/PadDataTest/library.ctk:
--------------------------------------------------------------------------------
1 | {"metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":[]},"id":"373a39cc-b1ec-49cb-9e4f-cab7b3cc5ff9","directory":{"path":"src\\test\\resources\\unit_test\\libraries\\PadDataTest"},"patterns":[],"patches":[{"name":"LOWLEAD","toneType":"SubSynth","id":"94261228-9ba7-49cd-ac60-c1796ee84277","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["subsynth","LOWLEAD"]},"index":-1},{"name":"HIGHLEAD","toneType":"SubSynth","id":"45d5350a-e991-47a4-a2c6-bd7a0971a5d8","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["subsynth","HIGHLEAD","DEFAULT"]},"index":-1},{"name":"BASS","toneType":"SubSynth","id":"4f895eae-cb32-4724-b1e5-8a25c279ece0","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["subsynth","BASS"]},"index":-1},{"name":"MELODY","toneType":"Bassline","id":"31bcb5eb-eaee-49ae-9f7e-1ce573641f61","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["bassline","MELODY"]},"index":-1},{"name":"STRINGS","toneType":"PCMSynth","id":"7bf3dcc6-a4f9-46f3-b31a-74aa53908c4f","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["pcmsynth","STRINGS","STRINGS2"]},"index":-1},{"name":"DRUMSIES","toneType":"Beatbox","id":"f9020c9b-2a05-4965-8bb3-99983a1cc174","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["beatbox","DRUMSIES"]},"index":-1}],"phrases":[{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":0,"resolution":"SIXTYFOURTH","length":1,"noteData":"0.000000 48 0.70 0.125000 0|0.250000 63 0.70 0.500000 0|0.500000 36 0.70 0.625000 0|0.750000 48 0.70 0.875000 0|1.000000 63 0.70 1.250000 0|1.250000 36 0.70 1.375000 0|1.500000 48 0.70 1.562500 0|1.750000 62 0.70 1.937500 0|2.000000 36 0.70 2.125000 0|2.250000 48 0.70 2.312500 0|2.500000 62 0.70 2.750000 0|2.750000 36 0.70 2.875000 0|3.000000 48 0.70 3.125000 0|3.250000 63 0.70 3.437500 0|3.500000 48 0.70 3.562500 0|3.750000 62 0.70 4.000000 0","toneType":"SubSynth","tempo":140.0,"id":"06607b44-1723-4cc3-a6d4-30db4b066922","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-1","140.0","subsynth","sixtyfourth"]},"index":-1},{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":1,"resolution":"SIXTYFOURTH","length":1,"noteData":"0.000000 44 0.70 0.125000 0|0.250000 63 0.75 0.500000 0|0.500000 32 0.70 0.625000 0|0.750000 44 0.70 0.875000 0|1.000000 63 0.75 1.250000 0|1.250000 32 0.70 1.375000 0|1.500000 44 0.70 1.562500 0|1.750000 62 0.75 1.937500 0|2.000000 32 0.70 2.125000 0|2.250000 44 0.70 2.312500 0|2.500000 62 0.75 2.750000 0|2.750000 32 0.70 2.875000 0|3.000000 44 0.70 3.125000 0|3.250000 63 0.75 3.437500 0|3.500000 44 0.70 3.562500 0|3.750000 62 0.75 4.000000 0","toneType":"SubSynth","tempo":140.0,"id":"f9cf8ca4-59ac-445d-a615-2dfd2ee5bf26","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-1","140.0","subsynth","sixtyfourth"]},"index":-1},{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":2,"resolution":"SIXTYFOURTH","length":1,"noteData":"0.000000 41 0.75 0.125000 0|0.250000 67 0.75 0.500000 0|0.500000 29 0.75 0.625000 0|0.750000 41 0.75 0.875000 0|1.000000 67 0.75 1.250000 0|1.250000 29 0.75 1.375000 0|1.500000 41 0.75 1.562500 0|1.750000 65 0.75 1.937500 0|2.000000 29 0.75 2.125000 0|2.250000 41 0.75 2.312500 0|2.500000 65 0.75 2.750000 0|2.750000 29 0.75 2.875000 0|3.000000 41 0.75 3.125000 0|3.250000 67 0.75 3.437500 0|3.500000 41 0.75 3.562500 0|3.750000 65 0.70 4.000000 0","toneType":"SubSynth","tempo":140.0,"id":"6e20f9d5-d0ef-4238-8de7-fc02bd94a49c","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-1","140.0","subsynth","sixtyfourth"]},"index":-1},{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":3,"resolution":"SIXTYFOURTH","length":1,"noteData":"0.000000 48 0.75 0.125000 0|0.250000 63 0.75 0.500000 0|0.500000 36 0.75 0.625000 0|0.750000 48 0.75 0.875000 0|1.000000 63 0.75 1.250000 0|1.250000 36 0.75 1.375000 0|1.500000 48 0.75 1.562500 0|1.750000 62 0.75 1.937500 0|2.000000 36 0.75 2.125000 0|2.250000 48 0.75 2.312500 0|2.500000 62 0.75 2.750000 0|2.750000 36 0.75 2.875000 0|3.000000 48 0.75 3.125000 0|3.250000 63 0.75 3.437500 0|3.500000 48 0.75 3.562500 0|3.750000 62 0.75 4.000000 0","toneType":"SubSynth","tempo":140.0,"id":"3001ed56-f714-4095-a109-2737ca3bd7df","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-1","140.0","subsynth","sixtyfourth"]},"index":-1},{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":4,"resolution":"SIXTYFOURTH","length":1,"noteData":"0.000000 48 0.75 0.125000 0|0.250000 63 0.75 0.500000 0|0.500000 36 0.75 0.625000 0|0.750000 48 0.75 0.875000 0|1.000000 63 0.75 1.250000 0|1.250000 36 0.75 1.375000 0|1.500000 48 0.75 1.625000 0|1.750000 63 0.75 2.000000 0|2.000000 36 0.75 2.125000 0|2.250000 48 0.75 2.312500 0|2.500000 63 0.75 2.750000 0|2.750000 36 0.75 2.875000 0|3.000000 48 0.75 3.125000 0|3.250000 63 0.75 3.375000 0|3.500000 62 0.75 3.750000 0|3.750000 48 0.75 3.812500 0","toneType":"SubSynth","tempo":140.0,"id":"da92dc57-f736-4f85-bfbc-51fb5d9206b9","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-1","140.0","subsynth","sixtyfourth"]},"index":-1},{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":5,"resolution":"SIXTYFOURTH","length":1,"noteData":"0.000000 41 0.75 0.125000 0|0.250000 56 0.75 0.500000 0|0.500000 29 0.75 0.625000 0|0.750000 41 0.75 0.875000 0|1.000000 56 0.75 1.250000 0|1.250000 29 0.75 1.375000 0|1.500000 41 0.75 1.625000 0|1.750000 56 0.75 2.000000 0|2.000000 29 0.75 2.125000 0|2.250000 41 0.75 2.312500 0|2.500000 56 0.75 2.750000 0|2.750000 29 0.75 2.875000 0|3.000000 41 0.75 3.125000 0|3.250000 56 0.75 3.375000 0|3.500000 55 0.75 3.750000 0|3.750000 41 0.75 3.812500 0","toneType":"SubSynth","tempo":140.0,"id":"b550af13-3568-498d-9d00-53ccdf3caa10","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-1","140.0","subsynth","sixtyfourth"]},"index":-1},{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":6,"resolution":"THIRTYSECOND","length":2,"noteData":"0.000000 56 0.75 0.125000 0|0.250000 60 0.75 0.500000 0|0.500000 44 0.75 0.625000 0|0.750000 56 0.75 0.875000 0|1.000000 60 0.75 1.250000 0|1.250000 44 0.75 1.375000 0|1.500000 56 0.75 1.625000 0|1.750000 60 0.75 2.000000 0|2.000000 44 0.75 2.125000 0|2.250000 56 0.75 2.375000 0|2.500000 60 0.75 2.750000 0|2.750000 44 0.75 2.875000 0|3.000000 56 0.75 3.125000 0|3.250000 60 0.75 3.500000 0|3.500000 44 0.75 3.625000 0|3.750000 56 0.75 3.875000 0|4.000000 56 0.75 4.125000 0|4.250000 60 0.75 4.500000 0|4.500000 44 0.75 4.625000 0|4.750000 56 0.75 4.875000 0|5.000000 60 0.75 5.250000 0|5.250000 44 0.75 5.375000 0|5.500000 56 0.75 5.625000 0|5.750000 60 0.75 6.000000 0|6.000000 44 0.75 6.125000 0|6.250000 56 0.75 6.375000 0|6.500000 60 0.75 6.750000 0|6.750000 44 0.75 6.875000 0|7.000000 56 0.75 7.125000 0|7.250000 60 0.75 7.500000 0|7.500000 44 0.75 7.625000 0|7.750000 56 0.75 7.875000 0","toneType":"SubSynth","tempo":140.0,"id":"d664980d-a131-4fb3-bb28-e2b83e497264","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-2","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"LOWLEAD","bankIndex":0,"patternIndex":7,"resolution":"THIRTYSECOND","length":2,"noteData":"0.000000 60 0.75 0.125000 0|0.250000 63 0.75 0.500000 0|0.500000 48 0.75 0.625000 0|0.750000 60 0.75 0.875000 0|1.000000 63 0.75 1.250000 0|1.250000 48 0.75 1.375000 0|1.500000 60 0.75 1.625000 0|1.750000 63 0.75 2.000000 0|2.000000 48 0.75 2.125000 0|2.250000 60 0.75 2.375000 0|2.500000 63 0.75 2.750000 0|2.750000 48 0.75 2.875000 0|3.000000 60 0.75 3.125000 0|3.250000 63 0.75 3.500000 0|3.500000 48 0.75 3.625000 0|3.750000 60 0.75 3.875000 0|4.000000 60 0.75 4.125000 0|4.250000 63 0.75 4.500000 0|4.500000 48 0.75 4.625000 0|4.750000 60 0.75 4.875000 0|5.000000 63 0.75 5.250000 0|5.250000 48 0.75 5.375000 0|5.500000 60 0.75 5.625000 0|5.750000 63 0.75 6.000000 0|6.000000 48 0.75 6.125000 0|6.250000 60 0.75 6.375000 0|6.500000 63 0.75 6.750000 0|6.750000 48 0.75 6.875000 0|7.000000 60 0.75 7.125000 0|7.250000 63 0.75 7.500000 0|7.500000 48 0.75 7.625000 0|7.750000 60 0.75 7.875000 0","toneType":"SubSynth","tempo":140.0,"id":"6109f583-a6ba-4a9b-a5ef-7187eb524957","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["LOWLEAD","length-2","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"HIGHLEAD","bankIndex":0,"patternIndex":0,"resolution":"THIRTYSECOND","length":1,"noteData":"0.000000 60 0.70 0.125000 0|0.250000 75 0.70 0.500000 0|0.500000 48 0.70 0.625000 0|0.750000 63 0.70 0.875000 0|1.000000 75 0.70 1.250000 0|1.250000 48 0.70 1.375000 0|1.500000 60 0.70 1.625000 0|1.750000 74 0.70 1.937500 0|2.000000 48 0.70 2.125000 0|2.250000 60 0.70 2.375000 0|2.500000 74 0.70 2.750000 0|2.750000 48 0.70 2.875000 0|3.000000 60 0.70 3.125000 0|3.250000 75 0.70 3.562500 0|3.500000 60 0.70 3.625000 0|3.750000 55 0.75 3.875000 0|3.750000 74 0.70 4.000000 0","toneType":"SubSynth","tempo":140.0,"id":"3e27e804-a58b-4826-9dda-b5050300df8b","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["HIGHLEAD","length-1","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"HIGHLEAD","bankIndex":0,"patternIndex":1,"resolution":"THIRTYSECOND","length":1,"noteData":"0.000000 56 0.70 0.125000 0|0.250000 75 0.75 0.500000 0|0.500000 44 0.70 0.625000 0|0.750000 56 0.70 0.875000 0|1.000000 75 0.75 1.250000 0|1.250000 44 0.70 1.375000 0|1.500000 56 0.70 1.625000 0|1.750000 74 0.75 1.937500 0|2.000000 44 0.70 2.125000 0|2.250000 56 0.70 2.375000 0|2.500000 74 0.75 2.750000 0|2.750000 44 0.70 2.875000 0|3.000000 56 0.70 3.125000 0|3.250000 70 0.75 3.375000 0|3.500000 56 0.70 3.625000 0|3.500000 72 0.75 3.625000 0|3.750000 74 0.75 4.000000 0","toneType":"SubSynth","tempo":140.0,"id":"29d551af-637c-4eed-8a31-f6a6a9e56691","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["HIGHLEAD","length-1","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"HIGHLEAD","bankIndex":0,"patternIndex":2,"resolution":"THIRTYSECOND","length":1,"noteData":"0.000000 53 0.75 0.125000 0|0.250000 79 0.75 0.500000 0|0.500000 41 0.75 0.625000 0|0.750000 53 0.75 0.875000 0|1.000000 79 0.75 1.250000 0|1.250000 41 0.75 1.375000 0|1.500000 53 0.75 1.625000 0|1.750000 77 0.75 1.937500 0|2.000000 41 0.75 2.125000 0|2.250000 53 0.75 2.375000 0|2.500000 77 0.75 2.750000 0|2.750000 41 0.75 2.875000 0|3.000000 53 0.75 3.125000 0|3.250000 79 0.75 3.437500 0|3.500000 53 0.75 3.625000 0|3.750000 77 0.70 4.000000 0","toneType":"SubSynth","tempo":140.0,"id":"f9d68415-af85-47e5-8254-6910937390f2","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["HIGHLEAD","length-1","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"HIGHLEAD","bankIndex":0,"patternIndex":3,"resolution":"THIRTYSECOND","length":1,"noteData":"0.000000 56 0.70 0.125000 0|0.250000 75 0.75 0.500000 0|0.500000 44 0.70 0.625000 0|0.750000 56 0.70 0.875000 0|1.000000 75 0.75 1.250000 0|1.250000 44 0.70 1.375000 0|1.500000 56 0.70 1.625000 0|1.750000 74 0.75 1.937500 0|2.000000 44 0.70 2.125000 0|2.250000 56 0.70 2.375000 0|2.500000 74 0.75 2.750000 0|2.750000 44 0.70 2.875000 0|3.000000 56 0.70 3.125000 0|3.250000 75 0.75 3.437500 0|3.500000 56 0.70 3.625000 0|3.500000 77 0.75 3.687500 0|3.750000 79 0.75 3.937500 0","toneType":"SubSynth","tempo":140.0,"id":"445fa7bc-d406-44c3-b5f2-24b1f66e4f0c","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["HIGHLEAD","length-1","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"HIGHLEAD","bankIndex":0,"patternIndex":4,"resolution":"THIRTYSECOND","length":1,"noteData":"0.000000 60 0.75 0.125000 0|0.250000 63 0.75 0.500000 0|0.500000 48 0.75 0.625000 0|0.750000 60 0.75 0.875000 0|1.000000 63 0.75 1.250000 0|1.250000 48 0.75 1.375000 0|1.500000 60 0.75 1.625000 0|1.750000 63 0.75 2.000000 0|2.000000 48 0.75 2.125000 0|2.250000 60 0.75 2.375000 0|2.500000 63 0.75 2.750000 0|2.750000 48 0.75 2.875000 0|3.000000 60 0.75 3.187500 0|3.250000 63 0.75 3.375000 0|3.500000 62 0.75 3.625000 0|3.750000 55 0.75 3.875000 0","toneType":"SubSynth","tempo":140.0,"id":"91d52564-2c55-4169-a60c-ad03e3a8aa41","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["HIGHLEAD","length-1","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"HIGHLEAD","bankIndex":0,"patternIndex":5,"resolution":"THIRTYSECOND","length":1,"noteData":"0.000000 68 0.75 0.125000 0|0.250000 63 0.75 0.375000 0|0.500000 72 0.75 0.750000 0|0.750000 68 0.75 0.875000 0|1.000000 75 0.75 1.125000 0|1.250000 79 0.75 1.500000 0|1.500000 80 0.75 1.625000 0|1.750000 72 0.75 1.875000 0|2.000000 75 0.75 2.250000 0|2.250000 79 0.75 2.375000 0|2.500000 72 0.75 2.625000 0|2.750000 75 0.75 3.000000 0|3.000000 68 0.75 3.125000 0|3.250000 72 0.75 3.375000 0|3.500000 68 0.75 3.750000 0|3.750000 72 0.75 3.875000 0","toneType":"SubSynth","tempo":140.0,"id":"8412fa71-82a6-43e0-94a4-d897f307d03f","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["HIGHLEAD","length-1","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"HIGHLEAD","bankIndex":0,"patternIndex":6,"resolution":"THIRTYSECOND","length":1,"noteData":"0.000000 72 0.75 0.125000 0|0.250000 67 0.75 0.375000 0|0.500000 75 0.75 0.750000 0|0.750000 72 0.75 0.875000 0|1.000000 79 0.75 1.125000 0|1.250000 82 0.75 1.500000 0|1.500000 84 0.75 1.625000 0|1.750000 75 0.75 1.875000 0|2.000000 79 0.75 2.250000 0|2.250000 82 0.75 2.375000 0|2.500000 75 0.75 2.625000 0|2.750000 79 0.75 3.000000 0|3.000000 72 0.75 3.125000 0|3.250000 75 0.75 3.375000 0|3.500000 72 0.75 3.750000 0|3.750000 75 0.75 3.875000 0","toneType":"SubSynth","tempo":140.0,"id":"b9b34313-f7a1-4b1e-9932-575ae6e18e4c","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["HIGHLEAD","length-1","140.0","subsynth","thirtysecond"]},"index":-1},{"machineName":"BASS","bankIndex":0,"patternIndex":0,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 48 0.75 1.000000 0|1.000000 60 0.75 2.000000 0|2.000000 48 0.75 3.000000 0|3.000000 60 0.75 4.000000 0|4.000000 48 0.75 5.000000 0|5.000000 60 0.75 6.000000 0|6.000000 48 0.75 7.000000 0|7.000000 60 0.75 8.000000 0|8.000000 44 0.75 9.000000 0|9.000000 56 0.75 10.000000 0|10.000000 44 0.75 11.000000 0|11.000000 56 0.75 12.000000 0|12.000000 44 0.75 13.000000 0|13.000000 56 0.75 14.000000 0|14.000000 44 0.75 15.000000 0|15.000000 56 0.75 16.000000 0|16.000000 41 0.75 17.000000 0|17.000000 53 0.75 18.000000 0|18.000000 41 0.75 19.000000 0|19.000000 53 0.75 20.000000 0|20.000000 44 0.75 21.000000 0|21.000000 56 0.75 22.000000 0|22.000000 46 0.75 23.000000 0|23.000000 58 0.75 24.000000 0|24.000000 48 0.75 25.000000 0|25.000000 60 0.75 26.000000 0|26.000000 48 0.75 27.000000 0|27.000000 60 0.75 28.000000 0|28.000000 48 0.75 29.000000 0|29.000000 60 0.75 30.000000 0|30.000000 48 0.75 31.000000 0|31.000000 60 0.75 32.000000 0","toneType":"SubSynth","tempo":140.0,"id":"247b782b-a53c-485d-9862-ae573dd2e362","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["BASS","length-8","140.0","subsynth","sixteenth"]},"index":-1},{"machineName":"BASS","bankIndex":0,"patternIndex":1,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 48 0.75 1.000000 0|1.000000 48 0.75 2.000000 0|2.000000 48 0.75 3.000000 0|3.000000 48 0.75 4.000000 0|4.000000 48 0.75 5.000000 0|5.000000 48 0.75 6.000000 0|6.000000 48 0.75 7.000000 0|7.000000 48 0.75 8.000000 0|8.000000 44 0.75 9.000000 0|9.000000 44 0.75 10.000000 0|10.000000 44 0.75 11.000000 0|11.000000 44 0.75 12.000000 0|12.000000 44 0.75 13.000000 0|13.000000 44 0.75 14.000000 0|14.000000 44 0.75 15.000000 0|15.000000 44 0.75 16.000000 0|16.000000 41 0.75 17.000000 0|17.000000 41 0.75 18.000000 0|18.000000 41 0.75 19.000000 0|19.000000 41 0.75 20.000000 0|20.000000 44 0.75 21.000000 0|21.000000 44 0.75 22.000000 0|22.000000 46 0.75 23.000000 0|23.000000 46 0.75 24.000000 0|24.000000 48 0.75 25.000000 0|25.000000 48 0.75 26.000000 0|26.000000 48 0.75 27.000000 0|27.000000 48 0.75 28.000000 0|28.000000 48 0.75 29.000000 0|29.000000 48 0.75 30.000000 0|30.000000 48 0.75 31.000000 0|31.000000 48 0.75 32.000000 0","toneType":"SubSynth","tempo":140.0,"id":"200a4e6f-d99e-44dd-91ce-692c241af74f","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["BASS","length-8","140.0","subsynth","sixteenth"]},"index":-1},{"machineName":"BASS","bankIndex":0,"patternIndex":2,"resolution":"SIXTEENTH","length":4,"noteData":"0.000000 44 0.75 8.000000 0|8.000000 48 0.75 16.000000 0","toneType":"SubSynth","tempo":140.0,"id":"855d05a8-0c5f-460e-b5ba-ba4dd06ab4d9","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["BASS","length-4","140.0","subsynth","sixteenth"]},"index":-1},{"machineName":"MELODY","bankIndex":0,"patternIndex":0,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 63 0.75 0.500000 0|0.500000 70 0.75 8.000000 1|8.000000 72 0.75 8.500000 1|8.500000 63 0.75 16.500000 1|16.500000 74 0.75 17.500000 1|17.500000 65 0.75 19.500000 1|19.500000 75 0.75 20.500000 1|20.500000 74 0.75 21.500000 1|21.500000 75 0.75 22.500000 1|22.500000 74 0.75 23.500000 1|23.500000 70 0.75 24.500000 1|24.500000 72 0.75 28.500000 1|28.500000 60 0.75 32.000000 1","toneType":"Bassline","tempo":140.0,"id":"3b144b03-ad24-4518-970b-a15373a32b5f","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["MELODY","length-8","140.0","bassline","sixteenth"]},"index":-1},{"machineName":"MELODY","bankIndex":0,"patternIndex":1,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 60 0.75 0.500000 0|0.500000 67 0.75 4.500000 1|4.500000 60 0.75 8.500000 1|8.000000 68 0.75 11.000000 1|11.000000 70 0.75 11.500000 1|11.500000 68 0.75 12.000000 0|12.000000 67 0.75 16.500000 1|16.500000 75 0.75 18.500000 1|18.500000 68 0.75 20.500000 1|20.500000 70 0.75 24.500000 1|24.500000 63 0.75 32.000000 1","toneType":"Bassline","tempo":140.0,"id":"7fb00bdf-d284-49df-bab6-6aac7934ba1b","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["MELODY","length-8","140.0","bassline","sixteenth"]},"index":-1},{"machineName":"MELODY","bankIndex":0,"patternIndex":2,"resolution":"SIXTEENTH","length":4,"noteData":"0.000000 63 0.75 4.000000 0","toneType":"Bassline","tempo":140.0,"id":"bfe6124d-54b9-4901-8734-e735fe5c7b96","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["MELODY","length-4","140.0","bassline","sixteenth"]},"index":-1},{"machineName":"MELODY","bankIndex":0,"patternIndex":3,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 70 0.75 0.500000 0|0.500000 63 0.75 4.000000 0|4.000000 70 0.75 4.500000 0|4.500000 63 0.75 6.000000 0|6.000000 72 0.75 6.500000 0|6.500000 65 0.75 8.000000 0|8.000000 74 0.75 8.500000 0|8.500000 67 0.75 12.000000 0|12.000000 75 0.75 12.500000 0|12.500000 72 0.75 13.000000 0|13.000000 75 0.75 13.500000 0|13.500000 65 0.75 14.500000 0|14.500000 70 0.75 15.000000 0|15.000000 63 0.75 16.000000 0|16.000000 62 0.75 16.500000 0|16.500000 63 0.75 17.000000 0|17.000000 62 0.75 17.500000 0|17.500000 70 0.75 19.000000 0|19.000000 72 0.75 19.500000 0|19.500000 67 0.75 21.000000 0|21.000000 65 0.75 22.500000 0|22.500000 70 0.75 23.000000 0|23.000000 63 0.75 24.000000 0|24.000000 70 0.75 24.500000 0|24.500000 62 0.75 26.000000 0|26.000000 70 0.75 26.500000 0|26.500000 63 0.75 27.500000 0|27.500000 67 0.75 28.000000 0|28.000000 63 0.75 29.000000 0|29.000000 75 0.75 29.500000 0|29.500000 74 0.75 30.000000 0|30.000000 75 0.75 30.500000 0|30.500000 67 0.75 32.000000 0","toneType":"Bassline","tempo":140.0,"id":"37c7b059-cf62-4f34-9936-788ebeb51fae","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["MELODY","length-8","140.0","bassline","sixteenth"]},"index":-1},{"machineName":"STRINGS","bankIndex":0,"patternIndex":0,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 60 0.75 32.000000 0|0.000000 55 0.75 32.000000 0|0.000000 63 0.75 32.000000 0|0.000000 48 0.75 32.000000 0","toneType":"PCMSynth","tempo":140.0,"id":"99f0526f-226e-4936-97e4-c1623ddb7f70","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["STRINGS","length-8","140.0","pcmsynth","sixteenth"]},"index":-1},{"machineName":"STRINGS","bankIndex":0,"patternIndex":1,"resolution":"SIXTEENTH","length":4,"noteData":"0.000000 55 0.75 8.000000 0|0.000000 63 0.75 8.000000 0|0.000000 48 0.75 8.000000 0|0.000000 60 0.75 8.000000 0|8.000000 51 0.75 16.000000 0|8.000000 63 0.75 16.000000 0|8.000000 44 0.75 16.000000 0|8.000000 60 0.75 16.000000 0","toneType":"PCMSynth","tempo":140.0,"id":"90780d9a-b45f-4b42-a1b8-071da75fc2db","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["STRINGS","length-4","140.0","pcmsynth","sixteenth"]},"index":-1},{"machineName":"STRINGS","bankIndex":0,"patternIndex":2,"resolution":"SIXTEENTH","length":4,"noteData":"0.000000 60 0.75 8.000000 0|0.000000 63 0.75 8.000000 0|0.000000 68 0.75 8.000000 0|0.000000 56 0.75 8.000000 0|8.000000 60 0.75 16.000000 0|8.000000 67 0.75 16.000000 0|8.000000 63 0.75 16.000000 0|8.000000 48 0.75 16.000000 0","toneType":"PCMSynth","tempo":140.0,"id":"c19f7972-4c4b-4ed6-a840-d68baf7b4678","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["STRINGS","length-4","140.0","pcmsynth","sixteenth"]},"index":-1},{"machineName":"STRINGS","bankIndex":0,"patternIndex":3,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 63 0.75 8.000000 0|0.000000 67 0.75 8.000000 0|0.000000 60 0.75 8.000000 0|0.000000 48 0.75 8.000000 0|8.000000 60 0.75 16.000000 0|8.000000 68 0.75 16.000000 0|8.000000 63 0.75 16.000000 0|8.000000 56 0.75 16.000000 0|16.000000 65 0.75 20.000000 0|16.000000 68 0.75 20.000000 0|16.000000 60 0.75 20.000000 0|16.000000 56 0.75 20.000000 0|20.000000 56 0.75 24.000000 0|20.000000 60 0.75 24.000000 0|20.000000 63 0.75 24.000000 0|20.000000 68 0.75 24.000000 0|24.000000 55 0.75 32.000000 0|24.000000 60 0.75 32.000000 0|24.000000 63 0.75 32.000000 0|24.000000 67 0.75 32.000000 0","toneType":"PCMSynth","tempo":140.0,"id":"9c6b9da8-2dae-418f-a7d8-65911d0312f8","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["STRINGS","length-8","140.0","pcmsynth","sixteenth"]},"index":-1},{"machineName":"DRUMSIES","bankIndex":0,"patternIndex":0,"resolution":"SIXTEENTH","length":1,"noteData":"0.000000 48 0.75 0.250000 0|0.000000 53 0.75 0.250000 0|0.500000 53 0.75 0.750000 0|0.500000 51 0.75 0.750000 0|1.000000 53 0.75 1.250000 0|1.000000 48 0.75 1.250000 0|1.000000 49 0.75 1.250000 0|1.500000 51 0.75 1.750000 0|1.500000 53 0.75 1.750000 0|2.000000 48 0.75 2.250000 0|2.000000 53 0.75 2.250000 0|2.500000 53 0.75 2.750000 0|2.500000 51 0.75 2.750000 0|3.000000 49 0.75 3.250000 0|3.000000 53 0.75 3.250000 0|3.000000 48 0.75 3.250000 0|3.500000 53 0.75 3.750000 0|3.500000 51 0.75 3.750000 0","toneType":"Beatbox","tempo":140.0,"id":"ac3de223-b169-4b27-bba7-ff7abbe60bc3","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DRUMSIES","length-1","140.0","beatbox","sixteenth"]},"index":-1},{"machineName":"DRUMSIES","bankIndex":0,"patternIndex":1,"resolution":"SIXTEENTH","length":1,"noteData":"0.000000 48 0.75 0.250000 0|1.000000 48 0.75 1.250000 0|2.000000 48 0.75 2.250000 0|3.000000 48 0.75 3.250000 0","toneType":"Beatbox","tempo":140.0,"id":"20fcfb44-4cc5-4629-b346-5a71eff67818","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DRUMSIES","length-1","140.0","beatbox","sixteenth"]},"index":-1},{"machineName":"DRUMSIES","bankIndex":0,"patternIndex":2,"resolution":"SIXTEENTH","length":8,"noteData":"0.000000 49 0.75 0.250000 0|1.000000 49 0.75 1.250000 0|2.000000 49 0.75 2.250000 0|3.000000 49 0.75 3.250000 0|4.000000 49 0.75 4.250000 0|5.000000 49 0.75 5.250000 0|6.000000 49 0.75 6.250000 0|7.000000 49 0.75 7.250000 0|7.500000 49 0.75 7.750000 0|8.000000 49 0.75 8.250000 0|9.000000 49 0.75 9.250000 0|10.000000 49 0.75 10.250000 0|11.000000 49 0.75 11.250000 0|12.000000 49 0.75 12.250000 0|13.000000 49 0.75 13.250000 0|14.000000 49 0.75 14.250000 0|15.000000 49 0.75 15.250000 0|15.750000 49 0.75 16.000000 0|16.000000 49 0.75 16.250000 0|16.500000 49 0.40 16.750000 0|17.000000 49 0.75 17.250000 0|17.500000 49 0.40 17.750000 0|18.000000 49 0.75 18.250000 0|18.500000 49 0.40 18.750000 0|19.000000 49 0.75 19.250000 0|19.500000 49 0.40 19.750000 0|20.000000 49 0.75 20.250000 0|20.500000 49 0.40 20.750000 0|21.000000 49 0.75 21.250000 0|21.500000 49 0.40 21.750000 0|22.000000 49 0.75 22.250000 0|22.500000 49 0.50 22.750000 0|23.000000 49 0.75 23.250000 0|23.500000 49 0.75 23.750000 0|24.000000 49 0.75 24.250000 0|24.250000 49 0.75 24.500000 0|24.500000 49 0.75 24.750000 0|24.750000 49 0.75 25.000000 0|25.000000 49 0.75 25.250000 0|25.250000 49 0.75 25.500000 0|25.500000 49 0.75 25.750000 0|25.750000 49 0.75 26.000000 0|26.000000 49 0.75 26.250000 0|26.250000 49 0.75 26.500000 0|26.500000 49 0.75 26.750000 0|26.750000 49 0.75 27.000000 0|27.000000 49 0.75 27.250000 0|27.250000 49 0.75 27.500000 0|27.500000 49 0.75 27.750000 0|27.750000 49 0.75 28.000000 0|28.000000 49 0.75 28.250000 0|28.250000 49 0.75 28.500000 0|28.500000 49 0.75 28.750000 0|28.750000 49 0.75 29.000000 0|29.000000 49 0.75 29.250000 0|29.250000 49 0.75 29.500000 0|29.500000 49 0.75 29.750000 0|29.750000 49 0.75 30.000000 0|30.000000 49 0.75 30.250000 0|30.250000 49 0.75 30.500000 0|30.500000 49 0.75 30.750000 0|30.750000 49 0.75 31.000000 0|31.000000 49 0.75 31.250000 0|31.250000 49 0.75 31.500000 0|31.500000 49 0.75 31.750000 0|31.750000 49 0.75 32.000000 0","toneType":"Beatbox","tempo":140.0,"id":"071559d3-8d13-4834-9dd9-f61531dad380","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DRUMSIES","length-8","140.0","beatbox","sixteenth"]},"index":-1},{"machineName":"DRUMSIES","bankIndex":0,"patternIndex":3,"resolution":"SIXTEENTH","length":1,"noteData":"0.000000 48 0.75 0.250000 0|0.000000 53 0.75 0.250000 0|0.000000 54 0.75 0.250000 0|0.500000 51 0.75 0.750000 0|0.500000 53 0.75 0.750000 0|1.000000 48 0.75 1.250000 0|1.000000 53 0.75 1.250000 0|1.000000 49 0.75 1.250000 0|1.500000 51 0.75 1.750000 0|1.500000 53 0.75 1.750000 0|2.000000 48 0.75 2.250000 0|2.000000 53 0.75 2.250000 0|2.500000 51 0.75 2.750000 0|2.500000 53 0.75 2.750000 0|3.000000 53 0.75 3.250000 0|3.000000 49 0.75 3.250000 0|3.000000 48 0.75 3.250000 0|3.500000 51 0.75 3.750000 0|3.500000 53 0.75 3.750000 0","toneType":"Beatbox","tempo":140.0,"id":"86eb66fa-73c8-411e-ab15-930576944af8","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DRUMSIES","length-1","140.0","beatbox","sixteenth"]},"index":-1},{"machineName":"DRUMSIES","bankIndex":0,"patternIndex":4,"resolution":"SIXTEENTH","length":1,"noteData":"0.000000 48 0.75 0.250000 0|0.500000 51 0.75 0.750000 0|1.000000 48 0.75 1.250000 0|1.500000 51 0.75 1.750000 0|2.000000 48 0.75 2.250000 0|2.500000 51 0.75 2.750000 0|3.000000 48 0.75 3.250000 0|3.500000 51 0.75 3.750000 0","toneType":"Beatbox","tempo":140.0,"id":"73574c49-6ba5-4fe7-83fa-0ff685f90b8b","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DRUMSIES","length-1","140.0","beatbox","sixteenth"]},"index":-1},{"machineName":"DRUMSIES","bankIndex":0,"patternIndex":5,"resolution":"SIXTEENTH","length":1,"noteData":"0.000000 53 0.75 0.250000 0|0.000000 48 0.75 0.250000 0|0.500000 53 0.75 0.750000 0|0.500000 51 0.75 0.750000 0|1.000000 49 0.75 1.250000 0|1.000000 48 0.75 1.250000 0|1.000000 53 0.75 1.250000 0|1.500000 53 0.75 1.750000 0|1.500000 51 0.75 1.750000 0|2.000000 53 0.75 2.250000 0|2.000000 48 0.75 2.250000 0|2.500000 51 0.75 2.750000 0|2.500000 53 0.75 2.750000 0|2.500000 49 0.75 2.750000 0|3.000000 53 0.75 3.250000 0|3.000000 49 0.75 3.250000 0|3.000000 48 0.75 3.250000 0|3.500000 53 0.75 3.750000 0|3.500000 51 0.75 3.750000 0|3.500000 49 0.75 3.750000 0|3.750000 49 0.75 4.000000 0","toneType":"Beatbox","tempo":140.0,"id":"6b83473d-049d-4905-986c-a8354e978e3f","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DRUMSIES","length-1","140.0","beatbox","sixteenth"]},"index":-1},{"machineName":"DRUMSIES","bankIndex":0,"patternIndex":6,"resolution":"SIXTEENTH","length":1,"noteData":"0.000000 48 0.75 0.250000 0","toneType":"Beatbox","tempo":140.0,"id":"6426e43f-3f1e-4be7-834e-7b22db6c2063","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DRUMSIES","length-1","140.0","beatbox","sixteenth"]},"index":-1}],"scenes":[{"soundSourceState":{"descriptors":{}},"soundMixerState":{"index":-1},"effectMixerState":{},"id":"7f254d30-4889-49db-9dbe-92a895c3e07a","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["DefaultScene"]},"index":-1},{"soundSourceState":{"descriptors":{"0":{"index":0,"name":"LOWLEAD","toneType":"SubSynth"},"1":{"index":1,"name":"HIGHLEAD","toneType":"SubSynth"},"2":{"index":2,"name":"BASS","toneType":"SubSynth"},"3":{"index":3,"name":"MELODY","toneType":"Bassline"},"4":{"index":4,"name":"STRINGS","toneType":"PCMSynth"},"5":{"index":5,"name":"DRUMSIES","toneType":"Beatbox"}}},"soundMixerState":{"data":"{\"masterMixer\":{\"equalizer\":{\"bass\":0.0,\"bassMidFreq\":0.0,\"mid\":0.0,\"midHighFreq\":0.0,\"high\":0.0,\"bypass\":false},\"limiter\":{\"pre\":-42.0,\"attack\":-42.0,\"release\":-42.0,\"post\":-42.0,\"bypass\":false},\"delay\":{\"steps\":-42,\"loop\":-42,\"time\":-42,\"sync\":-42,\"feedback\":-42.0,\"feedbackFirst\":-42,\"damping\":-42.0,\"wet\":-42.0,\"bypass\":false},\"reverb\":{\"preDelay\":-42.0,\"roomSize\":-42.0,\"hfDamping\":-42.0,\"diffuse\":-42.0,\"ditherEchoes\":-42,\"erGain\":-42.0,\"erDecay\":-42.0,\"stereoDelay\":-42.0,\"stereoSpread\":-42.0,\"wet\":-42.0,\"bypass\":false},\"volume\":1.0},\"channels\":{\"0\":{\"index\":0,\"bass\":-0.008696437,\"mid\":1.1920929E-7,\"high\":0.06956482,\"delaySend\":0.504348,\"reverbSend\":0.05869567,\"pan\":0.0,\"stereoWidth\":0.99609375,\"mute\":false,\"solo\":false,\"volume\":1.2782611},\"1\":{\"index\":1,\"bass\":-1.0,\"mid\":0.034783125,\"high\":0.02666676,\"delaySend\":0.5000001,\"reverbSend\":0.0,\"pan\":0.0,\"stereoWidth\":0.0,\"mute\":false,\"solo\":false,\"volume\":1.3652176},\"2\":{\"index\":2,\"bass\":1.0,\"mid\":0.0,\"high\":-0.008695722,\"delaySend\":0.0,\"reverbSend\":0.0,\"pan\":0.0,\"stereoWidth\":0.0,\"mute\":false,\"solo\":false,\"volume\":1.2782608},\"3\":{\"index\":3,\"bass\":-1.0,\"mid\":-0.026086628,\"high\":-2.9802322E-7,\"delaySend\":0.52173936,\"reverbSend\":0.07173912,\"pan\":0.18260896,\"stereoWidth\":0.0,\"mute\":false,\"solo\":false,\"volume\":1.4383385},\"4\":{\"index\":4,\"bass\":-0.017391145,\"mid\":0.0,\"high\":0.38260782,\"delaySend\":0.0,\"reverbSend\":0.07391316,\"pan\":0.0,\"stereoWidth\":0.53269273,\"mute\":false,\"solo\":false,\"volume\":1.3913045},\"5\":{\"index\":5,\"bass\":1.0,\"mid\":-0.008694708,\"high\":5.9604645E-7,\"delaySend\":0.0,\"reverbSend\":0.0,\"pan\":0.0,\"stereoWidth\":0.99609375,\"mute\":false,\"solo\":false,\"volume\":0.99999934}}}","index":-1},"effectMixerState":{},"id":"139f10b5-ec5f-4716-a36f-2f3a3ee2111e","metadataInfo":{"name":"Untitled","author":"Unamed","description":"","tags":["PULSAR"]},"index":-1}]}
--------------------------------------------------------------------------------