/* -*- 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 #ifndef MAXFLOAT #define MAXFLOAT (3.40282347e+38F) #endif SampleSorter::SampleSorter() { setMouseTracking(true); data = NULL; size = 0; attlen = 666; // Magical constants needs biblical proportions... cur_thr = -1; threshold.push_back(0.8); threshold_is_moving = false; } void SampleSorter::setWavData(const float *data, size_t size) { this->data = data; this->size = size; resort(); } void SampleSorter::setSelections(Selections s) { _selections = s; resort(); } int SampleSorter::attackLength() { return attlen; } void SampleSorter::setAttackLength(int len) { attlen = len; resort(); } Selections SampleSorter::selections() { Selections s; int j = 0; QMap::iterator i = sorted.begin(); while(i != sorted.end()) { s[j++] = i.value(); i++; } return s; } Levels SampleSorter::levels() { Levels lvls; // Sort the segmentation lines: for(int i = 0; i < threshold.size(); i++) { for(int j = 0; j < threshold.size(); j++) { if(threshold[i] < threshold[j]) { float tmp = threshold[i]; threshold[i] = threshold[j]; threshold[j] = tmp; } } } // for(int i = -1; i < threshold.size(); i++) { Level lvl; if(i == -1) lvl.velocity = 0; else lvl.velocity = threshold[i] * threshold[i]; float next; if(i == threshold.size() - 1) next = 1.0; else next = threshold[i+1] * threshold[i+1]; QMap::iterator si = sorted.begin(); while(si != sorted.end()) { float val = (si.key()/max); if(val >= lvl.velocity && val <= next) { lvl.selections[si.key()] = si.value(); } si++; } lvls.push_back(lvl); } return lvls; } void SampleSorter::resort() { sorted.clear(); min = MAXFLOAT; max = 0.0; QMap::iterator i = _selections.begin(); while(i != _selections.end()) { float energy = 0.0; Selection s = i.value(); 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]; } while(sorted.find(energy) != sorted.end()) { energy += 1; // Make sure that the key is unique. } sorted[energy] = i.value(); if(energy < min) min = energy; if(energy > max) max = energy; i++; } update(); } void SampleSorter::setActiveSelection(Selection s) { sel = s; update(); } #define MAP(p) (height()-(int)(p*((float)height()/(float)width()))) #define unmapX(x) ((double)x/(double)(width()-1)) #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); QColor colFg = QColor(160, 180, 160); QColor colPt = QColor(255, 100, 100); QColor colPtSel = QColor(255, 255, 100); QColor colVel = QColor(0, 0, 0); painter.setPen(colBg); painter.setBrush(colBg); painter.drawRect(event->rect()); painter.setPen(colFg); painter.drawLine(0,height(),width(),0); for(int i = 0; i < threshold.size(); i++) { if(cur_thr == i) painter.setPen(colPtSel); else painter.setPen(colPt); painter.drawLine(mapX(threshold[i]), 0, mapX(threshold[i]), height()); char valstr[32]; sprintf(valstr, "%.3f", threshold[i] * threshold[i]); painter.setPen(colVel); painter.drawText(mapX(threshold[i]), height(), valstr); } if(sorted.isEmpty()) return; QMap::iterator i = sorted.begin(); while(i != sorted.end()) { if(sel.to == i.value().to && sel.from == i.value().from) painter.setPen(colPtSel); else painter.setPen(colPt); float x = (i.key()/max); x = sqrt(x); x *= (float)width(); drawCircle(painter, x, MAP(x)); i++; } } Selection *SampleSorter::getSelectionByCoordinate(int px, int py) { py -= C_RADIUS; QMap::iterator i = sorted.begin(); while(i != sorted.end()) { float x = (i.key()/max); x = sqrt(x); x *= (float)width(); if(px < (x + C_RADIUS) && px > (x - C_RADIUS) && py < (MAP(x) + C_RADIUS) && py > (MAP(x) - C_RADIUS)) { return &i.value(); } i++; } return NULL; } void SampleSorter::mouseMoveEvent(QMouseEvent *event) { if(cur_thr != -1 && cur_thr < threshold.size()) { float val = unmapX(event->x()); if(val < 0.0) val = 0.0; if(val > 1.0) val = 1.0; threshold[cur_thr] = fabs(val); update(); return; } if(event->button() != Qt::LeftButton) { setCursor(Qt::ArrowCursor); for(size_t i = 0; i < (size_t)threshold.size(); i++) { if(abs(event->x() - mapX(threshold[i])) < 2 || abs(event->x() - mapX(-threshold[i])) < 2 ) { setCursor(Qt::SplitHCursor); } } } } void SampleSorter::mousePressEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { Selection *psel = getSelectionByCoordinate(event->x(), event->y()); if(psel) { emit activeSelectionChanged(*psel); return; } // Check if threshold is being dragged. for(size_t i = 0; i < (size_t)threshold.size(); i++) { if(abs(event->x() - mapX(threshold[i])) < 2 || abs(event->x() - mapX(-threshold[i])) < 2 ) { cur_thr = i; threshold_is_moving = true; update(); return; } } // Make new selection float from = unmapX(event->x()); threshold.push_back(from); cur_thr = threshold.size() - 1; threshold_is_moving = true; update(); return; } } void SampleSorter::mouseReleaseEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { if(threshold_is_moving) { if(threshold[cur_thr] == 0.0 || threshold[cur_thr] == 1.0) { threshold.remove(cur_thr); } threshold_is_moving = false; cur_thr = -1; setCursor(Qt::ArrowCursor); update(); return; } } }