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
ScopeView()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
~ScopeView()57 ScopeView::~ScopeView()
58 {
59 delete[] fLeftSamples;
60 delete[] fRightSamples;
61 }
62
63
AttachedToWindow()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
DetachedFromWindow()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
Draw(BRect updateRect)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
_Thread(void * data)106 ScopeView::_Thread(void* data)
107 {
108 return ((ScopeView*) data)->Thread();
109 }
110
111
112 int32
Thread()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
DrawLoading()134 ScopeView::DrawLoading()
135 {
136 DrawText(B_TRANSLATE("Loading instruments" B_UTF8_ELLIPSIS));
137 }
138
139
140 void
DrawNoFile()141 ScopeView::DrawNoFile()
142 {
143 DrawText(B_TRANSLATE("Drop MIDI file here"));
144 }
145
146
147 void
DrawDisabled()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
DrawStopped()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
DrawPlaying()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
DrawText(const char * text)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