about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-03-28 15:49:50 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-03-28 15:49:50 -0400
commit251248adfab36b061eb8fdd08aa54b318462da65 (patch)
tree4ff1eb792a1e1b442ec2fa6a99e01b28518c5cf4
parentaf49d3435302118d036c512aef1def848fb88089 (diff)
downloadcadence-251248adfab36b061eb8fdd08aa54b318462da65.tar.gz
cadence-251248adfab36b061eb8fdd08aa54b318462da65.tar.bz2
cadence-251248adfab36b061eb8fdd08aa54b318462da65.zip
Revamped bot with new algorithm
The percentages currently used by the generator are trial-and-error and may be changed in the future.

refs #1
-rw-r--r--cadence.cpp49
-rw-r--r--generator/CMakeLists.txt2
-rw-r--r--generator/generator.cpp109
-rw-r--r--generator/mood.cpp94
-rw-r--r--generator/mood.h60
-rw-r--r--generator/schema.sql10
m---------vendor/hkutil0
7 files changed, 121 insertions, 203 deletions
diff --git a/cadence.cpp b/cadence.cpp index 704f946..97a74b7 100644 --- a/cadence.cpp +++ b/cadence.cpp
@@ -13,8 +13,6 @@
13#include <hkutil/string.h> 13#include <hkutil/string.h>
14#include <hkutil/database.h> 14#include <hkutil/database.h>
15 15
16const std::vector<std::string> moods = {"party", "chill", "crazy", "happy", "sad", "instrumental", "vocal"};
17
18int main(int argc, char** argv) 16int main(int argc, char** argv)
19{ 17{
20 if (argc != 2) 18 if (argc != 2)
@@ -81,21 +79,25 @@ int main(int argc, char** argv)
81 { 79 {
82 std::cout << "Generating tweet..." << std::endl; 80 std::cout << "Generating tweet..." << std::endl;
83 81
84 std::string mood = moods[std::uniform_int_distribution<int>(0, moods.size()-1)(random_engine)]; 82 hatkirby::row songRow =
83 abdb.queryFirst(
84 "SELECT song_id, title, artist FROM songs ORDER BY RANDOM() LIMIT 1");
85 85
86 hatkirby::row song = 86 hatkirby::row moodRow =
87 abdb.queryFirst( 87 abdb.queryFirst(
88 "SELECT title, artist FROM songs WHERE category = ? ORDER BY RANDOM() LIMIT 1", 88 "SELECT mood FROM moods WHERE song_id = ? ORDER BY RANDOM() LIMIT 1",
89 { mood }); 89 { mpark::get<int>(songRow[0]) });
90 90
91 std::string songTitle = mpark::get<std::string>(song[0]); 91 std::string songTitle = mpark::get<std::string>(songRow[1]);
92 std::string songArtist = mpark::get<std::string>(song[1]); 92 std::string songArtist = mpark::get<std::string>(songRow[2]);
93 std::string mood = mpark::get<std::string>(moodRow[0]);
93 94
94 std::string action = "{" + hatkirby::uppercase(mood) + "}"; 95 std::string action = "{" + hatkirby::uppercase(mood) + "}";
95 int tknloc; 96 int tknloc;
96 while ((tknloc = action.find("{")) != std::string::npos) 97 while ((tknloc = action.find("{")) != std::string::npos)
97 { 98 {
98 std::string token = action.substr(tknloc+1, action.find("}")-tknloc-1); 99 std::string token = action.substr(tknloc+1, action.find("}")-tknloc-1);
100
99 std::string modifier; 101 std::string modifier;
100 int modloc; 102 int modloc;
101 if ((modloc = token.find(":")) != std::string::npos) 103 if ((modloc = token.find(":")) != std::string::npos)
@@ -104,10 +106,7 @@ int main(int argc, char** argv)
104 token = token.substr(0, modloc); 106 token = token.substr(0, modloc);
105 } 107 }
106 108
107 std::string canontkn; 109 std::string canontkn = hatkirby::uppercase(token);
108 std::transform(std::begin(token), std::end(token), std::back_inserter(canontkn), [] (char ch) {
109 return std::toupper(ch);
110 });
111 110
112 std::string result; 111 std::string result;
113 if (canontkn == "\\N") 112 if (canontkn == "\\N")
@@ -125,18 +124,23 @@ int main(int argc, char** argv)
125 124
126 result = group[dist(random_engine)]; 125 result = group[dist(random_engine)];
127 } else { 126 } else {
128 std::cout << "Badly formatted forms file:" << std::endl; 127 throw std::logic_error("No such form as " + canontkn);
129 std::cout << "No such form as " + canontkn << std::endl;
130
131 return 1;
132 } 128 }
133 129
134 if (modifier == "indefinite") 130 if (modifier == "indefinite")
135 { 131 {
136 if ((result.length() > 1) && (isupper(result[0])) && (isupper(result[1]))) 132 if (
133 (result.length() > 1) &&
134 isupper(result[0]) &&
135 isupper(result[1]))
137 { 136 {
138 result = "an " + result; 137 result = "an " + result;
139 } else if ((result[0] == 'a') || (result[0] == 'e') || (result[0] == 'i') || (result[0] == 'o') || (result[0] == 'u')) 138 } else if (
139 (result[0] == 'a') ||
140 (result[0] == 'e') ||
141 (result[0] == 'i') ||
142 (result[0] == 'o') ||
143 (result[0] == 'u'))
140 { 144 {
141 result = "an " + result; 145 result = "an " + result;
142 } else { 146 } else {
@@ -147,9 +151,7 @@ int main(int argc, char** argv)
147 std::string finalresult; 151 std::string finalresult;
148 if (islower(token[0])) 152 if (islower(token[0]))
149 { 153 {
150 std::transform(std::begin(result), std::end(result), std::back_inserter(finalresult), [] (char ch) { 154 finalresult = hatkirby::lowercase(result);
151 return std::tolower(ch);
152 });
153 } else if (isupper(token[0]) && !isupper(token[1])) 155 } else if (isupper(token[0]) && !isupper(token[1]))
154 { 156 {
155 auto words = hatkirby::split<std::list<std::string>>(result, " "); 157 auto words = hatkirby::split<std::list<std::string>>(result, " ");
@@ -158,7 +160,10 @@ int main(int argc, char** argv)
158 word[0] = std::toupper(word[0]); 160 word[0] = std::toupper(word[0]);
159 } 161 }
160 162
161 finalresult = hatkirby::implode(std::begin(words), std::end(words), " "); 163 finalresult = hatkirby::implode(
164 std::begin(words),
165 std::end(words),
166 " ");
162 } else { 167 } else {
163 finalresult = result; 168 finalresult = result;
164 } 169 }
diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index dffbe0e..8031f6a 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt
@@ -9,7 +9,7 @@ include_directories(
9 vendor/json 9 vendor/json
10 ../vendor/hkutil) 10 ../vendor/hkutil)
11 11
12add_executable(generator mood.cpp generator.cpp main.cpp) 12add_executable(generator generator.cpp main.cpp)
13set_property(TARGET generator PROPERTY CXX_STANDARD 11) 13set_property(TARGET generator PROPERTY CXX_STANDARD 11)
14set_property(TARGET generator PROPERTY CXX_STANDARD_REQUIRED ON) 14set_property(TARGET generator PROPERTY CXX_STANDARD_REQUIRED ON)
15target_link_libraries(generator ${sqlite3_LIBRARIES}) 15target_link_libraries(generator ${sqlite3_LIBRARIES})
diff --git a/generator/generator.cpp b/generator/generator.cpp index 19eba70..4244fd2 100644 --- a/generator/generator.cpp +++ b/generator/generator.cpp
@@ -6,7 +6,6 @@
6#include <json.hpp> 6#include <json.hpp>
7#include <hkutil/progress.h> 7#include <hkutil/progress.h>
8#include <hkutil/string.h> 8#include <hkutil/string.h>
9#include "mood.h"
10 9
11namespace cadence { 10namespace cadence {
12 namespace generator { 11 namespace generator {
@@ -93,7 +92,8 @@ namespace cadence {
93 DIR* subdir; 92 DIR* subdir;
94 if ((subdir = opendir(directory.c_str())) == nullptr) 93 if ((subdir = opendir(directory.c_str())) == nullptr)
95 { 94 {
96 throw std::invalid_argument("Invalid AcousticBrainz data directory"); 95 throw std::invalid_argument(
96 "Invalid AcousticBrainz data directory");
97 } 97 }
98 98
99 struct dirent* subent; 99 struct dirent* subent;
@@ -106,7 +106,8 @@ namespace cadence {
106 DIR* subsubdir; 106 DIR* subsubdir;
107 if ((subsubdir = opendir(subdirectory.c_str())) == nullptr) 107 if ((subsubdir = opendir(subdirectory.c_str())) == nullptr)
108 { 108 {
109 throw std::invalid_argument("Invalid AcousticBrainz data directory"); 109 throw std::invalid_argument(
110 "Invalid AcousticBrainz data directory");
110 } 111 }
111 112
112 struct dirent* subsubent; 113 struct dirent* subsubent;
@@ -149,27 +150,87 @@ namespace cadence {
149 150
150 try 151 try
151 { 152 {
152 std::vector<mood> moods; 153 auto& hl = jsonData["highlevel"];
153 moods.emplace_back(mood::type::danceable, jsonData["highlevel"]["danceability"]["all"]["danceable"]); 154
154 moods.emplace_back(mood::type::acoustic, jsonData["highlevel"]["mood_acoustic"]["all"]["acoustic"]); 155 double danceable = hl["danceability"]["all"]["danceable"];
155 moods.emplace_back(mood::type::aggressive, jsonData["highlevel"]["mood_aggressive"]["all"]["aggressive"]); 156 double acoustic = hl["mood_acoustic"]["all"]["acoustic"];
156 moods.emplace_back(mood::type::electronic, jsonData["highlevel"]["mood_electronic"]["all"]["electronic"]); 157 double aggressive = hl["mood_aggressive"]["all"]["aggressive"];
157 moods.emplace_back(mood::type::happy, jsonData["highlevel"]["mood_happy"]["all"]["happy"]); 158 double electronic = hl["mood_electronic"]["all"]["electronic"];
158 moods.emplace_back(mood::type::party, jsonData["highlevel"]["mood_party"]["all"]["party"]); 159 double happy = hl["mood_happy"]["all"]["happy"];
159 moods.emplace_back(mood::type::relaxed, jsonData["highlevel"]["mood_relaxed"]["all"]["relaxed"]); 160 double party = hl["mood_party"]["all"]["party"];
160 moods.emplace_back(mood::type::sad, jsonData["highlevel"]["mood_sad"]["all"]["sad"]); 161 double relaxed = hl["mood_relaxed"]["all"]["relaxed"];
161 moods.emplace_back(mood::type::instrumental, jsonData["highlevel"]["voice_instrumental"]["all"]["instrumental"]); 162 double sad = hl["mood_sad"]["all"]["sad"];
162 163 double instrumental = hl["voice_instrumental"]["all"]["instrumental"];
163 std::sort(std::begin(moods), std::end(moods), [] (const mood& left, const mood& right) -> bool { 164
164 return left.getProbability() > right.getProbability(); 165 std::string title = jsonData["metadata"]["tags"]["title"][0];
165 }); 166 std::string artist = jsonData["metadata"]["tags"]["artist"][0];
166 167
167 std::list<hatkirby::column> columns; 168 uint64_t songId = db_.insertIntoTable(
168 columns.emplace_back("title", jsonData["metadata"]["tags"]["title"][0].get<std::string>()); 169 "songs",
169 columns.emplace_back("artist", jsonData["metadata"]["tags"]["artist"][0].get<std::string>()); 170 {
170 columns.emplace_back("category", moods.front().getCategory()); 171 { "title", title },
171 172 { "artist", artist }
172 db_.insertIntoTable("songs", std::move(columns)); 173 });
174
175 std::list<std::string> moods;
176
177 // ~38%
178 if ((party > 0.5) || (danceable > 0.75))
179 {
180 moods.push_back("party");
181 }
182
183 // ~38%
184 if ((relaxed > 0.81) || (acoustic > 0.5))
185 {
186 moods.push_back("chill");
187 }
188
189 // ~42%
190 if ((aggressive > 0.5) || (electronic > 0.95))
191 {
192 moods.push_back("crazy");
193 }
194
195 // ~30%
196 if (happy > 0.5)
197 {
198 moods.push_back("happy");
199 }
200
201 // ~30%
202 if (sad > 0.5)
203 {
204 moods.push_back("sad");
205 }
206
207 // ~38%
208 if (instrumental > 0.9)
209 {
210 moods.push_back("instrumental");
211 }
212
213 // ~34%
214 if (instrumental < 0.2)
215 {
216 moods.push_back("vocal");
217 }
218
219 // ~1%
220 if (moods.empty())
221 {
222 moods.push_back("unknown");
223 }
224
225 for (const std::string& mood : moods)
226 {
227 db_.insertIntoTable(
228 "moods",
229 {
230 { "song_id", static_cast<int>(songId) },
231 { "mood", mood }
232 });
233 }
173 } catch (const std::domain_error& ex) 234 } catch (const std::domain_error& ex)
174 { 235 {
175 // Weird data. Ignore silently. 236 // Weird data. Ignore silently.
diff --git a/generator/mood.cpp b/generator/mood.cpp deleted file mode 100644 index b74211c..0000000 --- a/generator/mood.cpp +++ /dev/null
@@ -1,94 +0,0 @@
1#include "mood.h"
2
3namespace cadence {
4 namespace generator {
5
6 // The categories are:
7 // - party (+danceable, -acoustic, +electronic, +party) = ~.21
8 // - chill (-danceable, +acoustic, -aggressive, -electronic, -party, +relaxed) = ~.49
9 // - crazy (+aggressive, -relaxed) = ~.02
10 // - happy (+happy, -sad) = ~.009
11 // - sad (-happy, +sad) = ~.02
12 // - instrumental (+instrumental) = ~.12
13 // - vocal (-instrumental) = ~.10
14
15 mood::mood(type t, double prob) : type_(t)
16 {
17 if (prob >= 0.5)
18 {
19 probability_ = prob;
20 positive_ = true;
21 } else {
22 probability_ = 1.0 - prob;
23 positive_ = false;
24 }
25
26 switch (t)
27 {
28 case type::danceable:
29 {
30 category_ = (positive_ ? "party" : "chill");
31
32 break;
33 }
34
35 case type::acoustic:
36 {
37 category_ = (positive_ ? "chill" : "party");
38
39 break;
40 }
41
42 case type::aggressive:
43 {
44 category_ = (positive_ ? "crazy" : "chill");
45
46 break;
47 }
48
49 case type::electronic:
50 {
51 category_ = (positive_ ? "party" : "chill");
52
53 break;
54 }
55
56 case type::happy:
57 {
58 category_ = (positive_ ? "happy" : "sad");
59
60 break;
61 }
62
63 case type::party:
64 {
65 category_ = (positive_ ? "party" : "chill");
66
67 break;
68 }
69
70 case type::relaxed:
71 {
72 category_ = (positive_ ? "chill" : "crazy");
73
74 break;
75 }
76
77 case type::sad:
78 {
79 category_ = (positive_ ? "sad" : "happy");
80
81 break;
82 }
83
84 case type::instrumental:
85 {
86 category_ = (positive_ ? "instrumental" : "vocal");
87
88 break;
89 }
90 }
91 }
92
93 };
94};
diff --git a/generator/mood.h b/generator/mood.h deleted file mode 100644 index c36e5ee..0000000 --- a/generator/mood.h +++ /dev/null
@@ -1,60 +0,0 @@
1#ifndef MOOD_H_B9A39F40
2#define MOOD_H_B9A39F40
3
4#include <string>
5
6namespace cadence {
7 namespace generator {
8
9 class mood {
10 public:
11 enum class type {
12 danceable,
13 acoustic,
14 aggressive,
15 electronic,
16 happy,
17 party,
18 relaxed,
19 sad,
20 instrumental
21 };
22
23 // Constructor
24
25 mood(type t, double prob);
26
27 // Accessors
28
29 type getType() const
30 {
31 return type_;
32 }
33
34 double getProbability() const
35 {
36 return probability_;
37 }
38
39 bool getPositive() const
40 {
41 return positive_;
42 }
43
44 std::string getCategory() const
45 {
46 return category_;
47 }
48
49 private:
50 type type_;
51 double probability_;
52 bool positive_;
53 std::string category_;
54
55 };
56
57 };
58};
59
60#endif /* end of include guard: MOOD_H_B9A39F40 */
diff --git a/generator/schema.sql b/generator/schema.sql index 879ebb3..7d211ba 100644 --- a/generator/schema.sql +++ b/generator/schema.sql
@@ -1,5 +1,11 @@
1CREATE TABLE `songs` ( 1CREATE TABLE `songs` (
2 `song_id` INTEGER PRIMARY KEY,
2 `title` VARCHAR(255) NOT NULL, 3 `title` VARCHAR(255) NOT NULL,
3 `artist` VARCHAR(255) NOT NULL, 4 `artist` VARCHAR(255) NOT NULL
4 `category` VARCHAR(255) NOT NULL
5); 5);
6
7CREATE TABLE `moods` (
8 `song_id` INTEGER,
9 `mood` VARCHAR(255),
10 PRIMARY KEY (`song_id`, `mood`)
11) WITHOUT ROWID;
diff --git a/vendor/hkutil b/vendor/hkutil
Subproject 41f2eb087900d4f2ea95c3e11087cf2c71395ba Subproject 57e2f2262cd89f502f7a801b8ee7796e454a5e7