Crear una lista que se ajuste a tu pausa

C++Beginner
Practicar Ahora

Introducción

Una pausa tiene un límite de tiempo. Tu biblioteca musical contiene grabaciones completas con una puntuación de preferencia, una duración en segundos y un ID de pista estable. Crearás un programa C++ reutilizable que considere las pistas en un orden público, conserve cada grabación que quepa y guarde una lista que puedas reproducir de verdad.

Necesitas navegación básica por la terminal y conocimientos de cadenas, clases, vector, flujos de archivos y excepciones de C++. Aquí introduciremos las referencias const, la función de comparación y las rutas relativas utilizadas. El analizador, el audio sintético original y el reproductor del navegador están preparados. Escribirás las funciones de selección y exportación, no un conversor de audio ni un algoritmo de empaquetado óptimo.

Usa la pestaña Terminal para los comandos y Player para escuchar. Ambas utilizan este mismo entorno. Edita únicamente selection.cpp y export.cpp; crea las listas directamente en outputs/. Conserva sin cambios el catálogo, el marco proporcionado y los originales de outputs/audio/. Estas composiciones sintéticas originales no utilizan muestras de audio externas.

Encajar pistas completas en una pausa

En este paso, escucharás un ejemplo normal y después utilizarás un contador de tiempo restante para seleccionar grabaciones completas. Empieza en Player. Selecciona example-120.m3u8, pulsa Load playlist y después Play 1. La lista contiene Morning Light (50 segundos) y Window Seat (70 segundos). Usa los controles de audio para pausar o cambiar la posición. Selecciona example-90.m3u8, cárgala y reproduce su única pista. La pausa más corta conserva la primera grabación completa; no recorta la segunda.

Cambia a Terminal. Si aparecen caracteres extraños o un comando incompleto en el indicador, pulsa Ctrl+C para cancelar esa entrada. cd cambia de directorio. Esta ruta absoluta también permite recuperarte de un error normal de directorio:

cd /home/labex/project/short-break

cat muestra un archivo de texto. sample.tsv es un catálogo de dos pistas; TSV significa valores separados por tabuladores. Sus columnas son ID, título, segundos enteros, preferencia y ruta de audio. Las preferencias más altas se consideran primero. Un ID identifica una grabación; los títulos pueden repetirse.

cat sample.tsv

playlist.hpp declara el objeto Track y las interfaces de las dos funciones. Cada Track agrupa los cinco campos. Un vector<Track> guarda varios objetos en orden. Lee esta breve definición:

cat playlist.hpp

Compila el programa inicial. g++ compila C++; -std=c++17 selecciona C++17, -Wall -Wextra solicita advertencias útiles y -o playlist nombra el ejecutable. Los tres archivos .cpp se compilan juntos. Es normal recibir una advertencia sobre el presupuesto sin usar del programa inicial.

g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist

./playlist ejecuta el programa del directorio actual. Sus argumentos son el catálogo, un presupuesto de segundos enteros entre 1 y 86400, ambos incluidos, y la ruta de salida. El analizador preparado aplica ese mismo límite a las duraciones y preferencias del catálogo; no tienes que implementarlo. Ejecuta la versión incompleta con el ejemplo de 90 segundos:

./playlist sample.tsv 90 outputs/sample-90.m3u8

Imprime ambas grabaciones y Total: 120s / 90s. Ese es el fallo que corregirás. El archivo de salida inicial solo contiene títulos; si lo cargas ahora en Player, aparecerá un error por la cabecera #EXTM3U ausente. Es un fallo de formato esperado, distinto de un archivo de audio ausente. El paso 3 enseñará el formato de exportación.

Ahora haz que chooseTracks utilice el presupuesto restante. Pasar tracks por valor crea una copia local; la futura ordenación no cambiará el catálogo. const auto& track lee cada objeto existente sin copiarlo ni modificarlo. push_back añade un objeto seleccionado. <= incluye una pista que cabe exactamente. Después, -= resta su duración. Una pista que no cabe se omite y el bucle continúa.

Reemplaza el archivo corto con la función completa siguiente. > sustituye este archivo fuente. El documento aquí incluido, <<'CPP', envía a cat todas las líneas hasta el CPP final; el delimitador entre comillas conserva literalmente los caracteres de C++. Pega el bloque entero, incluido el delimitador final en su propia línea.

cat > selection.cpp <<'CPP'
#include "playlist.hpp"
#include <algorithm>
std::vector<Track> chooseTracks(std::vector<Track> tracks, int budget) {
    std::vector<Track> selected;
    for (const auto& track : tracks) {
        if (track.seconds <= budget) {
            selected.push_back(track);
            budget -= track.seconds;
        }
    }
    return selected;
}
CPP

Vuelve a compilar después de editar el código y ejecuta el mismo ejemplo:

g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist sample.tsv 90 outputs/sample-90.m3u8

Ahora solo aparece t01 | Morning Light | 50s, con Total: 50s / 90s. Quedan cuarenta segundos: la grabación de 70 no cabe. Esta versión respeta el orden de entrada. A continuación, independizarás el orden de la disposición de las filas del catálogo.

Si un error de compilación señala una línea, abre selection.cpp en el editor o repite el bloque de sustitución completo y vuelve a compilar. Ejecutar un binario antiguo después de una compilación fallida no comprueba tu modificación.

Aplicar un orden de preferencia predecible

En este paso, ordenarás toda la biblioteca antes de elegir pistas. Empieza en Terminal, dentro de /home/labex/project/short-break. Examina el catálogo completo:

cat catalog.tsv

Las filas están desordenadas a propósito. Considera las preferencias de mayor a menor y, en caso de empate, el ID de pista en orden alfabético ascendente. Así, t02 precede a t03, aunque sea más larga. Son decisiones sencillas de preferencia, no una búsqueda de la mayor duración total posible.

Con un presupuesto de 90 segundos, t01 utiliza 50. Ni t02 (95) ni t03 (70) caben en los 40 restantes. Continúa hasta t04 (30), que deja 10. t05 (20) no cabe. El resultado es t01, t04, con 80 segundos en total. Detenerse en la primera pista demasiado larga perdería una elección posterior válida.

std::sort utiliza el intervalo semiabierto de begin() a end(). La comparación devuelve true cuando a debe preceder a b. [] introduce una pequeña función anónima, llamada lambda; sus dos parámetros const Track& leen los objetos comparados. La prueba != decide la comparación principal y el return final resuelve un empate por ID. Reemplaza selection.cpp con la versión completa:

cat > selection.cpp <<'CPP'
#include "playlist.hpp"
#include <algorithm>
std::vector<Track> chooseTracks(std::vector<Track> tracks, int budget) {
    std::sort(tracks.begin(), tracks.end(),
        [](const Track& a, const Track& b) {
            if (a.preference != b.preference)
                return a.preference > b.preference;
            return a.id < b.id;
        });
    std::vector<Track> selected;
    for (const auto& track : tracks) {
        if (track.seconds <= budget) {
            selected.push_back(track);
            budget -= track.seconds;
        }
    }
    return selected;
}
CPP

Recompila y ejecuta ambos presupuestos:

g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist catalog.tsv 90 outputs/break-90.m3u8
./playlist catalog.tsv 120 outputs/break-120.m3u8

El resultado de 90 segundos es t01, t04, con un total de 80. El de 120 es t01, t03, exactamente 120: se omite t02 y la pista de 70 cabe en los 70 restantes. Ambas selecciones utilizan grabaciones enteras. La salida sigue siendo una lista de títulos hasta el siguiente paso.

Exportar una lista que puedas reproducir

En este paso, guardarás los objetos elegidos como M3U extendido en UTF-8. Empieza en Terminal. Un archivo .m3u8 es una lista de texto, no el audio. La primera línea es #EXTM3U. Para cada pista seleccionada, se escribe una línea de metadatos #EXTINF:seconds,title, seguida de su ruta de audio.

Una ruta relativa se resuelve desde el directorio de la lista, no desde el del catálogo ni el directorio actual del proceso. Guarda las listas generadas directamente en outputs/; los originales están en outputs/audio/. Por tanto, una entrada como audio/Morning Light.wav llega a la grabación situada junto a esa lista. Conserva esta relación de carpetas al descargar o compartir. Se admiten espacios y Unicode; mantén las rutas proporcionadas, sin saltos de línea, # inicial, rutas absolutas, acceso al directorio padre ni instrucciones adicionales de lista.

std::ofstream abre un archivo de salida. << escribe texto o números; \n escribe un salto de línea. La función recibe el vector seleccionado y el nombre de salida mediante referencias const, sin copiarlos ni cambiarlos. Los fallos de archivo lanzan una excepción que comunica el programa principal preparado. El analizador valida el presupuesto entero positivo, el catálogo y todo el audio referenciado antes de que esta función abra la salida, conservando cualquier salida existente ante esos fallos de entrada.

Reemplaza export.cpp:

cat > export.cpp <<'CPP'
#include "playlist.hpp"
#include <fstream>
#include <stdexcept>
void writePlaylist(const std::vector<Track>& tracks, const std::string& output) {
    std::ofstream file(output);
    if (!file) throw std::runtime_error("Cannot write playlist.");
    file << "#EXTM3U\n";
    for (const auto& track : tracks) {
        file << "#EXTINF:" << track.seconds << ',' << track.title << '\n';
        file << track.path << '\n';
    }
    if (!file) throw std::runtime_error("Playlist write failed.");
}
CPP

Recompila y vuelve a crear las dos listas con el exportador actualizado:

g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist catalog.tsv 90 outputs/break-90.m3u8
./playlist catalog.tsv 120 outputs/break-120.m3u8

Examina el archivo guardado de 90 segundos:

cat outputs/break-90.m3u8

El archivo UTF-8 guardado contiene:

#EXTM3U
#EXTINF:50,Morning Light
audio/Morning Light.wav
#EXTINF:30,夜の散歩
audio/夜の散歩.wav

El título y el nombre japonés siguen siendo texto UTF-8. Algunas terminales muestran caracteres de sustitución aunque los bytes guardados estén intactos. No renombres los originales para corregir la visualización; Player muestra el título y la ruta originales. Las líneas de duración describen los archivos enteros: cambiar aquí un número no acorta el audio.

Cambia a Player. Pulsa Refresh files, selecciona break-90.m3u8 y pulsa Load playlist. Deben aparecer dos pistas y 80 segundos declarados. Pulsa Play 1, cambia la posición con los controles y pausa; después pulsa Play 2 para escuchar la segunda grabación. El navegador lee tu M3U8 guardado y exactamente sus rutas. Una fuente ausente produce Cannot play, no una pista de reemplazo. Usa Download playlist y Download current track para guardar los archivos reales; la lista por sí sola no contiene el audio.

La lista guardada de la pausa en la sala de escucha

Observa las dos filas y sus duraciones, y los controles inferiores. Después de comprobar la reproducción, vuelve a Terminal.

Reutilizar el programa e interpretar un rechazo

En este paso, distinguirás un resultado corto válido de una entrada incorrecta. Empieza en Terminal. Un presupuesto positivo menor que todas las pistas es válido: no cabe ninguna grabación completa. Genera una lista de un segundo:

./playlist catalog.tsv 1 outputs/tiny.m3u8

Comunica Total: 0s / 1s. El archivo solo contiene #EXTM3U. Al cargarlo en Player deben aparecer cero pistas. Es un resultado vacío correcto, no un fallo de conexión o análisis.

Un presupuesto como 90s es inválido porque la CLI espera dígitos que expresen segundos enteros. El siguiente comando termina deliberadamente con estado 1 y no debe reemplazar tu lista existente de 90 segundos. echo $? muestra inmediatamente el estado de salida del comando anterior (0 significa éxito). Ejecuta estos dos comandos consecutivamente:

./playlist catalog.tsv 90s outputs/break-90.m3u8
echo $?

El error indica que el presupuesto y las duraciones deben ser segundos enteros positivos, seguido del estado 1. No reinicies el entorno: elimina el sufijo y utiliza 90 al reintentar. Si escribes mal una ruta de entrada, Cannot open catalog señala el argumento del catálogo. Recupérate con el cd absoluto del paso 1 y el nombre real catalog.tsv. Missing audio significa que una ruta del catálogo no llega a un archivo; conserva los originales preparados y comprueba la ruta antes de reintentar.

Confirma que el archivo correcto existente sigue conteniendo los dos pares adecuados de metadatos y ruta:

cat outputs/break-90.m3u8

Ya puedes utilizar el mismo programa con distintos presupuestos positivos válidos, escribiendo siempre directamente en outputs/. Ordena por preferencia e ID, omite candidatos demasiado largos y nunca recorta una grabación. La salida de altavoces físicos y el comportamiento de todos los reproductores externos quedan fuera del ejercicio; el navegador proporcionado decodifica estos archivos WAV reales.

Resumen

Has utilizado objetos C++ y un vector para ordenar grabaciones, seleccionar pistas completas dentro de un presupuesto restante y escribir archivos M3U8 UTF-8. Reprodujiste la salida real, comprendiste las rutas relativas y distinguiste una lista vacía válida de una entrada inválida. El siguiente desafío cambia la biblioteca, pero mantiene estas reglas.