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