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
23static const int32 kMsgParametersChanged = 'pmch';
24
25
26// #pragma mark -
27
28
29class 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
57class 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
69WaveView::WaveView()
70	:
71	BView("wave", B_WILL_DRAW)
72{
73	SetExplicitMinSize(BSize(512, 256));
74}
75
76
77void
78WaveView::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
141static 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
154class 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
173MainWindow::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
234void
235MainWindow::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
295int main(int argc, char** argv)
296{
297	BApplication app("application/x-vnd.Haiku-MixerToy");
298	(new MainWindow())->Show();
299	app.Run();
300}
301