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