-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathmemory.cpp
More file actions
179 lines (146 loc) · 6.45 KB
/
memory.cpp
File metadata and controls
179 lines (146 loc) · 6.45 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
172
173
174
175
176
177
178
#include "memory.h"
// Note: EEPROM operations are in main .ino file to avoid linking issues
// ============================================================================
// Recording Operations
// ============================================================================
void startRecording(RecordingState& state, uint8_t slotNumber) {
if (slotNumber >= MAX_MEMORY_SLOTS) return;
state.startRecording(slotNumber);
Serial.print("Started recording to memory slot ");
Serial.println(slotNumber + 1);
}
void recordKeyEvent(RecordingState& state, bool keyDown, uint8_t paddle) {
if (!state.isRecording) return;
unsigned long now = millis();
unsigned long duration = now - state.lastEventTime;
// Handle the state transition
if (keyDown != state.keyCurrentlyDown) {
// On very first key down, don't record the delay before it - just start timing
if (state.transitionCount == 0 && !state.keyCurrentlyDown && keyDown) {
Serial.print("REC: First key DOWN - starting timing (paddle=");
Serial.print(paddle == PADDLE_DIT_FLAG ? "DIT" : "DAH");
Serial.println(")");
state.currentPaddle = paddle;
} else if (state.transitionCount < MAX_TRANSITIONS_PER_MEMORY) {
// Record the previous transition with its paddle info
uint16_t encodedTransition = ENCODE_TRANSITION(state.currentPaddle, duration);
state.transitions[state.transitionCount++] = encodedTransition;
Serial.print("REC[");
Serial.print(state.transitionCount - 1);
Serial.print("]: ");
Serial.print(state.keyCurrentlyDown ? "DN" : "UP"); // Print what we just recorded (previous state duration)
Serial.print(" paddle=");
Serial.print(state.currentPaddle == PADDLE_DIT_FLAG ? "DIT" : "DAH");
Serial.print(" dur=");
Serial.print(duration);
Serial.println("ms");
// If this was a key-up event ending, update the last key-release time
if (!keyDown) {
state.lastKeyReleaseTime = now;
}
// Update paddle for next transition if key is going down
if (keyDown) {
state.currentPaddle = paddle;
}
}
state.keyCurrentlyDown = keyDown;
state.lastEventTime = now;
} else if (keyDown && paddle != state.currentPaddle) {
// Paddle changed while key is still down (shouldn't normally happen, but handle it)
state.currentPaddle = paddle;
}
}
void stopRecording(RecordingState& state, CWMemory& memory) {
if (!state.isRecording) return;
// Calculate the time elapsed since the last key-release
unsigned long now = millis();
unsigned long timeSinceLastRelease = now - state.lastKeyReleaseTime;
Serial.print("Recording stopped. Time since last key release: ");
Serial.print(timeSinceLastRelease);
Serial.println("ms");
// If the last recorded transition was a key-down (odd count), we need to add a final key-up
// to properly end the tone. Use a short fixed duration (50ms) for this final spacing.
if (state.transitionCount > 0 && (state.transitionCount % 2) == 1) {
const uint16_t FINAL_KEY_UP_DURATION = 50; // Short spacing to end the final tone
if (state.transitionCount < MAX_TRANSITIONS_PER_MEMORY) {
uint16_t encodedTransition = ENCODE_TRANSITION(state.currentPaddle, FINAL_KEY_UP_DURATION);
state.transitions[state.transitionCount++] = encodedTransition;
Serial.print("Added final key-UP transition: ");
Serial.print(FINAL_KEY_UP_DURATION);
Serial.println("ms");
}
}
// Copy the recording to the memory structure
memory.transitionCount = state.transitionCount;
for (uint16_t i = 0; i < state.transitionCount; i++) {
memory.transitions[i] = state.transitions[i];
}
state.stopRecording();
Serial.print("Recorded ");
Serial.print(memory.transitionCount);
Serial.print(" transitions (");
Serial.print(memory.getDurationMs());
Serial.println("ms)");
}
// ============================================================================
// Playback Operations
// ============================================================================
bool startPlayback(PlaybackState& state, uint8_t slotNumber, CWMemory& memory) {
if (slotNumber >= MAX_MEMORY_SLOTS) return false;
if (memory.isEmpty()) return false;
state.startPlayback(slotNumber, &memory);
Serial.print("Started playback of memory slot ");
Serial.print(slotNumber + 1);
Serial.print(" (");
Serial.print(memory.transitionCount);
Serial.print(" transitions, ");
Serial.print(memory.getDurationMs());
Serial.println("ms)");
return true;
}
void updatePlayback(PlaybackState& state) {
if (!state.isPlaying || state.memory == nullptr) return;
// Check if we're past the last transition (cleanup phase)
if (state.currentTransitionIndex >= state.memory->transitionCount) {
// If key is still down, turn it off
if (state.keyCurrentlyDown) {
state.keyCurrentlyDown = false;
Serial.println("Final key-UP before playback complete");
return; // Let main loop process the key-up, then we'll stop on next update
}
// Key is up, safe to stop
state.stopPlayback();
Serial.println("Playback complete");
return;
}
unsigned long now = millis();
unsigned long elapsed = now - state.transitionStartTime;
// Decode current transition
uint16_t encodedTransition = state.memory->transitions[state.currentTransitionIndex];
uint16_t duration = DECODE_DURATION(encodedTransition);
uint8_t paddle = DECODE_PADDLE(encodedTransition);
// Check if current transition is complete
if (elapsed >= duration) {
// Log the transition that just completed (showing the state that WAS active)
Serial.print("PLAY[");
Serial.print(state.currentTransitionIndex);
Serial.print("]: ");
Serial.print(state.keyCurrentlyDown ? "DN" : "UP"); // Current state (before toggle)
Serial.print(" paddle=");
Serial.print(paddle == PADDLE_DIT_FLAG ? "DIT" : "DAH");
Serial.print(" dur=");
Serial.print(duration);
Serial.println("ms");
// Toggle key state for next transition
state.keyCurrentlyDown = !state.keyCurrentlyDown;
// Move to next transition
state.currentTransitionIndex++;
// Start timing for the next transition (if there is one)
if (state.currentTransitionIndex < state.memory->transitionCount) {
state.transitionStartTime = now;
// Decode the paddle for the NEXT transition (for correct routing on next key-down)
uint16_t nextEncoded = state.memory->transitions[state.currentTransitionIndex];
state.currentPaddle = DECODE_PADDLE(nextEncoded);
}
}
}