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