xref: /haiku/src/tests/add-ons/media/media-add-ons/mixer/main.cpp (revision 0de25abadc86e260328c6f7c4255acbee8f70d4e)
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