Skip to content

Commit 8511867

Browse files
committed
added frame group support
1 parent 57d6b36 commit 8511867

File tree

3 files changed

+135
-44
lines changed

3 files changed

+135
-44
lines changed

include/DMDUtil/SceneGenerator.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,15 @@ struct SceneData
2020
int sceneId;
2121
int frameCount;
2222
int durationPerFrame;
23-
bool interruptable;
23+
bool interruptable = false;
24+
bool immediateStart = false;
25+
int repeat = 0; // 0 - play once, 1 - loop, >= 2 - repeat x times
26+
int frameGroup = 0;
27+
bool random = false;
28+
int autoStart = 0; // 0 - no autostart, >= 1 - start this scene after x seconds of inactivity (no new frames), only
29+
// use once, could be combined with frame groups
30+
int endFrame = 0; // 0 - when scene is finished, show last frame of the scene until a new frame is matched, 1 - black
31+
// screen, 2 - show last frame before scene started
2432
};
2533

2634
class DMDUTILAPI SceneGenerator
@@ -31,7 +39,7 @@ class DMDUTILAPI SceneGenerator
3139
bool parseCSV(const std::string& csv_filename);
3240
bool generateDump(const std::string& dump_filename, int id = -1);
3341
bool getSceneInfo(int sceneId, int& frameCount, int& durationPerFrame, bool& interruptable) const;
34-
bool generateFrame(int sceneId, int frameIndex, uint8_t* buffer);
42+
bool generateFrame(int sceneId, int frameIndex, uint8_t* buffer, int group = -1);
3543
void setDepth(int depth);
3644
void Reset()
3745
{
@@ -57,6 +65,7 @@ class DMDUTILAPI SceneGenerator
5765
void initializeTemplate();
5866

5967
int m_depth = 2; // Default depth for rendering
68+
int m_currentGroup = 0;
6069
};
6170

6271
} // namespace DMDUtil

scene_test.csv

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
1-
# Format:
2-
# PUP Trigger ID, Number od Frames, Duration of a Frame, Interruptable
1+
# Format of a PUP scene line:
32
#
3+
# 1: PUP trigger ID,
4+
# 2: number of frames,
5+
# 3: duration of each frame,
6+
# 4: 0 - not interruptable, 1 - interruptable by frame match or PUP trigger
7+
# 5: 0 - start immediately, replacing triggering frame, 1 - start after frame duration (see 3)
8+
# 6: 0 - play once, 1 - loop, >= 2 - repeat x times
9+
# 7: 0 - no frame groups, >= 2 - create x frame groups (you get x times the number of frames entered in 2 to play changing scenes)
10+
# 8: 0 - play frame group in order, 1 - play random frame group
11+
# 9: 0 - no autostart, >= 1 - start this scene after x seconds of inactivity (no new frames), only use once, could be combined with frame groups
12+
# 10: 0 - when scene is finished, show last frame of the scene until a new frame is matched, 1 - black screen, 2 - show last frame before scene started
13+
#
14+
# Postions 4 - 10 are optional. If not provided, the default is 0.
15+
416
# Scene 60001
5-
60001,38,16,0
6-
# Scene 63479
7-
63479,52,20,1
17+
60001,38,16
18+
19+
# Scene 61479, interruptable
20+
61479,52,20,1
21+
22+
# Scene 63500, interruptable, starting delayed by frame duration, loop
23+
63500,40,20,1,1,1
24+
25+
# Scene 63511, interruptable, starting delayed by frame duration, play once, 3 frame groups
26+
# This will generate 3 groups of 75 frames, so 225 frames in total.
27+
# Every time the PUP ID 63511 get triggered, only one of the three groups gets played (75 frames).
28+
# First occurrence of the PUP ID will play group 1, the second group 2. After the third group, it will start again with group 1.
29+
63511,75,20,1,1,0,3
30+
31+
# This will generate 4 groups of 200 frames, so 800 frames in total.
32+
# If no frame is matched for 5 seconds, randomly one of the 4 groups gets played once.
33+
# When the 100 frames of the scene ended, the previous original frame will be shown.
34+
# The internal timer gets reseted and after 5 more seconds of inactivity, another group of 100 frames gets randomly played
35+
#
36+
# This way an attract mode for early electronic games with segment displays can be implemented.
37+
# Normally, the last scores are shown until someone starts a new game.
38+
# Now the scores are displayed for 5 seconds, than a random short video scene out of four gets played for 4 seconds.
39+
# Then the scores are shown again and so on until someone starts a game.
40+
63600,200,20,1,1,0,4,1,5,2

src/SceneGenerator.cpp

Lines changed: 86 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ std::string formatNumber(int num, int width)
2828

2929
// Constants for text positioning
3030
const int SCENE_Y = 2;
31-
const int FRAME_Y = 12;
32-
const int DURATION_Y = 22;
31+
const int GROUP_Y = 12;
32+
const int FRAME_Y = 22;
3333
const int RIGHT_ALIGN_X = 127;
3434
const int NUMBER_WIDTH = 5;
3535
const int NUMBER_PIXELS = NUMBER_WIDTH * 6 - 1; // 5 digits * 6px/digit - last space
@@ -97,14 +97,13 @@ bool SceneGenerator::parseCSV(const std::string& csv_filename)
9797
data.sceneId = std::stoi(row[0]);
9898
data.frameCount = std::stoi(row[1]);
9999
data.durationPerFrame = std::stoi(row[2]);
100-
101-
// Default to non-interruptable if not specified
102-
data.interruptable = false;
103-
if (row.size() >= 4)
104-
{
105-
int interruptValue = std::stoi(row[3]);
106-
data.interruptable = (interruptValue == 1);
107-
}
100+
if (row.size() >= 4) data.interruptable = (std::stoi(row[3]) == 1);
101+
if (row.size() >= 5) data.immediateStart = (std::stoi(row[4]) == 1);
102+
if (row.size() >= 6) data.repeat = std::stoi(row[5]);
103+
if (row.size() >= 7) data.frameGroup = std::stoi(row[6]);
104+
if (row.size() >= 8) data.random = (std::stoi(row[7]) == 1);
105+
if (row.size() >= 9) data.autoStart = std::stoi(row[8]);
106+
if (row.size() >= 10) data.endFrame = std::stoi(row[9]);
108107

109108
m_sceneData.push_back(data);
110109
}
@@ -135,33 +134,37 @@ bool SceneGenerator::generateDump(const std::string& dump_filename, int id)
135134
continue; // Skip scenes that don't match the specified ID
136135
}
137136

138-
for (int frameIndex = 1; frameIndex <= scene.frameCount; frameIndex++)
137+
int goups = scene.frameGroup > 0 ? scene.frameGroup : 1;
138+
for (int group = 1; group <= goups; group++)
139139
{
140-
cumulative_duration += static_cast<uint32_t>(scene.durationPerFrame);
140+
for (int frameIndex = 0; frameIndex < scene.frameCount; frameIndex++)
141+
{
142+
cumulative_duration += static_cast<uint32_t>(scene.durationPerFrame);
141143

142-
// Format as 8-digit hex with 0x prefix
143-
char hex_line[11];
144-
std::snprintf(hex_line, sizeof(hex_line), "0x%08x", cumulative_duration);
145-
out_dump << hex_line << "\r\n";
144+
// Format as 8-digit hex with 0x prefix
145+
char hex_line[11];
146+
std::snprintf(hex_line, sizeof(hex_line), "0x%08x", cumulative_duration);
147+
out_dump << hex_line << "\r\n";
146148

147-
uint8_t frameBuffer[4096];
148-
if (!generateFrame(scene.sceneId, frameIndex, frameBuffer))
149-
{
150-
std::cerr << "Error generating frame " << frameIndex << " for scene " << scene.sceneId << std::endl;
151-
continue;
152-
}
149+
uint8_t frameBuffer[4096];
150+
if (!generateFrame(scene.sceneId, frameIndex, frameBuffer, group))
151+
{
152+
std::cerr << "Error generating frame " << frameIndex << " for scene " << scene.sceneId << std::endl;
153+
continue;
154+
}
153155

154-
// Write 128x32 grid
155-
for (int row = 0; row < 32; row++)
156-
{
157-
for (int col = 0; col < 128; col++)
156+
// Write 128x32 grid
157+
for (int row = 0; row < 32; row++)
158158
{
159-
uint8_t value = frameBuffer[row * 128 + col];
160-
out_dump << static_cast<char>(value ? (m_depth == 2 ? '3' : 'f') : '0');
159+
for (int col = 0; col < 128; col++)
160+
{
161+
uint8_t value = frameBuffer[row * 128 + col];
162+
out_dump << static_cast<char>(value ? (m_depth == 2 ? '3' : 'f') : '0');
163+
}
164+
out_dump << "\r\n";
161165
}
162-
out_dump << "\r\n";
166+
out_dump << "\r\n"; // Empty line between frames
163167
}
164-
out_dump << "\r\n"; // Empty line between frames
165168
}
166169
}
167170

@@ -184,7 +187,7 @@ bool SceneGenerator::getSceneInfo(int sceneId, int& frameCount, int& durationPer
184187
return true;
185188
}
186189

187-
bool SceneGenerator::generateFrame(int sceneId, int frameIndex, uint8_t* buffer)
190+
bool SceneGenerator::generateFrame(int sceneId, int frameIndex, uint8_t* buffer, int group)
188191
{
189192
auto it = std::find_if(m_sceneData.begin(), m_sceneData.end(),
190193
[sceneId](const SceneData& data) { return data.sceneId == sceneId; });
@@ -194,23 +197,36 @@ bool SceneGenerator::generateFrame(int sceneId, int frameIndex, uint8_t* buffer)
194197
return false;
195198
}
196199

197-
if (frameIndex < 1 || frameIndex > it->frameCount)
200+
if (frameIndex < 0 || frameIndex >= it->frameCount)
198201
{
199202
return false;
200203
}
201204

205+
if (frameIndex == 0)
206+
{
207+
if (group == -1)
208+
{
209+
// @todo random or order play.
210+
m_currentGroup = 1;
211+
}
212+
else
213+
{
214+
m_currentGroup = group;
215+
}
216+
}
217+
202218
// Copy pre-rendered template
203219
std::memcpy(buffer, m_template.fullFrame, 4096);
204220

205221
// Render dynamic numbers at right-aligned positions
206222
std::string sceneIdStr = formatNumber(sceneId, NUMBER_WIDTH);
207223
renderString(buffer, sceneIdStr, NUM_X, SCENE_Y);
208224

209-
std::string frameStr = formatNumber(frameIndex, NUMBER_WIDTH);
210-
renderString(buffer, frameStr, NUM_X, FRAME_Y);
225+
std::string groupStr = formatNumber(m_currentGroup, NUMBER_WIDTH);
226+
renderString(buffer, groupStr, NUM_X, GROUP_Y);
211227

212-
// std::string durStr = formatNumber(it->durationPerFrame, NUMBER_WIDTH);
213-
// renderString(buffer, durStr, NUM_X, DURATION_Y);
228+
std::string frameStr = formatNumber(frameIndex + 1, NUMBER_WIDTH);
229+
renderString(buffer, frameStr, NUM_X, FRAME_Y);
214230

215231
return true;
216232
}
@@ -224,8 +240,8 @@ void SceneGenerator::initializeTemplate()
224240

225241
// Render fixed text to template at new positions
226242
renderString(m_template.fullFrame, "PUP SCENE ID", 0, SCENE_Y);
243+
renderString(m_template.fullFrame, "FRAME GROUP", 0, GROUP_Y);
227244
renderString(m_template.fullFrame, "FRAME NUMBER", 0, FRAME_Y);
228-
// renderString(m_template.fullFrame, "FRAME DURATION", 0, DURATION_Y);
229245

230246
m_templateInitialized = true;
231247
}
@@ -250,15 +266,26 @@ const unsigned char* SceneGenerator::getCharFont(char c) const
250266
static const unsigned char D[7] = {30, 17, 17, 17, 17, 17, 30}; // 'D'
251267
static const unsigned char E[7] = {31, 16, 16, 30, 16, 16, 31}; // 'E'
252268
static const unsigned char F[7] = {31, 16, 16, 30, 16, 16, 16}; // 'F'
269+
static const unsigned char G[7] = {14, 17, 16, 19, 17, 17, 14}; // 'G'
270+
static const unsigned char H[7] = {17, 17, 17, 31, 17, 17, 17}; // 'H'
253271
static const unsigned char I[7] = {31, 4, 4, 4, 4, 4, 31}; // 'I'
272+
static const unsigned char J[7] = {15, 2, 2, 2, 18, 18, 12}; // 'J'
273+
static const unsigned char K[7] = {17, 18, 20, 24, 20, 18, 17}; // 'K'
274+
static const unsigned char L[7] = {16, 16, 16, 16, 16, 16, 31}; // 'L'
254275
static const unsigned char M[7] = {17, 27, 21, 17, 17, 17, 17}; // 'M'
255276
static const unsigned char N[7] = {17, 17, 25, 21, 19, 17, 17}; // 'N'
256277
static const unsigned char O[7] = {14, 17, 17, 17, 17, 17, 14}; // 'O'
257278
static const unsigned char P[7] = {30, 17, 17, 30, 16, 16, 16}; // 'P'
279+
static const unsigned char Q[7] = {14, 17, 17, 17, 21, 18, 13}; // 'Q'
258280
static const unsigned char R[7] = {30, 17, 17, 30, 18, 17, 17}; // 'R'
259281
static const unsigned char S[7] = {14, 17, 16, 14, 1, 17, 14}; // 'S'
260282
static const unsigned char T[7] = {31, 4, 4, 4, 4, 4, 4}; // 'T'
261283
static const unsigned char U[7] = {17, 17, 17, 17, 17, 17, 14}; // 'U'
284+
static const unsigned char V[7] = {17, 17, 17, 17, 10, 10, 4}; // 'V'
285+
static const unsigned char W[7] = {17, 17, 17, 21, 21, 21, 10}; // 'W'
286+
static const unsigned char X[7] = {17, 17, 10, 4, 10, 17, 17}; // 'X'
287+
static const unsigned char Y[7] = {17, 17, 10, 4, 4, 4, 4}; // 'Y'
288+
static const unsigned char Z[7] = {31, 1, 2, 4, 8, 16, 31}; // 'Z'
262289
static const unsigned char space[7] = {0}; // ' '
263290

264291
if (c >= '0' && c <= '9')
@@ -281,8 +308,18 @@ const unsigned char* SceneGenerator::getCharFont(char c) const
281308
return E;
282309
case 'F':
283310
return F;
311+
case 'G':
312+
return G;
313+
case 'H':
314+
return H;
284315
case 'I':
285316
return I;
317+
case 'J':
318+
return J;
319+
case 'K':
320+
return K;
321+
case 'L':
322+
return L;
286323
case 'M':
287324
return M;
288325
case 'N':
@@ -291,6 +328,8 @@ const unsigned char* SceneGenerator::getCharFont(char c) const
291328
return O;
292329
case 'P':
293330
return P;
331+
case 'Q':
332+
return Q;
294333
case 'R':
295334
return R;
296335
case 'S':
@@ -299,6 +338,16 @@ const unsigned char* SceneGenerator::getCharFont(char c) const
299338
return T;
300339
case 'U':
301340
return U;
341+
case 'V':
342+
return V;
343+
case 'W':
344+
return W;
345+
case 'X':
346+
return X;
347+
case 'Y':
348+
return Y;
349+
case 'Z':
350+
return Z;
302351
default:
303352
return space;
304353
}

0 commit comments

Comments
 (0)