-
Notifications
You must be signed in to change notification settings - Fork 2
/
user_fizzgig.cpp
executable file
·207 lines (191 loc) · 6.67 KB
/
user_fizzgig.cpp
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// SPDX-FileCopyrightText: 2019 Phillip Burgess for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#if 0 // Change to 1 to enable this code (must enable ONE user*.cpp only!)
#include "globals.h"
#include <Servo.h>
// Servo stuff
Servo myservo;
#define SERVO_MOUTH_OPEN 750 // Servo pulse microseconds
#define SERVO_MOUTH_CLOSED 1850
#define SERVO_PIN 3
#define BUTTON_PIN 2
// WAV player stuff
#define WAV_BUFFER_SIZE 256
static uint8_t wavBuf[2][WAV_BUFFER_SIZE];
static File wavFile;
static bool playing = false;
static int remainingBytesInChunk;
static uint8_t activeBuf;
static uint16_t bufIdx, bufEnd, nextBufEnd;
static bool startWav(char *filename);
static void wavOutCallback(void);
static uint32_t wavEventTime; // WAV start or end time, in ms
static const char *wav_path = "fizzgig";
static struct wavlist { // Linked list of WAV filenames
char *filename;
struct wavlist *next;
} *wavListStart = NULL, *wavListPtr = NULL;
#define MAX_WAV_FILES 20
void user_setup(void) {
File entry;
struct wavlist *wptr;
char filename[SD_MAX_FILENAME_SIZE+1];
// Scan wav_path for .wav files:
for(int i=0; i<MAX_WAV_FILES; i++) {
entry = arcada.openFileByIndex(wav_path, i, O_READ, "wav");
if(!entry) break;
// Found one, alloc new wavlist struct, try duplicating filename
if((wptr = (struct wavlist *)malloc(sizeof(struct wavlist)))) {
entry.getName(filename, SD_MAX_FILENAME_SIZE);
if((wptr->filename = strdup(filename))) {
// Alloc'd OK, add to linked list...
if(wavListPtr) { // List already started?
wavListPtr->next = wptr; // Point prior last item to new one
} else {
wavListStart = wptr; // Point list head to new item
}
wavListPtr = wptr; // Update last item to new one
} else {
free(wptr); // Alloc failed, delete interim stuff
}
}
entry.close();
}
if(wavListPtr) { // Any items in WAV list?
wavListPtr->next = wavListStart; // Point last item's next to list head (list is looped)
wavListPtr = wavListStart; // Update list pointer to head
}
}
void user_loop(void) {
if(playing) {
// While WAV is playing, wiggle servo between middle and open-mouth positions:
uint32_t elapsed = millis() - wavEventTime; // Time since audio start
uint16_t frac = elapsed % 500; // 0 to 499 = 0.5 sec
float n = 1.0 - ((float)abs(250 - frac) / 500.0); // Ramp 0.5, 1.0, 0.5 in 0.5 sec
myservo.writeMicroseconds((int)((float)SERVO_MOUTH_CLOSED + (float)(SERVO_MOUTH_OPEN - SERVO_MOUTH_CLOSED) * n));
// BUTTON_PIN button is ignored while sound is playing.
} else if(wavListPtr) {
// Not currently playing WAV. Check for button press on pin BUTTON_PIN.
pinMode(BUTTON_PIN, INPUT_PULLUP);
delayMicroseconds(20); // Avoid boop code interference
if(!digitalRead(BUTTON_PIN)) {
arcada.chdir(wav_path);
startWav(wavListPtr->filename);
wavListPtr = wavListPtr->next; // Will loop around from end to start of list
}
pinMode(BUTTON_PIN, INPUT);
if(myservo.attached()) { // If servo still active (from recent WAV playing)
myservo.writeMicroseconds(SERVO_MOUTH_CLOSED); // Make sure it's in closed position
// If it's been more than 1 sec since audio stopped,
// deactivate the servo to reduce power, heat & noise.
if((millis() - wavEventTime) > 1000) {
myservo.detach();
}
}
}
}
static uint16_t readWaveData(uint8_t *dst) {
if(remainingBytesInChunk <= 0) {
// Read next chunk
struct {
char id[4];
uint32_t size;
} header;
for(;;) {
if(wavFile.read(&header, 8) != 8) return 0;
if(!strncmp(header.id, "data", 4)) {
remainingBytesInChunk = header.size;
break;
}
if(!wavFile.seekCur(header.size)) { // If not "data" then skip
return 0; // Seek failed, return invalid count
}
}
}
int16_t bytesRead = wavFile.read(dst, min(WAV_BUFFER_SIZE, remainingBytesInChunk));
if(bytesRead > 0) remainingBytesInChunk -= bytesRead;
return bytesRead;
}
// Partially swiped from Wave Shield code.
// Is pared-down, handles 8-bit mono only to keep it simple.
static bool startWav(char *filename) {
wavFile = arcada.open(filename);
if(!wavFile) {
Serial.println("Failed to open WAV file");
return false;
}
union {
struct {
char id[4];
uint32_t size;
char data[4];
} riff; // riff chunk
struct {
uint16_t compress;
uint16_t channels;
uint32_t sampleRate;
uint32_t bytesPerSecond;
uint16_t blockAlign;
uint16_t bitsPerSample;
uint16_t extraBytes;
} fmt; // fmt data
} buf;
uint16_t size;
if((wavFile.read(&buf, 12) == 12)
&& !strncmp(buf.riff.id, "RIFF", 4)
&& !strncmp(buf.riff.data, "WAVE", 4)) {
// next chunk must be fmt, fmt chunk size must be 16 or 18
if((wavFile.read(&buf, 8) == 8)
&& !strncmp(buf.riff.id, "fmt ", 4)
&& (((size = buf.riff.size) == 16) || (size == 18))
&& (wavFile.read(&buf, size) == size)
&& ((size != 18) || (buf.fmt.extraBytes == 0))) {
if((buf.fmt.channels == 1) && (buf.fmt.bitsPerSample == 8)) {
Serial.printf("Samples/sec: %d\n", buf.fmt.sampleRate);
bufEnd = readWaveData(wavBuf[0]);
if(bufEnd > 0) {
// Initialize A/D, speaker and start timer
analogWriteResolution(8);
analogWrite(A0, 128);
analogWrite(A1, 128);
arcada.enableSpeaker(true);
wavEventTime = millis(); // WAV starting time
bufIdx = 0;
playing = true;
arcada.timerCallback(buf.fmt.sampleRate, wavOutCallback);
nextBufEnd = readWaveData(wavBuf[1]);
myservo.attach(SERVO_PIN);
}
return true;
} else {
Serial.println("Only 8-bit mono WAVs are supported");
}
} else {
Serial.println("WAV uses compression or other unrecognized setting");
}
} else {
Serial.println("Not WAV file");
}
wavFile.close();
return false;
}
static void wavOutCallback(void) {
uint8_t n = wavBuf[activeBuf][bufIdx];
analogWrite(A0, n);
analogWrite(A1, n);
if(++bufIdx >= bufEnd) {
if(nextBufEnd <= 0) {
arcada.timerStop();
arcada.enableSpeaker(false);
playing = false;
wavEventTime = millis(); // Same var now holds WAV end time
return;
}
bufIdx = 0;
bufEnd = nextBufEnd;
nextBufEnd = readWaveData(wavBuf[activeBuf]);
activeBuf = 1 - activeBuf;
}
}
#endif // 0