-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscripts.js
More file actions
321 lines (306 loc) · 13.4 KB
/
scripts.js
File metadata and controls
321 lines (306 loc) · 13.4 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
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/***********************
* VARIABLES DE CONFIGURACIÓN
***********************/
// Definición de las 5 secciones con 3 preguntas cada una (y etiquetas para la escala 1-5)
const sections = [
{
name: "COHERENCIA ESTRUCTURAL",
questions: [
{ text: "¿La melodía tiene una idea motívica clara (un tema reconocible)?", left: "Sin motivo discernible", right: "El motivo es claro y creativo" },
{ text: "¿Se percibe intencionalidad en la composición de melodía y armonía?", left: "Mucha aleatoriedad en la composición", right: "Se percibe intencionalidad en la composición" },
{ text: "¿Existe una sensación de equilibrio formal (simetría de la frase, repetición/contraste)?", left: "Caótico o desequilibrado", right: "Equilibrado e intencional" }
]
},
{
name: "INTEGRACIÓN ARMÓNICO-MELÓDICA",
questions: [
{ text: "¿Los acordes parecen tener un propósito en relación con la melodía?", left: "Acordes aleatorios / no relacionados", right: "Los acordes complementan la melodía muy bien" },
{ text: "¿La melodía en suma a la armonía se resuelven de una manera apropiada?", left: "Hay disonancias no resueltas", right: "La melodía se resuelve de manera elegante" },
{ text: "¿La armonía crea un centro tonal claro o alguna ambigüedad intencional?", left: "Tonalmente confuso", right: "Tonalmente intencional o creativamente ambiguo" }
]
},
{
name: "FLUIDEZ RÍTMICA",
questions: [
{ text: "¿El ritmo de la melodía se siente natural y con un propósito?", left: "El ritmo es inconexo", right: "El ritmo fluye naturalmente" },
{ text: "¿El ritmo armónico se alinea con el fraseo de la melodía?", left: "Los acordes cambian arbitrariamente", right: "El ritmo armónico mejora el fraseo" },
{ text: "¿Se identifican patrones rítmicos distintivos?", left: "Ritmo sin patrones claros", right: "Ritmo coherente" }
]
},
{
name: "IMPACTO EMOCIONAL Y EXPRESIVO",
questions: [
{ text: "¿La pieza evoca un estado de ánimo o emoción específicos?", left: "Sin carácter emocional", right: "La emoción es vívida y consistente" },
{ text: "¿Se identifica la separación de las frases y/o la articulación de ideas en la melodía?", left: "No se identifica la separación de frases", right: "Se siente la separación de frases" },
{ text: "¿La música se siente emocionalmente atractiva o memorable?", left: "Olvidable", right: "Muy atractiva/memorable" }
]
},
{
name: "ELEMENTOS INDIVIDUALES",
questions: [
{ text: "¿La progresión de acordes es buena por sí misma (sin considerar la melodía)?", left: "Progresión sin identidad propia", right: "Progresión buena y satisfactoria" },
{ text: "¿La melodía es buena por sí misma (sin considerar los acordes)?", left: "La melodía no es buena por sí misma", right: "La melodía es buena y satisfactoria" },
{ text: "¿El ritmo de las notas se adecúan bien al tipo de compás?", left: "El ritmo no se adecúa bien al tipo de compás", right: "El ritmo distingue bien el compás" }
]
},
{
name: "PREGUNTAS EXTRA",
questions: [
{ text: "¿Del 0 al 10 qué calificación le das a la pieza?", left: "", right: "", scaleMin: 0, scaleMax: 10 },
{ text: "¿Tiene potencial para convertirse en una canción/obra musical completa?", options: ["Sí", "No"] }
]
}
];
let songs = [];
let results = {};
let currentSongIndex = 0;
let currentSectionIndex = 0;
let currentQuestionIndex = 0;
let userData = {};
/***********************
* REFERENCIAS A ELEMENTOS DEL DOM
***********************/
const initialFormDiv = document.getElementById("initialForm");
const evaluationDiv = document.getElementById("evaluation");
const userForm = document.getElementById("userForm");
const startBtn = document.getElementById("startBtn");
const selectFolderBtn = document.getElementById("selectFolderBtn");
const songTitle = document.getElementById("songTitle");
const songTitleCounter = document.getElementById("songTitleCounter");
const songVideo = document.getElementById("songVideo");
const sectionTitle = document.getElementById("sectionTitle");
const sectionTitleCounter = document.getElementById("sectionTitleCounter");
const questionText = document.getElementById("questionText");
const leftLabel = document.getElementById("leftLabel");
const rightLabel = document.getElementById("rightLabel");
const scaleButtonsDiv = document.getElementById("scaleButtons");
const prevBtn = document.getElementById("prevBtn");
const nextBtn = document.getElementById("nextBtn");
/***********************
* CARGA DE CANCIONES DESDE LA CARPETA LOCAL
***********************/
async function loadSongsFromFolder() {
if (!window.showDirectoryPicker) {
alert("Tu navegador no soporta la API de Acceso al Sistema de Archivos.");
return;
}
try {
const directoryHandle = await window.showDirectoryPicker();
songs = [];
for await (const entry of directoryHandle.values()) {
if (entry.kind === "file" && entry.name.toLowerCase().endsWith(".mp4")) {
const file = await entry.getFile();
const fileURL = URL.createObjectURL(file);
songs.push({ name: entry.name, fileURL: fileURL });
}
}
songs.sort((a, b) => a.name.localeCompare(b.name));
if (songs.length === 0) {
alert("No se encontraron archivos mp4 en la carpeta seleccionada.");
} else {
alert(`Se cargaron ${songs.length} canción(es).`);
startBtn.disabled = false;
}
} catch (error) {
console.error("Error al cargar la carpeta:", error);
}
}
/***********************
* ACTUALIZACIÓN DE LA INFORMACIÓN DE PROGRESO
***********************/
function updateProgressInfo() {
// Actualización del contador de canciones
songTitleCounter.textContent = `[${currentSongIndex + 1} / ${songs.length}]`;
// Para secciones, se usa sections.length - 1 ya que la última es EXTRA
sectionTitleCounter.textContent = `[${currentSectionIndex + 1} / ${sections.length - 1}]`;
}
/***********************
* ACTUALIZACIÓN DEL VIDEO Y TÍTULO DE LA CANCIÓN
***********************/
function updateSong() {
const currentSong = songs[currentSongIndex];
songTitle.textContent = "MIA " + currentSong.name.replace(".mp4", "");
songVideo.src = currentSong.fileURL;
}
/***********************
* CREACIÓN DE LOS BOTONES DE RESPUESTA (dinámico)
***********************/
function createScaleButtons() {
scaleButtonsDiv.innerHTML = "";
const currentQ = sections[currentSectionIndex].questions[currentQuestionIndex];
if (currentQ.options) {
currentQ.options.forEach(option => {
const btn = document.createElement("button");
btn.textContent = option;
btn.onclick = function () {
recordAnswer(option);
nextBtn.disabled = false;
Array.from(scaleButtonsDiv.children).forEach(b => b.classList.remove("selected"));
btn.classList.add("selected");
};
scaleButtonsDiv.appendChild(btn);
});
} else if (currentQ.scaleMin !== undefined && currentQ.scaleMax !== undefined) {
for (let i = currentQ.scaleMin; i <= currentQ.scaleMax; i++) {
const btn = document.createElement("button");
btn.textContent = i;
btn.onclick = function () {
recordAnswer(i);
nextBtn.disabled = false;
Array.from(scaleButtonsDiv.children).forEach(b => b.classList.remove("selected"));
btn.classList.add("selected");
};
scaleButtonsDiv.appendChild(btn);
}
} else {
// Por defecto, escala de 1 a 5
for (let i = 1; i <= 5; i++) {
const btn = document.createElement("button");
btn.textContent = i;
btn.onclick = function () {
recordAnswer(i);
nextBtn.disabled = false;
Array.from(scaleButtonsDiv.children).forEach(b => b.classList.remove("selected"));
btn.classList.add("selected");
};
scaleButtonsDiv.appendChild(btn);
}
}
}
/***********************
* REGISTRO DE RESPUESTAS
***********************/
function recordAnswer(value) {
if (!results[currentSongIndex]) {
results[currentSongIndex] = {
songName: songs[currentSongIndex].name.replace(".mp4", ""),
sections: sections.map(sec =>
Array(sec.questions.length).fill(sec.name === "EXTRA" ? "" : 0)
)
};
}
results[currentSongIndex].sections[currentSectionIndex][currentQuestionIndex] = value;
}
/***********************
* ACTUALIZACIÓN DE LA PREGUNTA ACTUAL
***********************/
function updateQuestion() {
updateProgressInfo();
sectionTitle.textContent = sections[currentSectionIndex].name;
const currentQ = sections[currentSectionIndex].questions[currentQuestionIndex];
questionText.textContent = currentQ.text;
leftLabel.textContent = currentQ.left || "";
rightLabel.textContent = currentQ.right || "";
createScaleButtons();
// Si ya hay respuesta previa, se resalta el botón correspondiente
const prevAnswer = results[currentSongIndex]
? results[currentSongIndex].sections[currentSectionIndex][currentQuestionIndex]
: (currentQ.options ? "" : 0);
Array.from(scaleButtonsDiv.children).forEach(btn => {
if (btn.textContent == prevAnswer) {
btn.classList.add("selected");
nextBtn.disabled = false;
}
});
if (prevAnswer === 0 || prevAnswer === "") {
nextBtn.disabled = true;
}
prevBtn.disabled = (currentSectionIndex === 0 && currentQuestionIndex === 0);
}
/***********************
* NAVEGACIÓN ENTRE PREGUNTAS/SECCIONES
***********************/
function next() {
const answer = results[currentSongIndex].sections[currentSectionIndex][currentQuestionIndex];
if (answer === 0 || answer === "") {
alert("Por favor seleccione una respuesta antes de avanzar.");
return;
}
if (currentQuestionIndex < sections[currentSectionIndex].questions.length - 1) {
currentQuestionIndex++;
} else {
if (currentSectionIndex < sections.length - 1) {
currentSectionIndex++;
currentQuestionIndex = 0;
} else {
// Fin de la última sección: guardar y pasar a la siguiente canción
saveJSON("logs_" + new Date().toISOString().replace(/[:.]/g, "-") + ".json", results[currentSongIndex]);
setTimeout(() => {
nextSong();
}, 500);
return;
}
}
updateQuestion();
}
function prev() {
if (currentQuestionIndex > 0) {
currentQuestionIndex--;
} else if (currentSectionIndex > 0) {
currentSectionIndex--;
currentQuestionIndex = sections[currentSectionIndex].questions.length - 1;
}
updateQuestion();
}
/***********************
* CAMBIO DE CANCIÓN
***********************/
function nextSong() {
if (currentSongIndex < songs.length - 1) {
currentSongIndex++;
currentSectionIndex = 0;
currentQuestionIndex = 0;
if (!results[currentSongIndex]) {
results[currentSongIndex] = {
songName: songs[currentSongIndex].name.replace(".mp4", ""),
sections: sections.map(sec =>
Array(sec.questions.length).fill(sec.name === "EXTRA" ? "" : 0)
)
};
}
updateSong();
updateQuestion();
} else {
const finalData = { user: userData, results: results };
saveJSON(userData.initials + "_" + new Date().toISOString().replace(/[:.]/g, "-") + ".json", finalData);
alert("Evaluación completa. ¡Gracias por participar!");
}
}
/***********************
* UTILIDAD: GUARDAR JSON
***********************/
function saveJSON(filename, data) {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
/***********************
* EVENTOS
***********************/
prevBtn.addEventListener("click", prev);
nextBtn.addEventListener("click", next);
selectFolderBtn.addEventListener("click", loadSongsFromFolder);
userForm.addEventListener("submit", function (e) {
e.preventDefault();
userData.name = document.getElementById("name").value;
userData.gender = document.getElementById("gender").value;
userData.age = document.getElementById("age").value;
userData.semester = document.getElementById("semester").value;
userData.initials = userData.name.split(" ").map(n => n.charAt(0)).join("");
initialFormDiv.style.display = "none";
evaluationDiv.style.display = "block";
if (!results[currentSongIndex]) {
results[currentSongIndex] = {
songName: songs[currentSongIndex].name.replace(".mp4", ""),
sections: sections.map(sec =>
Array(sec.questions.length).fill(sec.name === "EXTRA" ? "" : 0)
)
};
}
updateSong();
updateQuestion();
});