gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

weWaveTreeItem.cpp (7654B)


      1 #include "weWaveTreeItem.h"
      2 
      3 #include "weWaveCategoryTreeItem.h"
      4 #include "weWaveDesc.h"
      5 #include "xtEditor.h"
      6 #include "xtWaveEditor.h"
      7 
      8 #include "juceUiLib/messageBox.h"
      9 
     10 #include "synthLib/midiToSysex.h"
     11 
     12 namespace xtJucePlugin
     13 {
     14 	WaveTreeItem::WaveTreeItem(WaveEditor& _editor, const WaveCategory _category, const xt::WaveId _waveIndex)
     15 		: m_editor(_editor)
     16 		, m_category(_category)
     17 		, m_waveIndex(_waveIndex)
     18 	{
     19 		m_onWaveChanged.set(m_editor.getData().onWaveChanged, [this](const xt::WaveId& _waveIndex)
     20 		{
     21 			onWaveChanged(_waveIndex);
     22 		});
     23 
     24 		setText(getWaveName(_waveIndex));
     25 	}
     26 
     27 	void WaveTreeItem::paintWave(const xt::WaveData& _data, juce::Graphics& _g, const int _x, const int _y, const int _width, const int _height, const juce::Colour& _colour)
     28 	{
     29 		_g.setColour(_colour);
     30 
     31 		const float scaleX = static_cast<float>(_width)  / static_cast<float>(_data.size());
     32 		const float scaleY = static_cast<float>(_height) / static_cast<float>(256);
     33 
     34 		for(uint32_t x=1; x<_data.size(); ++x)
     35 		{
     36 			const auto x0 = static_cast<float>(x - 1) * scaleX + static_cast<float>(_x);
     37 			const auto x1 = static_cast<float>(x    ) * scaleX + static_cast<float>(_x);
     38 
     39 			const auto y0 = static_cast<float>(255 - (_data[x - 1] + 128)) * scaleY + static_cast<float>(_y);
     40 			const auto y1 = static_cast<float>(255 - (_data[x    ] + 128)) * scaleY + static_cast<float>(_y);
     41 
     42 			_g.drawLine(x0, y0, x1, y1);
     43 		}
     44 	}
     45 
     46 	std::string WaveTreeItem::getWaveName(const xt::WaveId _waveIndex)
     47 	{
     48 		if(!xt::wave::isValidWaveIndex(_waveIndex.rawId()))
     49 			return {};
     50 		const auto category = getCategory(_waveIndex);
     51 		return WaveCategoryTreeItem::getCategoryName(category) + ' ' + std::to_string(_waveIndex.rawId());
     52 	}
     53 
     54 	WaveCategory WaveTreeItem::getCategory(const xt::WaveId _waveIndex)
     55 	{
     56 		if(_waveIndex.rawId() < xt::wave::g_romWaveCount)
     57 			return WaveCategory::Rom;
     58 		if(_waveIndex.rawId() >= xt::wave::g_firstRamWaveIndex && _waveIndex.rawId() < xt::wave::g_firstRamWaveIndex + xt::wave::g_ramWaveCount)
     59 			return WaveCategory::User;
     60 		return WaveCategory::Invalid;
     61 	}
     62 
     63 	void WaveTreeItem::itemSelectionChanged(bool isNowSelected)
     64 	{
     65 		TreeItem::itemSelectionChanged(isNowSelected);
     66 
     67 		if(isNowSelected)
     68 			m_editor.setSelectedWave(m_waveIndex);
     69 	}
     70 
     71 	juce::var WaveTreeItem::getDragSourceDescription()
     72 	{
     73 		auto* desc = new WaveDesc(m_editor);
     74 
     75 		desc->waveIds = {m_waveIndex};
     76 		desc->source = WaveDescSource::WaveList;
     77 
     78 		desc->fillData(m_editor.getData());
     79 
     80 		return desc;
     81 	}
     82 
     83 	bool WaveTreeItem::isInterestedInDragSource(const juce::DragAndDropTarget::SourceDetails& dragSourceDetails)
     84 	{
     85 		if(xt::wave::isReadOnly(m_waveIndex))
     86 			return false;
     87 		const auto* waveDesc = WaveDesc::fromDragSource(dragSourceDetails);
     88 		if(!waveDesc)
     89 			return false;
     90 		if(waveDesc->waveIds.size() != 1)
     91 			return false;
     92 		return true;
     93 	}
     94 
     95 	void WaveTreeItem::itemDropped(const juce::DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex)
     96 	{
     97 		TreeItem::itemDropped(dragSourceDetails, insertIndex);
     98 		if(xt::wave::isReadOnly(m_waveIndex))
     99 			return;
    100 		const auto* waveDesc = WaveDesc::fromDragSource(dragSourceDetails);
    101 		if(!waveDesc)
    102 			return;
    103 		auto& data = m_editor.getData();
    104 
    105 		if(data.copyWave(m_waveIndex, waveDesc->waveIds.front()))
    106 		{
    107 			setSelected(true, true, juce::dontSendNotification);
    108 			data.sendWaveToDevice(m_waveIndex);
    109 		}
    110 	}
    111 
    112 	bool WaveTreeItem::isInterestedInFileDrag(const juce::StringArray& files)
    113 	{
    114 		if(xt::wave::isReadOnly(m_waveIndex))
    115 			return false;
    116 
    117 		if (files.size() != 1)
    118 			return false;
    119 
    120 		const auto& f = files[0];
    121 
    122 		if(f.endsWithIgnoreCase(".mid") || f.endsWithIgnoreCase(".syx") || f.endsWithIgnoreCase(".wav"))
    123 			return true;
    124 
    125 		return TreeItem::isInterestedInFileDrag(files);
    126 	}
    127 
    128 	void WaveTreeItem::filesDropped(const juce::StringArray& files, int insertIndex)
    129 	{
    130 		if(xt::wave::isReadOnly(m_waveIndex))
    131 			return;
    132 
    133 		if (files.isEmpty())
    134 			return;
    135 
    136 		if (files[0].endsWithIgnoreCase(".wav"))
    137 		{
    138 			if (auto wave = m_editor.importWaveFile(files[0].toStdString()))
    139 			{
    140 				m_editor.getData().setWave(m_waveIndex, *wave);
    141 				m_editor.getData().sendWaveToDevice(m_waveIndex);
    142 			}
    143 			return;
    144 		}
    145 
    146 		const auto errorTitle = m_editor.getEditor().getProcessor().getProperties().name + " - Error";
    147 
    148 		std::map<xt::WaveId, xt::WaveData> waves;
    149 		std::map<xt::TableId, xt::TableData> tables;
    150 		m_editor.filesDropped(waves, tables, files);
    151 
    152 		if (waves.empty())
    153 		{
    154 			if (tables.size() == 1)
    155 				genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, errorTitle, "This file doesn't contain a Wave but a Control Table, please drop on a User Table slot.");
    156 
    157 			return;
    158 		}
    159 
    160 		m_editor.getData().setWave(m_waveIndex, waves.begin()->second);
    161 		m_editor.getData().sendWaveToDevice(m_waveIndex);
    162 	}
    163 
    164 	std::vector<std::vector<uint8_t>> WaveTreeItem::getSysexFromFiles(const juce::StringArray& _files)
    165 	{
    166 		std::vector<std::vector<uint8_t>> sysex;
    167 
    168 		for(const auto& file : _files)
    169 		{
    170 			std::vector<std::vector<uint8_t>> s;
    171 			synthLib::MidiToSysex::extractSysexFromFile(s, file.toStdString());
    172 			sysex.insert(sysex.end(), s.begin(), s.end());
    173 		}
    174 
    175 		return sysex;
    176 	}
    177 
    178 	void WaveTreeItem::itemClicked(const juce::MouseEvent& _mouseEvent)
    179 	{
    180 		if(!_mouseEvent.mods.isPopupMenu())
    181 		{
    182 			TreeItem::itemClicked(_mouseEvent);
    183 			return;
    184 		}
    185 
    186 		juce::PopupMenu menu;
    187 
    188 		const auto selectedTableId = m_editor.getSelectedTable();
    189 
    190 		if(selectedTableId.isValid())
    191 		{
    192 			const auto subMenuCT = m_editor.createCopyToSelectedTableMenu(m_waveIndex);
    193 			menu.addSubMenu("Copy to current Control Table...", subMenuCT);
    194 		}
    195 
    196 		const auto subMenuUW = WaveEditor::createRamWavesPopupMenu([this](const xt::WaveId _dest)
    197 		{
    198 			if (m_editor.getData().copyWave(_dest, m_waveIndex))
    199 			{
    200 				m_editor.getData().sendWaveToDevice(_dest);
    201 				m_editor.setSelectedWave(_dest);
    202 			}
    203 		});
    204 		menu.addSubMenu("Copy to User Wave...", subMenuUW);
    205 		menu.addSeparator();
    206 
    207 		if (auto wave = m_editor.getData().getWave(m_waveIndex))
    208 		{
    209 			auto w = *wave;
    210 
    211 			juce::PopupMenu exportMenu;
    212 
    213 			if (xt::wave::isReadOnly(m_waveIndex))
    214 			{
    215 				// we cannot export a .mid or .syx that is a rom location as the hardware cannot import it
    216 				auto subMenuSyx = WaveEditor::createRamWavesPopupMenu([this, w](const xt::WaveId _id)
    217 				{
    218 					m_editor.exportAsSyx(_id, w);
    219 				});
    220 				exportMenu.addSubMenu(".syx", subMenuSyx);
    221 				auto subMenuMid = WaveEditor::createRamWavesPopupMenu([this, w](const xt::WaveId _id)
    222 				{
    223 					m_editor.exportAsMid(_id, w);
    224 				});
    225 				exportMenu.addSubMenu(".mid", subMenuMid);
    226 			}
    227 			else
    228 			{
    229 				exportMenu.addItem(".syx", [this, w]
    230 				{
    231 					m_editor.exportAsSyx(m_waveIndex, w);
    232 				});
    233 				exportMenu.addItem(".mid", [this, w]
    234 				{
    235 					m_editor.exportAsMid(m_waveIndex, w);
    236 				});
    237 			}
    238 			exportMenu.addItem(".wav", [this, w]
    239 			{
    240 				m_editor.exportAsWav(w);
    241 			});
    242 			menu.addSubMenu("Export as...", exportMenu);
    243 		}
    244 
    245 		if (!xt::wave::isReadOnly(m_waveIndex))
    246 		{
    247 			menu.addSeparator();
    248 			menu.addItem("Import .syx/.mid...", [this]
    249 			{
    250 				m_editor.selectImportFile([this](const juce::String& _filename)
    251 				{
    252 					juce::StringArray files;
    253 					files.add(_filename);
    254 					filesDropped(files, 0);
    255 				});
    256 			});
    257 		}
    258 
    259 		menu.showMenuAsync({});
    260 	}
    261 
    262 	void WaveTreeItem::onWaveChanged(const xt::WaveId _index) const
    263 	{
    264 		if(_index != m_waveIndex)
    265 			return;
    266 		onWaveChanged();
    267 	}
    268 
    269 	void WaveTreeItem::onWaveChanged() const
    270 	{
    271 		repaintItem();
    272 	}
    273 
    274 	void WaveTreeItem::paintItem(juce::Graphics& g, int width, int height)
    275 	{
    276 		if(const auto wave = m_editor.getData().getWave(m_waveIndex))
    277 			paintWave(*wave, g, width>>1, 0, width>>1, height, juce::Colour(0xffffffff));
    278 
    279 		TreeItem::paintItem(g, width, height);
    280 	}
    281 }