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