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:
Wave()32 Wave()
33 :
34 fData(NULL)
35 { Resize(1); step = 1; }
ValueAt(int index)36 float ValueAt(int index) { return fData[index % fSize]; }
SetValue(int pos,float val)37 void SetValue(int pos, float val) { fData[pos] = val; }
Resize(int newSize)38 void Resize(int newSize) {
39 if (newSize <= 0)
40 newSize = 1;
41 fData = (float*)realloc(fData, newSize * sizeof(float));
42 fSize = newSize;
43 }
Size()44 int Size() { return fSize; }
Raw()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
WaveView()69 WaveView::WaveView()
70 :
71 BView("wave", B_WILL_DRAW)
72 {
73 SetExplicitMinSize(BSize(512, 256));
74 }
75
76
77 void
Draw(BRect update)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
makeFormatMenu()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
MainWindow()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
MessageReceived(BMessage * message)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
main(int argc,char ** argv)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