/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * samplesorter.cc * * Mon Nov 30 07:45:58 CET 2009 * Copyright 2009 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of DrumGizmo. * * DrumGizmo is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * DrumGizmo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "samplesorter.h" #include #include #include #include #include #ifndef MAXFLOAT #define MAXFLOAT (3.40282347e+38F) #endif SampleSorter::SampleSorter(Ranges& s, Ranges& p, Instrument& instrument) : ranges(s) , ranges_preview(p) , instrument(instrument) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); // Enable keyboard event on click data = NULL; size = 0; attlen = 666; // Magical constants needs biblical proportions... sel_moving = SEL_NONE; setSpreadFactor(1000); } void SampleSorter::setShowPreview(bool s) { show_preview = s; update(); } void SampleSorter::setWavData(const float* data, size_t size) { this->data = data; this->size = size; relayout(); } int SampleSorter::attackLength() { return attlen; } void SampleSorter::setSpreadFactor(int s) { spread = (double)s / 1000.0; spread *= spread; relayout(); } void SampleSorter::setAttackLength(int len) { attlen = len; relayout(); } void SampleSorter::addRange(sel_id_t id) { Range s = ranges.get(id); double energy = 0.0; for(size_t idx = s.from; (idx < (size_t)s.from + (size_t)attackLength()) && (idx < (size_t)s.to) && (idx < size); idx++) { energy += data[idx] * data[idx]; } s.energy = pow(energy, spread); ranges.update(id, s); relayout(); } void SampleSorter::addRangePreview(sel_id_t id) { Range s = ranges_preview.get(id); double energy = 0.0; for(size_t idx = s.from; (idx < (size_t)s.from + (size_t)attackLength()) && (idx < (size_t)s.to) && (idx < size); idx++) { energy += data[idx] * data[idx]; } s.energy = pow(energy, spread); ranges_preview.update(id, s); relayout(); } void SampleSorter::relayout() { min = MAXFLOAT; max = 0.0; { QVector ids = ranges.ids(); QVector::iterator i = ids.begin(); while(i != ids.end()) { Range sel = ranges.get(*i); if(sel.energy < min) { min = sel.energy; } if(sel.energy > max) { max = sel.energy; } i++; } } if(show_preview) { QVector ids = ranges_preview.ids(); QVector::iterator i = ids.begin(); while(i != ids.end()) { Range sel = ranges_preview.get(*i); if(sel.energy < min) { min = sel.energy; } if(sel.energy > max) { max = sel.energy; } i++; } } update(); } #define MAP(p) (height()-(int)(p*((float)height()/(float)width()))) #define unmapX(x) ((double)x/(double)(width()-1)*(1.0/0.9)) #define unmapY(x) x #define mapX(x) (((double)x)*(width()-1)) #define mapY(x) x #define C_RADIUS 2 static void drawCircle(QPainter& p, int x, int y) { p.drawEllipse(x - C_RADIUS, y - C_RADIUS, 2 * C_RADIUS, 2 * C_RADIUS); } void SampleSorter::paintEvent(QPaintEvent* event) { QPainter painter(this); QColor colBg = QColor(180, 200, 180, 160); QColor colFg = QColor(160, 180, 160, 160); QColor colPt = QColor(255, 100, 100, 160); QColor colPtPreview = QColor(0, 0, 255, 160); QColor colPtSel = QColor(255, 255, 100, 160); painter.setPen(colBg); painter.setBrush(colBg); painter.drawRect(event->rect()); painter.setPen(colFg); painter.drawLine(0,height(),width(),0); { QVector ids = ranges.ids(); QVector::iterator i = ids.begin(); while(i != ids.end()) { Range sel = ranges.get(*i); if(*i == ranges.active()) { painter.setPen(colPtSel); } else { painter.setPen(colPt); } float x = (sel.energy / max); x = sqrt(x); x *= (float)width() * 0.9; drawCircle(painter, x, MAP(x)); i++; } } if(show_preview) { QVector ids = ranges_preview.ids(); QVector::iterator i = ids.begin(); while(i != ids.end()) { Range sel = ranges_preview.get(*i); painter.setPen(colPtPreview); float x = (sel.energy / max); x = sqrt(x); x *= (float)width() * 0.9; drawCircle(painter, x, MAP(x)); i++; } } } sel_id_t SampleSorter::getRangeByCoordinate(int px, int py) { // Hit radius is slithly larger than the circles themselves. int hit_r = C_RADIUS + 1; QVector ids = ranges.ids(); QVector::iterator i = ids.begin(); while(i != ids.end()) { Range sel = ranges.get(*i); float x = (sel.energy/max); x = sqrt(x); x *= (float)width() * 0.9; if(px < (x + hit_r) && px > (x - hit_r) && py < (MAP(x) + hit_r) && py > (MAP(x) - hit_r)) { return *i; } i++; } return SEL_NONE; } void SampleSorter::mouseMoveEvent(QMouseEvent* event) { if(sel_moving != SEL_NONE) { Range sel = ranges.get(sel_moving); if(sel_moving != SEL_NONE) { double power = unmapX(event->x()); power *= power; power *= max; sel.energy = power; ranges.update(sel_moving, sel); } update(); return; } else { sel_id_t psel = getRangeByCoordinate(event->x(), event->y()); if(psel != SEL_NONE) { setCursor(Qt::OpenHandCursor); } else { setCursor(Qt::ArrowCursor); } } } void SampleSorter::mousePressEvent(QMouseEvent* event) { if(event->button() == Qt::LeftButton) { sel_id_t psel = getRangeByCoordinate(event->x(), event->y()); sel_moving = psel; ranges.setActive(psel); if(psel != SEL_NONE) { setCursor(Qt::ClosedHandCursor); } } } void SampleSorter::mouseReleaseEvent(QMouseEvent* event) { if(event->button() == Qt::LeftButton) { sel_moving = SEL_NONE; sel_id_t psel = getRangeByCoordinate(event->x(), event->y()); if(psel != SEL_NONE) { setCursor(Qt::OpenHandCursor); } else { setCursor(Qt::ArrowCursor); } } } void SampleSorter::keyReleaseEvent(QKeyEvent* event) { if(ranges.active() != SEL_NONE && event->key() == Qt::Key_Delete) { ranges.remove(ranges.active()); } }