1 /* Copyright 2014 Haiku, Inc. 2 * Distributed under the terms of the MIT license. 3 */ 4 5 6 #include <vector> 7 8 #include <Application.h> 9 #include <Box.h> 10 #include <CheckBox.h> 11 #include <GroupLayout.h> 12 #include <GroupView.h> 13 #include <MediaDefs.h> 14 #include <OptionPopUp.h> 15 #include <TextControl.h> 16 #include <Window.h> 17 18 #include <Interpolate.h> 19 #include <Resampler.h> 20 21 22 static const int32 kMsgParametersChanged = 'pmch'; 23 24 25 // #pragma mark - 26 27 28 class Wave 29 { 30 public: 31 Wave() 32 : 33 fData(NULL) 34 { Resize(1); step = 1; } 35 float ValueAt(int index) { return fData[index % fSize]; } 36 void SetValue(int pos, float val) { fData[pos] = val; } 37 void Resize(int newSize) { 38 if (newSize <= 0) 39 newSize = 1; 40 fData = (float*)realloc(fData, newSize * sizeof(float)); 41 fSize = newSize; 42 } 43 int Size() { return fSize; } 44 void* Raw() { return fData; } 45 46 float step; 47 private: 48 float* fData; 49 int fSize; 50 }; 51 52 53 // #pragma mark - 54 55 56 class WaveView: public BView 57 { 58 public: 59 WaveView(); 60 61 void Draw(BRect update); 62 63 Wave waves[3]; // reference, input, and output 64 float zoom; 65 }; 66 67 68 WaveView::WaveView() 69 : 70 BView("wave", B_WILL_DRAW) 71 { 72 SetExplicitMinSize(BSize(512, 256)); 73 } 74 75 76 void 77 WaveView::Draw(BRect update) 78 { 79 SetOrigin(0, 256); 80 SetPenSize(1); 81 for (float i = update.left; i <= update.right; i++) { 82 if (i < waves[0].Size()) 83 SetHighColor(make_color(0, 0, 0, 255)); 84 else 85 SetHighColor(make_color(180, 180, 180, 255)); 86 BPoint p(i, waves[0].ValueAt(i) * zoom); 87 StrokeLine(p, p); 88 } 89 90 float i = 0; 91 float w1pos = 0; 92 93 // Skip the part outside the updat rect 94 while (w1pos <= update.left) { 95 w1pos += waves[1].step; 96 i++; 97 } 98 99 while (w1pos <= update.right) { 100 if (i < waves[1].Size()) 101 SetHighColor(make_color(255, 0, 0, 255)); 102 else 103 SetHighColor(make_color(255, 180, 180, 255)); 104 BPoint p1(w1pos, INT16_MIN); 105 BPoint p2(w1pos, waves[1].ValueAt(i) * zoom); 106 StrokeLine(p1, p2); 107 108 w1pos += waves[1].step; 109 i++; 110 } 111 112 i = 0; 113 w1pos = 0; 114 115 // Skip the part outside the updat rect 116 while (w1pos <= update.left) { 117 w1pos += waves[2].step; 118 i++; 119 } 120 121 while (w1pos <= update.right) { 122 if (i < waves[2].Size()) 123 SetHighColor(make_color(0, 255, 0, 255)); 124 else 125 SetHighColor(make_color(180, 255, 180, 255)); 126 BPoint p1(w1pos, INT16_MAX); 127 BPoint p2(w1pos, waves[2].ValueAt(i) * zoom); 128 StrokeLine(p1, p2); 129 130 w1pos += waves[2].step; 131 i++; 132 } 133 134 } 135 136 137 // #pragma mark - 138 139 140 static BOptionPopUp* makeFormatMenu() 141 { 142 BOptionPopUp* format = new BOptionPopUp("fmt", "Sample format:", NULL); 143 format->AddOptionAt("U8", media_raw_audio_format::B_AUDIO_UCHAR, 0); 144 format->AddOptionAt("S8", media_raw_audio_format::B_AUDIO_CHAR, 1); 145 format->AddOptionAt("S16", media_raw_audio_format::B_AUDIO_SHORT, 2); 146 format->AddOptionAt("S32", media_raw_audio_format::B_AUDIO_INT, 3); 147 format->AddOptionAt("F32", media_raw_audio_format::B_AUDIO_FLOAT, 4); 148 149 return format; 150 } 151 152 153 class MainWindow: public BWindow 154 { 155 public: 156 MainWindow(); 157 158 void MessageReceived(BMessage* what); 159 160 private: 161 BTextControl* fInputRate; 162 BTextControl* fOutputRate; 163 BCheckBox* fInterpolate; 164 165 BTextControl* fSignalVolume; 166 BTextControl* fSignalFrequency; 167 168 WaveView* fWaveView; 169 }; 170 171 172 MainWindow::MainWindow() 173 : 174 BWindow(BRect(100, 100, 400, 400), "Mixer test", B_DOCUMENT_WINDOW, 175 B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS) 176 { 177 SetLayout(new BGroupLayout(B_VERTICAL)); 178 179 BBox* inputGroup = new BBox("Input", 0, B_FANCY_BORDER); 180 inputGroup->SetLabel("Input"); 181 182 BGroupView* inputs = new BGroupView(B_VERTICAL); 183 inputs->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING, 184 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING); 185 inputGroup->AddChild(inputs); 186 187 fInputRate = new BTextControl("rate", "Sampling rate:", "256", 188 new BMessage(kMsgParametersChanged)); 189 inputs->AddChild(fInputRate); 190 #if 0 191 inputs->AddChild(makeFormatMenu()); 192 #endif 193 194 fSignalVolume = new BTextControl("vol", "Volume:", "127", 195 new BMessage(kMsgParametersChanged)); 196 inputs->AddChild(fSignalVolume); 197 fSignalFrequency = new BTextControl("freq", "Signal freq:", "256", 198 new BMessage(kMsgParametersChanged)); 199 inputs->AddChild(fSignalFrequency); 200 201 BBox* outputGroup = new BBox("Output", 0, B_FANCY_BORDER); 202 outputGroup->SetLabel("Output"); 203 204 BGroupView* outputs = new BGroupView(B_VERTICAL); 205 outputs->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING, 206 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING); 207 outputGroup->AddChild(outputs); 208 209 fOutputRate = new BTextControl("rate", "Sampling rate:", "256", 210 new BMessage(kMsgParametersChanged)); 211 outputs->AddChild(fOutputRate); 212 #if 0 213 outputs->AddChild(makeFormatMenu()); 214 BTextControl* volume = new BTextControl("vol", "Gain:", "1", NULL); 215 outputs->AddChild(volume); 216 #endif 217 218 fInterpolate = new BCheckBox("interp", "Interpolate", 219 new BMessage(kMsgParametersChanged)); 220 outputs->AddChild(fInterpolate); 221 222 BGroupView* header = new BGroupView(B_HORIZONTAL); 223 AddChild(header); 224 header->GroupLayout()->SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 225 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING); 226 header->AddChild(inputGroup); 227 header->AddChild(outputGroup); 228 229 AddChild(fWaveView = new WaveView()); 230 } 231 232 233 void 234 MainWindow::MessageReceived(BMessage* message) 235 { 236 switch (message->what) { 237 case kMsgParametersChanged: 238 { 239 int freq = atoi(fSignalFrequency->Text()); 240 fWaveView->waves[0].Resize(freq); 241 242 int irate = atoi(fInputRate->Text()); 243 fWaveView->waves[1].Resize(irate); 244 245 int orate = atoi(fOutputRate->Text()); 246 fWaveView->waves[2].Resize(orate); 247 248 int vol = atoi(fSignalVolume->Text()); 249 250 fWaveView->waves[0].step = 1; 251 fWaveView->waves[1].step = (float)freq / irate; 252 fWaveView->waves[2].step = (float)freq / orate; 253 fWaveView->zoom = vol; 254 255 for (int i = 0; i < freq; i++) { 256 fWaveView->waves[0].SetValue(i, sinf(i * 2 * M_PI / freq)); 257 } 258 259 for (int i = 0; i < irate; i++) { 260 fWaveView->waves[1].SetValue(i, 261 fWaveView->waves[0].ValueAt(i * freq / irate)); 262 } 263 264 // FIXME handle gain 265 if (fInterpolate->Value() == B_CONTROL_ON) { 266 Interpolate sampler(media_raw_audio_format::B_AUDIO_FLOAT, 267 media_raw_audio_format::B_AUDIO_FLOAT); 268 269 // First call initializes the "old sample" in the interpolator. 270 // Since we do the interpolation on exactly one period of the 271 // sound wave, this works. 272 sampler.Resample(fWaveView->waves[1].Raw(), sizeof(float), irate, 273 fWaveView->waves[2].Raw(), sizeof(float), orate, 1); 274 275 sampler.Resample(fWaveView->waves[1].Raw(), sizeof(float), irate, 276 fWaveView->waves[2].Raw(), sizeof(float), orate, 1); 277 } else { 278 Resampler sampler(media_raw_audio_format::B_AUDIO_FLOAT, 279 media_raw_audio_format::B_AUDIO_FLOAT); 280 281 sampler.Resample(fWaveView->waves[1].Raw(), sizeof(float), irate, 282 fWaveView->waves[2].Raw(), sizeof(float), orate, 1); 283 } 284 285 fWaveView->Invalidate(); 286 return; 287 } 288 } 289 290 BWindow::MessageReceived(message); 291 } 292 293 294 int main(int argc, char** argv) 295 { 296 BApplication app("application/x-vnd.Haiku-MixerToy"); 297 (new MainWindow())->Show(); 298 app.Run(); 299 } 300