1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2 
3 #include "smsampleview.hh"
4 #include "smblockutils.hh"
5 #include <stdio.h>
6 
7 #include <QPainter>
8 #include <QMouseEvent>
9 
10 using namespace SpectMorph;
11 
12 using std::vector;
13 using std::min;
14 using std::max;
15 
16 #define HZOOM_SCALE 0.05
17 
SampleView()18 SampleView::SampleView()
19 {
20   audio = NULL;
21   markers = NULL;
22   attack_start = 0;
23   attack_end = 0;
24   hzoom = 1;
25   vzoom = 1;
26   m_edit_marker_type = MARKER_NONE;
27   button_1_pressed = false;
28   update_size();
29 
30   setMouseTracking (true);
31 }
32 
33 static double
freq_ratio_to_cent(double freq_ratio)34 freq_ratio_to_cent (double freq_ratio)
35 {
36   return log (freq_ratio) / log (2) * 1200;
37 }
38 
39 void
paintEvent(QPaintEvent * event)40 SampleView::paintEvent (QPaintEvent *event)
41 {
42   const int width = this->width();
43   const int height = this->height();
44 
45   QPainter painter (this);
46   painter.fillRect (rect(), QColor (255, 255, 255));
47 
48   painter.setPen (QColor (200, 0, 0));
49   double hz = HZOOM_SCALE * hzoom;
50   double vz = (height / 2) * vzoom;
51   draw_signal (signal, painter, event->rect(), height, vz, hz);
52 
53   // attack markers:
54   painter.setPen (QColor (150, 150, 150));
55   painter.drawLine (hz * attack_start, 0, hz * attack_start, height);
56   painter.drawLine (hz * attack_end, 0, hz * attack_end, height);
57 
58   if (audio)
59     {
60       if (audio->loop_type == Audio::LOOP_FRAME_FORWARD || audio->loop_type == Audio::LOOP_FRAME_PING_PONG)
61         {
62           // loop start marker
63           int loop_start = audio->loop_start * audio->frame_step_ms / 1000.0 * audio->mix_freq;
64 
65           if (edit_marker_type() == MARKER_LOOP_START)
66             painter.setPen (QColor (0, 0, 200));
67           else
68             painter.setPen (QColor (150, 150, 150));
69           painter.drawLine (hz * loop_start, 0, hz * loop_start, height);
70 
71           // loop end marker
72           int loop_end = audio->loop_end * audio->frame_step_ms / 1000.0 * audio->mix_freq;
73 
74           if (edit_marker_type() == MARKER_LOOP_END)
75             painter.setPen (QColor (0, 0, 200));
76           else
77             painter.setPen (QColor (150, 150, 150));
78           painter.drawLine (hz * loop_end, 0, hz * loop_end, height);
79         }
80 
81       if (m_show_tuning)
82         {
83           double last_cent = 0;
84 
85           for (size_t frame = 0; frame < audio->contents.size(); frame++)
86             {
87               double last_pos = (frame - 1.0) * audio->frame_step_ms / 1000.0 * audio->mix_freq;
88               double pos = frame * audio->frame_step_ms / 1000.0 * audio->mix_freq;
89 
90               const int n_partials = 3;
91               const AudioBlock& block = audio->contents[frame];
92 
93               double cent = freq_ratio_to_cent (block.estimate_fundamental (n_partials));
94 
95               painter.setPen (QColor (0, 200, 0));
96               painter.drawLine (hz * last_pos, height / 2 - last_cent * height / 100, hz * pos, height / 2 - cent * height / 100);
97 
98               last_cent = cent;
99             }
100         }
101     }
102 
103   if (markers)
104     {
105       for (size_t i = 0; i < markers->count(); i++)
106         {
107           if (markers->valid (i))
108             {
109               int marker_pos = markers->position (i) / 1000.0 * audio->mix_freq;
110 
111               if (markers->type (i) == m_edit_marker_type)
112                 painter.setPen (QColor (0, 0, 200));
113               else
114                 painter.setPen (QColor (150, 150, 150));
115 
116               painter.drawLine (hz * marker_pos, 0, hz * marker_pos, height);
117             }
118         }
119     }
120 
121   // black line @ zero:
122   painter.setPen (QColor (0, 0, 0));
123   painter.drawLine (0, (height / 2), width, height / 2);
124 }
125 
126 void
load(const WavData * wav_data,Audio * audio,Markers * markers)127 SampleView::load (const WavData *wav_data, Audio *audio, Markers *markers)
128 {
129   this->audio = audio;
130   this->markers = markers;
131 
132   signal.clear();
133   attack_start = 0;
134   attack_end = 0;
135 
136   if (!wav_data) // no sample selected
137     {
138       update_size();
139       update();
140       return;
141     }
142 
143   if (wav_data->n_channels() != 1)
144     {
145       fprintf (stderr, "SampleView: only mono samples supported\n");
146       exit (1);
147     }
148   signal = wav_data->samples();
149 
150   attack_start = audio->attack_start_ms / 1000.0 * audio->mix_freq - audio->zero_values_at_start;
151   attack_end   = audio->attack_end_ms / 1000.0 * audio->mix_freq - audio->zero_values_at_start;
152 
153   update_size();
154   update();
155 }
156 
157 void
update_size()158 SampleView::update_size()
159 {
160   int new_width = HZOOM_SCALE * signal.size() * hzoom;
161 
162   setFixedWidth (new_width);
163 }
164 
165 void
set_zoom(double new_hzoom,double new_vzoom)166 SampleView::set_zoom (double new_hzoom, double new_vzoom)
167 {
168   hzoom = new_hzoom;
169   vzoom = new_vzoom;
170 
171   update_size();
172   update();
173 }
174 
175 void
mousePressEvent(QMouseEvent * event)176 SampleView::mousePressEvent (QMouseEvent *event)
177 {
178   if (event->button() == Qt::LeftButton)
179     {
180       button_1_pressed = true;
181       if (m_edit_marker_type && audio)
182         setCursor (Qt::SizeAllCursor);
183 
184       move_marker (event->x());
185     }
186 }
187 
188 void
move_marker(int x)189 SampleView::move_marker (int x)
190 {
191   if (button_1_pressed && audio)
192     {
193       double hz = HZOOM_SCALE * hzoom;
194       int index = x / hz;
195 
196       if (audio->loop_type == Audio::LOOP_FRAME_FORWARD || audio->loop_type == Audio::LOOP_FRAME_PING_PONG)
197         {
198           if (m_edit_marker_type == MARKER_LOOP_START)
199             {
200               audio->loop_start = index / (audio->frame_step_ms / 1000 * audio->mix_freq);
201             }
202           else if (m_edit_marker_type == MARKER_LOOP_END)
203             {
204               audio->loop_end = index / (audio->frame_step_ms / 1000 * audio->mix_freq);
205             }
206         }
207       if (markers)
208         {
209           for (size_t m = 0; m < markers->count(); m++)
210             {
211               if (markers->type (m) == m_edit_marker_type)
212                 markers->set_position (m, index / audio->mix_freq * 1000);
213             }
214         }
215       Q_EMIT audio_edit();
216       update();
217     }
218 }
219 
220 void
mouseMoveEvent(QMouseEvent * event)221 SampleView::mouseMoveEvent (QMouseEvent *event)
222 {
223   if (audio)
224     {
225       double hz = HZOOM_SCALE * hzoom;
226       int index = event->x() / hz;
227 
228       Q_EMIT mouse_time_changed (index / audio->mix_freq * 1000);
229     }
230   move_marker (event->x());
231 }
232 
233 void
mouseReleaseEvent(QMouseEvent * event)234 SampleView::mouseReleaseEvent (QMouseEvent *event)
235 {
236   move_marker (event->x());
237   unsetCursor();
238 
239   if (event->button() == Qt::LeftButton)
240     button_1_pressed = false;
241 }
242 
243 
244 SampleView::EditMarkerType
edit_marker_type()245 SampleView::edit_marker_type()
246 {
247   return m_edit_marker_type;
248 }
249 
250 void
set_edit_marker_type(EditMarkerType marker_type)251 SampleView::set_edit_marker_type (EditMarkerType marker_type)
252 {
253   m_edit_marker_type = marker_type;
254   update();
255 }
256 
257 void
set_show_tuning(bool show_tuning)258 SampleView::set_show_tuning (bool show_tuning)
259 {
260   m_show_tuning = show_tuning;
261   update();
262 }
263