xref: /haiku/src/apps/midiplayer/ScopeView.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * Copyright (c) 2004 Matthijs Hollemans
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <Catalog.h>
24 #include <Locale.h>
25 #include <Synth.h>
26 #include <Window.h>
27 
28 #include "ScopeView.h"
29 
30 #undef B_TRANSLATION_CONTEXT
31 #define B_TRANSLATION_CONTEXT "Scope View"
32 
33 
34 //------------------------------------------------------------------------------
35 
36 ScopeView::ScopeView()
37 	: BView(BRect(0, 0, 180, 63), NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP,
38 	        B_WILL_DRAW)
39 {
40 	SetViewColor(0, 0, 0);
41 
42 	playing = false;
43 	enabled = true;
44 	haveFile = false;
45 	loading = false;
46 	liveInput = false;
47 
48 	sampleCount = (int32) Bounds().Width();
49 	leftSamples = new int16[sampleCount];
50 	rightSamples = new int16[sampleCount];
51 }
52 
53 //------------------------------------------------------------------------------
54 
55 ScopeView::~ScopeView()
56 {
57 	delete[] leftSamples;
58 	delete[] rightSamples;
59 }
60 
61 //------------------------------------------------------------------------------
62 
63 void ScopeView::AttachedToWindow()
64 {
65 	finished = false;
66 	threadId = spawn_thread(_Thread, "ScopeThread", B_NORMAL_PRIORITY, this);
67 	if (threadId >= B_OK)
68 	{
69 		resume_thread(threadId);
70 	}
71 }
72 
73 //------------------------------------------------------------------------------
74 
75 void ScopeView::DetachedFromWindow()
76 {
77 	if (threadId >= B_OK)
78 	{
79 		finished = true;
80 		status_t exitValue;
81 		wait_for_thread(threadId, &exitValue);
82 	}
83 }
84 
85 //------------------------------------------------------------------------------
86 
87 void ScopeView::Draw(BRect updateRect)
88 {
89 	super::Draw(updateRect);
90 
91 	if (loading)
92 	{
93 		DrawLoading();
94 	}
95 	else if (!haveFile && !liveInput)
96 	{
97 		DrawNoFile();
98 	}
99 	else if (!enabled)
100 	{
101 		DrawDisabled();
102 	}
103 	else if (playing || liveInput)
104 	{
105 		DrawPlaying();
106 	}
107 	else
108 	{
109 		DrawStopped();
110 	}
111 }
112 
113 //------------------------------------------------------------------------------
114 
115 void ScopeView::SetPlaying(bool flag)
116 {
117 	playing = flag;
118 }
119 
120 //------------------------------------------------------------------------------
121 
122 void ScopeView::SetEnabled(bool flag)
123 {
124 	enabled = flag;
125 }
126 
127 //------------------------------------------------------------------------------
128 
129 void ScopeView::SetHaveFile(bool flag)
130 {
131 	haveFile = flag;
132 }
133 
134 //------------------------------------------------------------------------------
135 
136 void ScopeView::SetLoading(bool flag)
137 {
138 	loading = flag;
139 }
140 
141 //------------------------------------------------------------------------------
142 
143 void ScopeView::SetLiveInput(bool flag)
144 {
145 	liveInput = flag;
146 }
147 
148 //------------------------------------------------------------------------------
149 
150 int32 ScopeView::_Thread(void* data)
151 {
152 	return ((ScopeView*) data)->Thread();
153 }
154 
155 //------------------------------------------------------------------------------
156 
157 int32 ScopeView::Thread()
158 {
159 	// Because Pulse() was too slow, I created a thread that tells the
160 	// ScopeView to repaint itself. Note that we need to call LockLooper
161 	// with a timeout, otherwise we'll deadlock in DetachedFromWindow().
162 
163 	while (!finished)
164 	{
165 		if (enabled && (playing || liveInput))
166 		{
167 			if (LockLooperWithTimeout(50000) == B_OK)
168 			{
169 				Invalidate();
170 				UnlockLooper();
171 			}
172 		}
173 		snooze(50000);
174 	}
175 	return 0;
176 }
177 
178 //------------------------------------------------------------------------------
179 
180 void ScopeView::DrawLoading()
181 {
182 	DrawText("Loading instruments" B_UTF8_ELLIPSIS);
183 }
184 
185 //------------------------------------------------------------------------------
186 
187 void ScopeView::DrawNoFile()
188 {
189 	DrawText(B_TRANSLATE("Drop MIDI file here"));
190 }
191 
192 //------------------------------------------------------------------------------
193 
194 void ScopeView::DrawDisabled()
195 {
196 	SetHighColor(64, 64, 64);
197 
198 	StrokeLine(
199 		BPoint(0, Bounds().Height() / 2),
200 		BPoint(Bounds().Width(), Bounds().Height() / 2));
201 }
202 
203 //------------------------------------------------------------------------------
204 
205 void ScopeView::DrawStopped()
206 {
207 	SetHighColor(0, 130, 0);
208 
209 	StrokeLine(
210 		BPoint(0, Bounds().Height() / 2),
211 		BPoint(Bounds().Width(), Bounds().Height() / 2));
212 }
213 
214 //------------------------------------------------------------------------------
215 
216 void ScopeView::DrawPlaying()
217 {
218 	int32 width = (int32) Bounds().Width();
219 	int32 height = (int32) Bounds().Height();
220 
221 	// Scope drawing magic based on code by Michael Pfeiffer.
222 
223 	int32 size = be_synth->GetAudio(leftSamples, rightSamples, sampleCount);
224 	if (size > 0)
225 	{
226 		SetHighColor(255, 0, 130);
227 		SetLowColor(0, 130, 0);
228 		#define N 16
229 		int32 x, y, sx = 0, f = (height << N) / 65535, dy = height / 2 + 1;
230 		for (int32 i = 0; i < width; i++)
231 		{
232 			x = sx / width;
233 			y = ((leftSamples[x] * f) >> N) + dy;
234 			FillRect(BRect(i, y, i, y));
235 			y = ((rightSamples[x] * f) >> N) + dy;
236 			FillRect(BRect(i, y, i, y), B_SOLID_LOW);
237 			sx += size;
238 		}
239 	}
240 }
241 
242 //------------------------------------------------------------------------------
243 
244 void ScopeView::DrawText(const char* text)
245 {
246 	font_height height;
247 	GetFontHeight(&height);
248 
249 	float strWidth = StringWidth(text);
250 	float strHeight = height.ascent + height.descent;
251 
252 	float x = (Bounds().Width() - strWidth)/2;
253 	float y = height.ascent + (Bounds().Height() - strHeight)/2;
254 
255 	SetHighColor(255, 255, 255);
256 	SetLowColor(ViewColor());
257 	SetDrawingMode(B_OP_OVER);
258 
259 	DrawString(text, BPoint(x, y));
260 }
261 
262 //------------------------------------------------------------------------------
263