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 }