1 /*
2 * Copyright (C) 2020 Rerrah
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use,
8 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following
11 * conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "adpcm_sample_editor.hpp"
27 #include "ui_adpcm_sample_editor.h"
28 #include <cmath>
29 #include <limits>
30 #include <vector>
31 #include <algorithm>
32 #include <QMimeData>
33 #include <QFile>
34 #include <QIODevice>
35 #include <QFileInfo>
36 #include <QFileDialog>
37 #include <QStringList>
38 #include <QPainter>
39 #include <QRect>
40 #include <QRectF>
41 #include <QToolBar>
42 #include <QMenu>
43 #include <QToolButton>
44 #include <QWheelEvent>
45 #include <QHoverEvent>
46 #include "chips/codec/ymb_codec.hpp"
47 #include "gui/event_guard.hpp"
48 #include "gui/instrument_editor/sample_length_dialog.hpp"
49 #include "gui/instrument_editor/grid_settings_dialog.hpp"
50 #include "gui/file_io_error_message_box.hpp"
51
ADPCMSampleEditor(QWidget * parent)52 ADPCMSampleEditor::ADPCMSampleEditor(QWidget *parent) :
53 QWidget(parent),
54 ui(new Ui::ADPCMSampleEditor),
55 isIgnoreEvent_(false),
56 zoom_(0),
57 viewedSampLen_(2),
58 gridIntr_(1),
59 cursorSamp_(0, 0),
60 prevPressedSamp_(-1, -1),
61 addrStart_(0),
62 addrStop_(0),
63 sample_(2),
64 drawMode_(DrawMode::Disabled)
65 {
66 ui->setupUi(this);
67
68 auto rkfunc = [&](int dummy) {
69 Q_UNUSED(dummy)
70 if (!isIgnoreEvent_) {
71 int rk = ui->rootKeySpinBox->value() * 12 + ui->rootKeyComboBox->currentIndex();
72 bt_.lock()->setSampleADPCMRootKeyNumber(ui->sampleNumSpinBox->value(), rk);
73 emit sampleParameterChanged(ui->sampleNumSpinBox->value());
74 emit modified();
75 }
76 };
77 // Leave Before Qt5.7.0 style due to windows xp
78 QObject::connect(ui->rootKeyComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, rkfunc);
79 QObject::connect(ui->rootKeySpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, rkfunc);
80
81 ui->memoryWidget->installEventFilter(this);
82 ui->sampleViewWidget->setAttribute(Qt::WA_Hover);
83 ui->sampleViewWidget->installEventFilter(this);
84
85 auto tb = new QToolBar();
86 tb->setIconSize(QSize(16, 16));
87 tb->setMinimumHeight(28);
88 tb->setMaximumHeight(28);
89 tb->addActions({ ui->action_Clear, ui->action_Import, ui->action_Resize });
90 tb->addSeparator();
91 tb->addActions({ ui->actionZoom_In, ui->actionZoom_Out });
92 auto gridMenu = new QMenu();
93 gridMenu->addActions({ ui->action_Grid_View, ui->actionG_rid_Settings });
94 auto gridButton = new QToolButton();
95 gridButton->setPopupMode(QToolButton::MenuButtonPopup);
96 gridButton->setMenu(gridMenu);
97 gridButton->setDefaultAction(ui->action_Grid_View);
98 tb->addWidget(gridButton);
99 tb->addSeparator();
100 auto editMenu = new QMenu();
101 editMenu->addActions({ ui->action_Draw_Sample, ui->actionDirec_t_Draw });
102 auto editButton = new QToolButton();
103 editButton->setPopupMode(QToolButton::MenuButtonPopup);
104 editButton->setMenu(editMenu);
105 editButton->setDefaultAction(ui->action_Draw_Sample);
106 QObject::connect(editButton, &QToolButton::triggered, editButton, &QToolButton::setDefaultAction);
107 tb->addWidget(editButton);
108 tb->addAction(ui->actionRe_verse);
109 ui->verticalLayout_2->insertWidget(0, tb);
110 }
111
~ADPCMSampleEditor()112 ADPCMSampleEditor::~ADPCMSampleEditor()
113 {
114 delete ui;
115 }
116
setCore(std::weak_ptr<BambooTracker> core)117 void ADPCMSampleEditor::setCore(std::weak_ptr<BambooTracker> core)
118 {
119 bt_ = core;
120 }
121
setConfiguration(std::weak_ptr<Configuration> config)122 void ADPCMSampleEditor::setConfiguration(std::weak_ptr<Configuration> config)
123 {
124 config_ = config;
125 }
126
setColorPalette(std::shared_ptr<ColorPalette> palette)127 void ADPCMSampleEditor::setColorPalette(std::shared_ptr<ColorPalette> palette)
128 {
129 palette_ = palette;
130 }
131
getSampleNumber() const132 int ADPCMSampleEditor::getSampleNumber() const
133 {
134 return ui->sampleNumSpinBox->value();
135 }
136
dragEnterEvent(QDragEnterEvent * event)137 void ADPCMSampleEditor::dragEnterEvent(QDragEnterEvent* event)
138 {
139 const QMimeData* mime = event->mimeData();
140 if (mime->hasUrls() && mime->urls().length() == 1
141 && QFileInfo(mime->urls().first().toLocalFile()).suffix().toLower() == "wav") {
142 event->acceptProposedAction();
143 }
144 }
145
eventFilter(QObject * obj,QEvent * ev)146 bool ADPCMSampleEditor::eventFilter(QObject* obj, QEvent* ev)
147 {
148 if (obj == ui->memoryWidget) {
149 switch (ev->type()) {
150 case QEvent::Paint:
151 {
152 QPainter painter(ui->memoryWidget);
153 painter.drawPixmap(ui->memoryWidget->rect(), memPixmap_);
154 break;
155 }
156 case QEvent::Resize:
157 memPixmap_ = QPixmap(ui->memoryWidget->size());
158 updateSampleMemoryBar();
159 break;
160 default:
161 break;
162 }
163 return false;
164 }
165
166 if (obj == ui->sampleViewWidget) {
167 switch (ev->type()) {
168 case QEvent::Paint:
169 {
170 QPainter painter(ui->sampleViewWidget);
171 painter.drawPixmap(ui->sampleViewWidget->rect(), sampViewPixmap_);
172 break;
173 }
174 case QEvent::Resize:
175 sampViewPixmap_ = QPixmap(ui->sampleViewWidget->size());
176 updateSampleView();
177 break;
178 case QEvent::Wheel:
179 {
180 auto we = dynamic_cast<QWheelEvent*>(ev);
181 int cnt = we->angleDelta().y() / 120;
182 bool ctrl = we->modifiers().testFlag(Qt::ControlModifier);
183 if (cnt > 0) {
184 for (int i = 0; i < cnt; ++i) {
185 if (ctrl) on_actionZoom_In_triggered();
186 else ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->value() - 1);
187 }
188 }
189 else {
190 for (int i = 0; cnt < i; --i) {
191 if (ctrl) on_actionZoom_Out_triggered();
192 else ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->value() + 1);
193 }
194 }
195 break;
196 }
197 case QEvent::HoverMove:
198 {
199 auto pos = dynamic_cast<QHoverEvent*>(ev)->pos();
200 detectCursorSamplePosition(pos.x(), pos.y());
201
202 if (prevPressedSamp_.x() != -1) { // Change sample
203 const int px = prevPressedSamp_.x();
204 const int py = prevPressedSamp_.y();
205 const int cx = cursorSamp_.x();
206 const int cy = cursorSamp_.y();
207 if (px < cx) {
208 const int dx = cx - px;
209 const int dy = cy - py;
210 for (int x = px + 1; x <= cx; ++x)
211 sample_.at(x) = (x - px) * dy / dx + py;
212 }
213 else if (px == cx) {
214 sample_.at(cx) = cy;
215 }
216 else {
217 const int dx = px - cx;
218 const int dy = py - cy;
219 for (int x = cx; x < px; ++x)
220 sample_.at(x) = (x - cx) * dy / dx + cy;
221 }
222 prevPressedSamp_ = cursorSamp_;
223
224 if (drawMode_ == DrawMode::Direct) {
225 sendEditedSample();
226 }
227 else {
228 updateSampleView();
229 ui->sampleViewWidget->update();
230 }
231 }
232 break;
233 }
234 case QEvent::MouseButtonPress:
235 {
236 if (drawMode_ == DrawMode::Disabled) break;
237 sample_.at(cursorSamp_.x()) = cursorSamp_.y();
238 prevPressedSamp_ = cursorSamp_;
239
240 if (drawMode_ == DrawMode::Direct) {
241 sendEditedSample();
242 }
243 else {
244 updateSampleView();
245 ui->sampleViewWidget->update();
246 }
247 break;
248 }
249 case QEvent::MouseButtonRelease:
250 {
251 prevPressedSamp_.setX(-1); // Clear
252 break;
253 }
254 default:
255 break;
256 }
257 return false;
258 }
259
260 return false;
261 }
262
dropEvent(QDropEvent * event)263 void ADPCMSampleEditor::dropEvent(QDropEvent* event)
264 {
265 importSampleFrom(reinterpret_cast<QDropEvent*>(event)->mimeData()->urls().first().toLocalFile());
266 }
267
setInstrumentSampleParameters(int sampNum,bool repeatable,int rKeyNum,int rDeltaN,size_t start,size_t stop,std::vector<uint8_t> sample)268 void ADPCMSampleEditor::setInstrumentSampleParameters(int sampNum, bool repeatable, int rKeyNum,
269 int rDeltaN, size_t start, size_t stop,
270 std::vector<uint8_t> sample)
271 {
272 Ui::EventGuard ev(isIgnoreEvent_);
273
274 ui->sampleNumSpinBox->setValue(sampNum);
275
276 updateUsersView();
277
278 ui->repeatCheckBox->setChecked(repeatable);
279 ui->rootKeyComboBox->setCurrentIndex(rKeyNum % 12);
280 ui->rootKeySpinBox->setValue(rKeyNum / 12);
281 ui->rootRateSpinBox->setValue(rDeltaN);
282
283 addrStart_ = start;
284 addrStop_ = stop;
285 updateSampleMemoryBar();
286 ui->memoryWidget->update();
287
288 if (!ui->action_Draw_Sample->isChecked()) {
289 sample_.resize(sample.size() * 2);
290 codec::ymb_decode(sample.data(), sample_.data(), static_cast<long>(sample_.size()));
291
292 // Slider settings
293 for (int z = 0, len = sample_.size(); ; ++z) {
294 int tmp = (len + 1) >> 1;
295 if (tmp < 2) {
296 zoom_ = z;
297 viewedSampLen_ = len;
298 ui->horizontalScrollBar->setMaximum(sample_.size() - len);
299 break;
300 }
301 else if (z == zoom_) {
302 viewedSampLen_ = len;
303 ui->horizontalScrollBar->setMaximum(sample_.size() - len);
304 break;
305 }
306 else {
307 len = tmp;
308 }
309 }
310 updateSampleView();
311 ui->sampleViewWidget->update();
312 }
313
314 ui->detailLabel->setText(updateDetailView());
315 }
316
importSampleFrom(const QString file)317 void ADPCMSampleEditor::importSampleFrom(const QString file)
318 {
319 std::unique_ptr<WavContainer> wav;
320 try {
321 QFile fp(file);
322 if (!fp.open(QIODevice::ReadOnly)) {
323 FileIOErrorMessageBox::openError(file, true, FileIO::FileType::WAV, this);
324 return;
325 }
326 QByteArray array = fp.readAll();
327 fp.close();
328
329 wav = std::make_unique<WavContainer>(BinaryContainer(std::vector<char>(array.begin(), array.end())));
330 }
331 catch (FileIOError& e) {
332 FileIOErrorMessageBox(file, true, e, this).exec();
333 return;
334 }
335 catch (std::exception& e) {
336 FileIOErrorMessageBox(file, true, FileIO::FileType::WAV, QString(e.what()), this).exec();
337 return;
338 }
339
340 if (wav->getSampleRate() < 2000 || 55466 < wav->getSampleRate()) {
341 FileIOErrorMessageBox(file, true, FileIO::FileType::WAV,
342 tr("Supported sample rate is 2kHz-55.5kHz, but the rate of selected sample is %1.")
343 .arg(wav->getSampleRate()), this).exec();
344 return;
345 }
346
347 if (wav->getChannelCount() != 1) {
348 FileIOErrorMessageBox(file, true, FileIO::FileType::WAV,
349 tr("The selected sample is not mono channel."), this).exec();
350 return;
351 }
352
353 BinaryContainer bc = wav->getSample();
354 size_t rawSize = bc.size() / 2;
355 std::vector<int16_t> raw(rawSize);
356 for (size_t i = 0; i < rawSize; ++i) {
357 raw[i] = bc.readInt16(i * 2);
358 }
359 std::vector<uint8_t> adpcm((raw.size() + 1) / 2);
360 codec::ymb_encode(raw.data(), adpcm.data(), static_cast<long>(raw.size()));
361
362 const int ROOT_KEY = 60; //C5
363
364 bt_.lock()->storeSampleADPCMRawSample(ui->sampleNumSpinBox->value(), adpcm);
365 ui->rootKeyComboBox->setCurrentIndex(ROOT_KEY % 12);
366 ui->rootKeySpinBox->setValue(ROOT_KEY / 12);
367 ui->rootRateSpinBox->setValue(calcADPCMDeltaN(wav->getSampleRate()));
368
369 emit modified();
370 emit sampleAssignRequested();
371 emit sampleParameterChanged(ui->sampleNumSpinBox->value());
372 }
373
updateSampleMemoryBar()374 void ADPCMSampleEditor::updateSampleMemoryBar()
375 {
376 QRect bar = memPixmap_.rect();
377 if (!bar.isValid()) return;
378
379 QPainter painter(&memPixmap_);
380 painter.fillRect(bar, palette_->instADPCMMemBackColor);
381
382 double maxSize = bt_.lock()->getADPCMStoredSize() >> 5;
383 double limit = bt_.lock()->getADPCMLimit() >> 5; // By 32 bytes
384 QRectF allSamp(0, 0, std::max(1., bar.width() * (maxSize / limit)), rect().height());
385 painter.fillRect(allSamp, palette_->instADPCMMemAllColor);
386
387 if (addrStart_ || addrStart_ != addrStop_) {
388 QRectF curSamp(bar.width() * (addrStart_ / limit),
389 0, std::max(1., bar.width() * ((addrStop_ - addrStart_) / limit)), rect().height());
390 painter.fillRect(curSamp, palette_->instADPCMMemCurColor);
391 }
392 }
393
updateSampleView()394 void ADPCMSampleEditor::updateSampleView()
395 {
396 QRect rect = sampViewPixmap_.rect();
397 if (!rect.isValid()) return;
398
399 QPainter painter(&sampViewPixmap_);
400 painter.fillRect(rect, palette_->instADPCMSampViewBackColor);
401
402 painter.setPen(palette_->instADPCMSampViewCenterColor);
403 const int maxX = rect.width();
404 const int centerY = rect.height() >> 1;
405 painter.drawLine(0, centerY, maxX, centerY);
406
407 QColor foreColor;
408 switch (drawMode_) {
409 case DrawMode::Disabled: foreColor = palette_->instADPCMSampViewForeColor; break;
410 case DrawMode::Normal: foreColor = palette_->instADPCMSampViewDrawColor; break;
411 case DrawMode::Direct: foreColor = palette_->instADPCMSampViewDirectDrawColor; break;
412 }
413 painter.setPen(foreColor);
414 const int16_t maxY = std::numeric_limits<int16_t>::max();
415 const size_t first = ui->horizontalScrollBar->value();
416 const bool showGrid = ui->action_Grid_View->isChecked();
417 if (maxX < viewedSampLen_) {
418 int prevY = centerY;
419 size_t g = first;
420 for (int x = 0; x < maxX; ++x) {
421 size_t i = (viewedSampLen_ - 1) * x / (maxX - 1);
422 size_t p = i + first;
423 int16_t sample = sample_.at(p);
424 int y = centerY - (centerY * sample / maxY);
425 if (showGrid && g <= p) {
426 painter.setPen(palette_->instADPCMSampViewGridColor);
427 painter.drawLine(x, 0, x, rect.height());
428 g = (g / gridIntr_ + 1) * gridIntr_;
429 painter.setPen(foreColor);
430 }
431 if (x) painter.drawLine(x - 1, prevY, x, y);
432 prevY = y;
433 }
434 }
435 else {
436 QPoint prev, p;
437 for (int i = 0; i < viewedSampLen_; ++i) {
438 p.setX((maxX - 1) * i / (viewedSampLen_ - 1));
439 int16_t sample = sample_[i + first];
440 p.setY(centerY - (centerY * sample / maxY));
441 if (showGrid && !(i % gridIntr_)) {
442 painter.setPen(palette_->instADPCMSampViewGridColor);
443 painter.drawLine(p.x(), 0, p.x(), rect.height());
444 painter.setPen(foreColor);
445 }
446 if (p.x()) painter.drawLine(prev, p);
447 prev = p;
448 }
449 }
450 }
451
updateUsersView()452 void ADPCMSampleEditor::updateUsersView()
453 {
454 std::vector<int> users = bt_.lock()->getSampleADPCMUsers(ui->sampleNumSpinBox->value());
455 QStringList l;
456 std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) {
457 return QString("%1").arg(n, 2, 16, QChar('0')).toUpper();
458 });
459 ui->usersLineEdit->setText(l.join(","));
460 }
461
detectCursorSamplePosition(int cx,int cy)462 void ADPCMSampleEditor::detectCursorSamplePosition(int cx, int cy)
463 {
464 const QRect& rect = ui->sampleViewWidget->rect();
465
466 // Detect x
467 const int w = rect.width();
468 if (viewedSampLen_ < w) {
469 const double segW = rect.width() / (viewedSampLen_ - 1.);
470 double th = segW / 2.;
471 for (int i = 0; i < viewedSampLen_; ++i, th += segW) {
472 if (cx < th) {
473 cursorSamp_.setX(ui->horizontalScrollBar->value() + i);
474 break;
475 }
476 }
477 }
478 else {
479 cursorSamp_.setX(
480 ui->horizontalScrollBar->value() + (viewedSampLen_ - 1) * clamp(cx, 0, w - 1) / (w - 1));
481 }
482
483 // Detect y
484 const double centerY = rect.height() >> 1;
485 int y = std::numeric_limits<int16_t>::max() * (centerY - cy) / centerY;
486 cursorSamp_.setY(clamp(y, static_cast<int>(std::numeric_limits<int16_t>::min()),
487 static_cast<int>(std::numeric_limits<int16_t>::max())));
488
489 // Update position view
490 ui->detailLabel->setText(updateDetailView());
491 }
492
sendEditedSample()493 void ADPCMSampleEditor::sendEditedSample()
494 {
495 std::vector<uint8_t> adpcm(sample_.size() >> 1);
496 codec::ymb_encode(sample_.data(), adpcm.data(), static_cast<long>(sample_.size()));
497 bt_.lock()->storeSampleADPCMRawSample(ui->sampleNumSpinBox->value(), std::move(adpcm));
498 emit modified();
499 emit sampleAssignRequested();
500 emit sampleParameterChanged(ui->sampleNumSpinBox->value());
501 }
502
onSampleNumberChanged()503 void ADPCMSampleEditor::onSampleNumberChanged()
504 {
505 updateUsersView();
506 }
507
onSampleMemoryUpdated(size_t start,size_t stop)508 void ADPCMSampleEditor::onSampleMemoryUpdated(size_t start, size_t stop)
509 {
510 addrStart_ = start;
511 addrStop_ = stop;
512 updateSampleMemoryBar();
513 ui->memoryWidget->update();
514 }
515
on_sampleNumSpinBox_valueChanged(int arg1)516 void ADPCMSampleEditor::on_sampleNumSpinBox_valueChanged(int arg1)
517 {
518 if (!isIgnoreEvent_) emit sampleNumberChanged(arg1); // Direct connection
519
520 onSampleNumberChanged();
521
522 updateSampleMemoryBar();
523 ui->memoryWidget->update();
524
525 updateSampleView();
526 ui->sampleViewWidget->update();
527 }
528
on_repeatCheckBox_toggled(bool checked)529 void ADPCMSampleEditor::on_repeatCheckBox_toggled(bool checked)
530 {
531 if (!isIgnoreEvent_) {
532 bt_.lock()->setSampleADPCMRepeatEnabled(ui->sampleNumSpinBox->value(), checked);
533 emit sampleParameterChanged(ui->sampleNumSpinBox->value());
534 emit modified();
535 }
536 }
537
on_rootRateSpinBox_valueChanged(int arg1)538 void ADPCMSampleEditor::on_rootRateSpinBox_valueChanged(int arg1)
539 {
540 ui->rootRateSpinBox->setSuffix(
541 QString(" (0x") + QString("%1 | ").arg(arg1, 3, 16, QChar('0')).toUpper()
542 + QString("%1Hz)").arg(QString::number(arg1 * 55500. * std::pow(2., -16.), 'f', 3)));
543
544 if (!isIgnoreEvent_) {
545 bt_.lock()->setSampleADPCMRootDeltaN(ui->sampleNumSpinBox->value(), arg1);
546 emit sampleParameterChanged(ui->sampleNumSpinBox->value());
547 emit modified();
548 }
549 }
550
on_action_Resize_triggered()551 void ADPCMSampleEditor::on_action_Resize_triggered()
552 {
553 SampleLengthDialog diag(sample_.size());
554 if (diag.exec() == QDialog::Accepted) {
555 sample_.resize(diag.getLength());
556 sendEditedSample();
557
558 updateSampleView();
559 ui->sampleViewWidget->update();
560 }
561 }
562
on_actionRe_verse_triggered()563 void ADPCMSampleEditor::on_actionRe_verse_triggered()
564 {
565 std::reverse(sample_.begin(), sample_.end());
566 sendEditedSample();
567
568 updateSampleView();
569 ui->sampleViewWidget->update();
570 }
571
on_actionZoom_In_triggered()572 void ADPCMSampleEditor::on_actionZoom_In_triggered()
573 {
574 int len = (viewedSampLen_ + 1) >> 1;
575 if (len > 1) {
576 ++zoom_;
577 viewedSampLen_ = len;
578 ui->horizontalScrollBar->setMaximum(sample_.size() - len);
579 updateSampleView();
580 ui->sampleViewWidget->update();
581
582 ui->detailLabel->setText(updateDetailView());
583 }
584 }
585
on_actionZoom_Out_triggered()586 void ADPCMSampleEditor::on_actionZoom_Out_triggered()
587 {
588 if (zoom_) {
589 --zoom_;
590 viewedSampLen_ = sample_.size() >> zoom_;
591 ui->horizontalScrollBar->setMaximum(sample_.size() - viewedSampLen_);
592 updateSampleView();
593 ui->sampleViewWidget->update();
594
595 ui->detailLabel->setText(updateDetailView());
596 }
597 }
598
on_horizontalScrollBar_valueChanged(int value)599 void ADPCMSampleEditor::on_horizontalScrollBar_valueChanged(int value)
600 {
601 Q_UNUSED(value)
602 updateSampleView();
603 ui->sampleViewWidget->update();
604 }
605
on_action_Import_triggered()606 void ADPCMSampleEditor::on_action_Import_triggered()
607 {
608 QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory());
609 QString file = QFileDialog::getOpenFileName(this, tr("Import sample"),
610 (dir.isEmpty() ? "./" : dir), tr("WAV signed 16-bit PCM (*.wav)"));
611 if (file.isNull()) return;
612
613 importSampleFrom(file);
614 }
615
on_action_Clear_triggered()616 void ADPCMSampleEditor::on_action_Clear_triggered()
617 {
618 bt_.lock()->clearSampleADPCMRawSample(ui->sampleNumSpinBox->value());
619
620 updateSampleMemoryBar();
621 ui->memoryWidget->update();
622
623 emit modified();
624
625 if (config_.lock()->getWriteOnlyUsedSamples()) {
626 emit sampleAssignRequested();
627 }
628 else {
629 emit sampleMemoryChanged();
630 }
631
632 emit sampleParameterChanged(ui->sampleNumSpinBox->value());
633 }
634
on_action_Grid_View_triggered()635 void ADPCMSampleEditor::on_action_Grid_View_triggered()
636 {
637 updateSampleView();
638 ui->sampleViewWidget->update();
639 }
640
on_actionG_rid_Settings_triggered()641 void ADPCMSampleEditor::on_actionG_rid_Settings_triggered()
642 {
643 GridSettingsDialog diag(gridIntr_);
644 if (diag.exec() == QDialog::Accepted) {
645 gridIntr_ = diag.getInterval();
646 updateSampleView();
647 ui->sampleViewWidget->update();
648 }
649 }
650
on_action_Draw_Sample_triggered(bool checked)651 void ADPCMSampleEditor::on_action_Draw_Sample_triggered(bool checked)
652 {
653 if (checked) {
654 ui->actionDirec_t_Draw->setChecked(false);
655 drawMode_ = DrawMode::Normal;
656 }
657 else {
658 if (drawMode_ == DrawMode::Normal) drawMode_ = DrawMode::Disabled;
659 sendEditedSample(); // Convert sample
660 }
661 updateSampleView();
662 }
663
on_actionDirec_t_Draw_triggered(bool checked)664 void ADPCMSampleEditor::on_actionDirec_t_Draw_triggered(bool checked)
665 {
666 if (checked) {
667 ui->action_Draw_Sample->setChecked(false);
668 if (drawMode_ == DrawMode::Normal) sendEditedSample(); // Convert sample
669 drawMode_ = DrawMode::Direct;
670 }
671 else {
672 if (drawMode_ == DrawMode::Direct) drawMode_ = DrawMode::Disabled;
673 }
674 updateSampleView();
675 }
676