-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscope.h
More file actions
171 lines (162 loc) · 5.8 KB
/
scope.h
File metadata and controls
171 lines (162 loc) · 5.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#ifndef CSYNTH_SCOPE_H
#define CSYNTH_SCOPE_H
#include <stdio.h>
#include <string.h>
#include "../../io/file.h"
#include "../../ui/render_pipe.h"
#include "../../util/error.h"
#include "../../util/fourier.h"
#include "../../util/math.h"
#include "../../util/worker.h"
/** @see scope_create */
typedef struct
{
/** @brief Image render target. */
RenderPipe *pipe;
/** @brief Whether to auto-detect the tick frequency. */
bool auto_detect;
/** @brief The detected tick frequency. */
double detected_tick;
/** @brief Sample input buffer. */
double *input_buffer;
/** @brief Sample phase buffer. */
double *phase_buffer;
/** @brief The number of trailing samples to display. */
size_t trail_count;
/** @brief The number of samples added to the buffer, up to trail_count. */
size_t trail_index;
/** @brief The current phase of the plot. */
double phase;
/** @brief Render buffer. */
double *render_input_buffer;
double *render_phase_buffer;
/** Async worker. */
Worker worker;
} ScopeContext;
static void scope_job(void *context_)
{
ScopeContext *context = (ScopeContext *)context_;
uint32_t *out_buffer = NULL;
size_t out_pitch = 0;
if (render_pipe_lock(context->pipe, &out_buffer, &out_pitch) != 0)
{
return;
}
memset(out_buffer, 0, context->pipe->width * context->pipe->height * sizeof(uint32_t));
for (size_t i = 0; i < context->trail_count; i++)
{
double phase = context->render_phase_buffer[i];
double input = context->render_input_buffer[i];
if (input <= -1.0 || input >= 1.0 || phase < 0.0 || phase >= 1.0)
{
continue;
}
size_t x_coord = (size_t)round(phase * (double)context->pipe->width);
size_t y_coord = (size_t)((double)context->pipe->height * (input + 1.0) * 0.5);
if (x_coord >= context->pipe->width || y_coord >= context->pipe->height)
{
continue;
}
uint32_t out = (uint32_t)math_clamp((double)0x100 * (double)i / (double)context->trail_count, 0, 0xFF);
out_buffer[y_coord * context->pipe->width + x_coord] = 0xFF000000 | (out << 16) | (out << 8) | out;
}
render_pipe_unlock(context->pipe);
if (context->auto_detect)
{
size_t window_size = 1;
while (window_size <= context->trail_count)
{
window_size <<= 1;
}
window_size >>= 1;
double db = 0.0, frequency = 0.0;
fourier_find_dominant(context->render_input_buffer, window_size, 1, &frequency, &db);
context->detected_tick = frequency;
}
}
static double scope_eval(__U size_t count, Gen **args, Eval *eval, void *context_)
{
ScopeContext *context = (ScopeContext *)context_;
double input = gen_eval(args[0], eval);
context->input_buffer[context->trail_index] = input;
context->phase_buffer[context->trail_index] = context->phase;
if (++context->trail_index >= context->trail_count)
{
context->trail_index = 0;
memcpy(context->render_input_buffer, context->input_buffer, context->trail_count * sizeof(double));
memcpy(context->render_phase_buffer, context->phase_buffer, context->trail_count * sizeof(double));
worker_run(&context->worker, scope_job, context);
}
context->phase += context->auto_detect ? context->detected_tick : gen_eval(args[1], eval);
while (context->phase >= 1.0)
{
context->phase -= 1.0;
}
return input;
}
static csError scope_init(__U size_t count, __U Gen **args, void *context_)
{
ScopeContext *context = (ScopeContext *)context_;
context->input_buffer = (double *)calloc_(context->trail_count, sizeof(double));
if (context->input_buffer == NULL)
{
return error_type(csErrorMemoryAlloc);
}
context->phase_buffer = (double *)calloc_(context->trail_count, sizeof(double));
if (context->phase_buffer == NULL)
{
free_(context->input_buffer);
return error_type(csErrorMemoryAlloc);
}
context->render_input_buffer = (double *)calloc_(context->trail_count, sizeof(double));
if (context->render_input_buffer == NULL)
{
free_(context->phase_buffer);
free_(context->input_buffer);
return error_type(csErrorMemoryAlloc);
}
context->render_phase_buffer = (double *)calloc_(context->trail_count, sizeof(double));
if (context->render_phase_buffer == NULL)
{
free_(context->phase_buffer);
free_(context->render_input_buffer);
free_(context->input_buffer);
return error_type(csErrorMemoryAlloc);
}
return worker_init(&context->worker);
}
static void scope_free(__U size_t count, void *context_)
{
ScopeContext *context = (ScopeContext *)context_;
worker_free(&context->worker);
free_(context->input_buffer);
free_(context->phase_buffer);
free_(context->render_input_buffer);
free_(context->render_phase_buffer);
}
/**
* @brief Creates a scope function that plots the wave of a given frequency.
*
* @param tick The frequency of the plot, i.e. which frequency will be primarily visible. NULL for auto-detect.
* @param trail_count The number of trailing samples to display.
* @param pipe The render pipe to use.
* @param input The input function.
* @return A pointer to the created scope function.
*/
Func *scope_create(Func *tick, size_t trail_count, Func *input, RenderPipe *pipe)
{
ScopeContext initial = {
.trail_count = trail_count,
.pipe = pipe,
.auto_detect = (tick == NULL),
};
if (tick == NULL)
{
return func_create(scope_init, scope_eval, scope_free, NULL, sizeof(ScopeContext), &initial, FuncFlagNone, input);
}
else
{
return func_create(scope_init, scope_eval, scope_free, NULL, sizeof(ScopeContext), &initial, FuncFlagNone, input, tick);
}
}
#endif // CSYNTH_SCOPE_H