summaryrefslogtreecommitdiff
path: root/libs/waveview/waveview/wave_view.h
blob: 4b1c8eb42b783b99fc2b53a092b51c38c62673b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*
    Copyright (C) 2011-2013 Paul Davis
    Copyright (C) 2017 Tim Mayberry
    Author: Carl Hetherington <cth@carlh.net>

    This program 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.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef _WAVEVIEW_WAVE_VIEW_H_
#define _WAVEVIEW_WAVE_VIEW_H_

#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>

#include <glibmm/refptr.h>

#include "ardour/types.h"
#include "canvas/item.h"
#include "waveview/visibility.h"

namespace ARDOUR {
	class AudioRegion;
}

namespace Gdk {
	class Pixbuf;
}

namespace ArdourWaveView {

class WaveViewCacheGroup;
class WaveViewDrawRequest;
class WaveViewDrawRequestQueue;
class WaveViewImage;
class WaveViewProperties;
class WaveViewDrawingThread;

class LIBWAVEVIEW_API WaveView : public ArdourCanvas::Item, public sigc::trackable
{
public:
	enum Shape { Normal, Rectified };

	std::string debug_name () const;

	/* Displays a single channel of waveform data for the given Region.

	   x = 0 in the waveview corresponds to the first waveform datum taken
	   from region->start() samples into the source data.

	   x = N in the waveview corresponds to the (N * spp)'th sample
	   measured from region->start() into the source data.

	   when drawing, we will map the zeroth-pixel of the waveview
	   into a window.

	   The waveview itself contains a set of pre-rendered Cairo::ImageSurfaces
	   that cache sections of the display. This is filled on-demand and
	   never cleared until something explicitly marks the cache invalid
	   (such as a change in samples_per_pixel, the log scaling, rectified or
	   other view parameters).
	*/

	WaveView (ArdourCanvas::Canvas*, boost::shared_ptr<ARDOUR::AudioRegion>);
	WaveView (Item*, boost::shared_ptr<ARDOUR::AudioRegion>);
	~WaveView ();

	virtual void prepare_for_render (ArdourCanvas::Rect const& window_area) const;

	virtual void render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context>) const;

	void compute_bounding_box () const;

	void set_samples_per_pixel (double);
	void set_height (ArdourCanvas::Distance);
	void set_channel (int);
	void set_region_start (ARDOUR::frameoffset_t);

	/** Change the first position drawn by @param pixels.
	 * @param pixels must be positive. This is used by
	 * AudioRegionViews in Ardour to avoid drawing the
	 * first pixel of a waveform, and exists in case
	 * there are uses for WaveView where we do not
	 * want this behaviour.
	 */
	void set_start_shift (double pixels);

	void set_fill_color (Gtkmm2ext::Color);
	void set_outline_color (Gtkmm2ext::Color);

	void region_resized ();
	void gain_changed ();

	void set_show_zero_line (bool);
	bool show_zero_line () const;

	void set_zero_color (Gtkmm2ext::Color);
	void set_clip_color (Gtkmm2ext::Color);
	void set_logscaled (bool);

	void set_gradient_depth (double);
	double gradient_depth () const;

	void set_shape (Shape);

	void set_always_get_image_in_thread (bool yn);

	/* currently missing because we don't need them (yet):
	 * set_shape_independent();
	 * set_logscaled_independent();
	 */

	static void set_global_gradient_depth (double);
	static void set_global_logscaled (bool);
	static void set_global_shape (Shape);
	static void set_global_show_waveform_clipping (bool);

	static double global_gradient_depth () { return _global_gradient_depth; }

	static bool global_logscaled () { return _global_logscaled; }

	static Shape global_shape () { return _global_shape; }

	void set_amplitude_above_axis (double v);

	double amplitude_above_axis () const;

	static void set_clip_level (double dB);
	static PBD::Signal0<void> ClipLevelChanged;

	static void start_drawing_thread ();
	static void stop_drawing_thread ();

	static void set_image_cache_size (uint64_t);

#ifdef CANVAS_COMPATIBILITY
	void*& property_gain_src () {
		return _foo_void;
	}
	void*& property_gain_function () {
		return _foo_void;
	}

private:
	void* _foo_void;
#endif

private:
	friend class WaveViewThreadClient;
	friend class WaveViewDrawingThread;

	boost::shared_ptr<ARDOUR::AudioRegion> _region;

	boost::scoped_ptr<WaveViewProperties> _props;

	mutable boost::shared_ptr<WaveViewImage> _image;

	mutable boost::shared_ptr<WaveViewCacheGroup> _cache_group;

	bool _shape_independent;
	bool _logscaled_independent;
	bool _gradient_depth_independent;

	/** Under almost conditions, this is going to return _region->length(),
	 * but if region_start has been reset, then we need to use this modified
	 * computation.
	 */
	ARDOUR::framecnt_t region_length () const;

	/** Under almost conditions, this is going to return _region->start() +
	 * _region->length(), but if region_start has been reset, then we need to use
	 * this modified computation.
	 */
	ARDOUR::framepos_t region_end () const;

	/**
	 * _image stays non-null after the first time it is set
	 */
	bool rendered () const { return _image.get(); }

	bool draw_image_in_gui_thread () const;

	/** If true, calls to render() will render a missing wave image in the GUI
	 * thread. Generally set to false, but true after a call to set_height().
	 */
	mutable bool _draw_image_in_gui_thread;

	/** If true, calls to render() will render a missing wave image in the GUI
	 * thread. Set true for waveviews we expect to keep updating (e.g. while
	 * recording)
	 */
	bool _always_draw_image_in_gui_thread;

	void init();

	mutable boost::shared_ptr<WaveViewDrawRequest> current_request;

	PBD::ScopedConnectionList invalidation_connection;

	static double _global_gradient_depth;
	static bool _global_logscaled;
	static Shape _global_shape;
	static bool _global_show_waveform_clipping;
	static double _global_clip_level;

	static PBD::Signal0<void> VisualPropertiesChanged;

	void handle_visual_property_change ();
	void handle_clip_level_change ();

	struct LineTips {
		double top;
		double bot;
		double spread;
		bool clip_max;
		bool clip_min;

		LineTips () : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
	};

	static ArdourCanvas::Coord y_extent (double, Shape const, double const height);

	static void compute_tips (ARDOUR::PeakData const& peak, LineTips& tips, double const effective_height);

	static void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int n_peaks,
	                        boost::shared_ptr<WaveViewDrawRequest>);
	static void draw_absent_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int);

	ARDOUR::framecnt_t optimal_image_width_samples () const;

	void set_image (boost::shared_ptr<WaveViewImage> img) const;

	// @return true if item area intersects with draw area
	bool get_item_and_draw_rect_in_window_coords (ArdourCanvas::Rect const& canvas_rect,
	                                              ArdourCanvas::Rect& item_area,
	                                              ArdourCanvas::Rect& draw_rect) const;

	boost::shared_ptr<WaveViewDrawRequest> create_draw_request (WaveViewProperties const&) const;

	void queue_draw_request (boost::shared_ptr<WaveViewDrawRequest> const&) const;

	static void process_draw_request (boost::shared_ptr<WaveViewDrawRequest>);

	boost::shared_ptr<WaveViewCacheGroup> get_cache_group () const;

	/**
	 * Notify the Cache that we are dropping our reference to the
	 * CacheGroup so it can also do so if it is the only reference holder
	 * of the cache group.
	 */
	void reset_cache_group ();
};

} /* namespace */

#endif