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