Skip to content

Commit

Permalink
Instrument presets can now be copied from the clipboard or dragged in…
Browse files Browse the repository at this point in the history
…to the application
  • Loading branch information
igorski committed Feb 27, 2022
1 parent 5b41fc5 commit 11aa4f3
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 85 deletions.
72 changes: 30 additions & 42 deletions src/components/instrument-editor/instrument-editor.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Igor Zinken 2019-2021 - https://www.igorski.nl
* Igor Zinken 2019-2022 - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the 'Software'), to deal in
Expand Down Expand Up @@ -85,13 +85,14 @@
<hr class="divider" />
<!-- current preset -->
<div class="current-preset">
<input v-model="presetName"
class="preset-name-input"
type="text"
:placeholder="$t('presetName')"
@focus="handleFocusIn"
@blur="handleFocusOut"
@keyup.enter="savePreset"
<input
v-model="presetName"
class="preset-name-input"
type="text"
:placeholder="$t('presetName')"
@focus="handleFocusIn"
@blur="handleFocusOut"
@keyup.enter="savePreset"
/>
<button v-t="'savePreset'"
type="button"
Expand All @@ -104,12 +105,12 @@
<script>
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
import Config from "@/config";
import Actions from "@/definitions/actions";
import ManualURLs from "@/definitions/manual-urls";
import ModalWindows from "@/definitions/modal-windows";
import AudioService from "@/services/audio-service";
import PubSubMessages from "@/services/pubsub/messages";
import createAction from "@/model/factories/action-factory";
import InstrumentFactory from "@/model/factories/instrument-factory";
import { enqueueState } from "@/model/factories/history-state-factory";
import { clone } from "@/utils/object-util";
import SelectBox from "@/components/forms/select-box";
import OscillatorEditor from "./components/oscillator-editor/oscillator-editor";
Expand All @@ -128,7 +129,6 @@ export default {
data: () => ({
oscillatorAmount: Config.OSCILLATOR_AMOUNT,
currentPreset: null,
presetName: '',
}),
computed: {
...mapState({
Expand All @@ -141,7 +141,7 @@ export default {
}),
...mapGetters([
"getInstrumentByPresetName",
"hasMidiSupport"
"hasMidiSupport",
]),
instrument: {
get() {
Expand All @@ -161,6 +161,14 @@ export default {
instrumentRef() {
return this.activeSong.instruments[this.selectedInstrument];
},
presetName: {
get() {
return this.instrumentRef?.presetName;
},
set( presetName ) {
this.setPresetName({ instrument: this.instrumentRef, presetName });
},
},
presets() {
const out = [
...this.instruments,
Expand Down Expand Up @@ -192,36 +200,16 @@ export default {
}
let instrumentPreset;
try {
instrumentPreset = await this.loadInstrument( this.getInstrumentByPresetName( selectedPresetName ));
instrumentPreset = await this.loadInstrumentFromLS( this.getInstrumentByPresetName( selectedPresetName ));
} catch {
return;
}
const { activeSong } = this;
const instrumentIndex = this.selectedInstrument;
const existingPresetName = this.presetName;
const existingInstrument = clone( activeSong.instruments[ instrumentIndex]);
const newInstrument = InstrumentFactory.loadPreset(
instrumentPreset, instrumentIndex, this.instrumentRef.name
instrumentPreset, this.selectedInstrument, this.instrumentRef.name
);
const store = this.$store;
const applyUpdate = () => {
this.setSelectedOscillatorIndex( 0 );
AudioService.cacheAllOscillators( instrumentIndex, newInstrument );
AudioService.applyModules( activeSong );
};
const commit = () => {
store.commit( "replaceInstrument", { instrumentIndex, instrument: newInstrument });
this.presetName = selectedPresetName;
applyUpdate();
};
commit();
enqueueState( `preset_${instrumentIndex}`, {
undo() {
store.commit( "replaceInstrument", { instrumentIndex, instrument: existingInstrument });
this.presetName = existingPresetName;
applyUpdate();
},
redo: commit,
createAction( Actions.REPLACE_INSTRUMENT, {
store: this.$store,
instrument: newInstrument,
});
},
},
Expand All @@ -247,8 +235,8 @@ export default {
"openModal",
]),
...mapActions([
"loadInstrument",
"saveInstrument",
"loadInstrumentFromLS",
"saveInstrumentIntoLS",
]),
openHelp() {
window.open( ManualURLs.INSTRUMENT_EDITOR_HELP, "_blank" );
Expand All @@ -257,8 +245,8 @@ export default {
this.openModal( ModalWindows.SETTINGS_WINDOW );
},
invalidatePreset() {
if ( this.instrumentRef.presetName && !this.instrumentRef.presetName.includes( "*" )) {
this.presetName = `${this.instrumentRef.presetName}*`;
if ( this.presetName && !this.presetName.includes( "*" )) {
this.setPresetName({ instrument: this.instrumentRef, presetName: `${this.presetName}*` });
}
},
savePreset() {
Expand All @@ -269,7 +257,7 @@ export default {
else {
newPresetName = newPresetName.replace( "*", "" );
this.setPresetName({ instrument: this.instrumentRef, presetName: newPresetName });
if ( this.saveInstrument( clone( this.instrumentRef ) )) {
if ( this.saveInstrumentIntoLS( clone( this.instrumentRef ) )) {
this.showNotification({ message: this.$t( "instrumentSaved", { name: newPresetName }) });
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/definitions/actions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Igor Zinken 2016-2021 - https://www.igorski.nl
* Igor Zinken 2016-2022 - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand Down Expand Up @@ -42,5 +42,6 @@ export default
DELETE_PATTERN : "S:8",
CUT_SELECTION : "S:9",
PASTE_SELECTION : "S:10",
TEMPO_CHANGE : "S:11"
TEMPO_CHANGE : "S:11",
REPLACE_INSTRUMENT : "S:12",
};
8 changes: 6 additions & 2 deletions src/efflux-application.vue
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,8 @@ export default {
this.publishMessage( PubSubMessages.EFFLUX_READY );
// if File content is dragged into the application, parse and load audio files within
const loadFiles = async ({ sounds, projects }) => {
// if File content is dragged or pasted into the application, parse and load supported files within
const loadFiles = async ({ sounds, projects, instruments }) => {
for ( const file of sounds ) {
try {
const buffer = await loadSample( file, getAudioContext());
Expand Down Expand Up @@ -338,6 +338,9 @@ export default {
confirm();
}
}
for ( const instrument of instruments ) {
this.loadInstrumentFromFile( instrument );
}
};
window.addEventListener( "paste", event => {
Expand Down Expand Up @@ -406,6 +409,7 @@ export default {
"prepareSequencer",
"loadStoredSettings",
"loadStoredInstruments",
"loadInstrumentFromFile",
"loadStoredSongs",
"createSong"
]),
Expand Down
31 changes: 30 additions & 1 deletion src/model/factories/action-factory.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The MIT License (MIT)
*
* Igor Zinken 2016-2021 - https://www.igorski.nl
* Igor Zinken 2016-2022 - https://www.igorski.nl
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
Expand All @@ -26,6 +26,8 @@ import EventUtil from "@/utils/event-util";
import { clone } from "@/utils/object-util";
import PatternUtil from "@/utils/pattern-util";
import { ACTION_NOTE_OFF } from "@/model/types/audio-event-def";
import { enqueueState } from "@/model/factories/history-state-factory";
import AudioService from "@/services/audio-service";
import PatternFactory from "./pattern-factory";

/**
Expand Down Expand Up @@ -74,6 +76,9 @@ export default function( type, data ) {

case Actions.PASTE_SELECTION:
return pasteSelectionAction( data );

case Actions.REPLACE_INSTRUMENT:
return replaceInstrumentAction( data );
}
}

Expand Down Expand Up @@ -510,6 +515,30 @@ function pasteSelectionAction({ store }) {
};
}

function replaceInstrumentAction({ store, instrument }) {
const instrumentIndex = ( store.rootState || store.state ).editor.selectedInstrument;
const existingInstrument = clone( store.getters.activeSong.instruments[ instrumentIndex ]);
instrument.index = instrumentIndex;

const applyUpdate = instrument => {
store.commit( "setSelectedOscillatorIndex", 0 );
AudioService.cacheAllOscillators( instrumentIndex, instrument );
AudioService.applyModules( store.getters.activeSong );
};
const commit = () => {
store.commit( "replaceInstrument", { instrumentIndex, instrument });
applyUpdate( instrument );
};
commit();
enqueueState( `preset_${instrumentIndex}`, {
undo() {
store.commit( "replaceInstrument", { instrumentIndex, instrument: existingInstrument });
applyUpdate( existingInstrument );
},
redo: commit,
});
}

// when changing states of observables, we need to take heed to always restore
// a fresh clone from the last state as repeated undo/redo actions on (a cloned)
// object reference will mutate the reference to be different from its initial state
Expand Down
Loading

0 comments on commit 11aa4f3

Please sign in to comment.