Introdução
Uma pausa tem limite de tempo. Sua biblioteca musical contém gravações completas com uma pontuação de preferência, duração em segundos e um ID estável de faixa. Você criará um programa C++ reutilizável que considera as faixas em uma ordem declarada, mantém cada gravação que cabe e salva uma lista que pode ser reproduzida de verdade.
Você precisa saber navegar no terminal e conhecer strings, classes, vector, fluxos de arquivos e exceções em C++. Apresentaremos as referências const, a função de comparação e os caminhos relativos usados aqui. O analisador, o áudio sintético original e o reprodutor no navegador estão preparados. Você escreverá seleção e exportação, não um conversor de áudio nem um algoritmo de preenchimento ótimo.
Use a aba Terminal para os comandos e Player para ouvir. Ambas usam o mesmo ambiente. Edite somente selection.cpp e export.cpp e crie listas diretamente em outputs/. Preserve o catálogo, a estrutura fornecida e os originais de outputs/audio/. Estas composições sintéticas originais não utilizam amostras de áudio externas.
Encaixar faixas completas na pausa
Nesta etapa, você ouvirá primeiro um exemplo normal e depois usará um contador de tempo restante para escolher gravações completas. Comece em Player. Selecione example-120.m3u8, clique em Load playlist e depois em Play 1. A lista contém Morning Light (50 segundos) e Window Seat (70 segundos). Use os controles de áudio para pausar ou mudar a posição. Selecione então example-90.m3u8, carregue a lista e reproduza sua única faixa. A pausa mais curta mantém a primeira gravação inteira; não corta a segunda.
Mude para Terminal. Se houver caracteres estranhos ou um comando incompleto no prompt, pressione Ctrl+C para cancelar essa entrada. cd muda o diretório. Este caminho absoluto também ajuda a corrigir um erro comum de diretório de trabalho:
cd /home/labex/project/short-break
cat mostra um arquivo de texto. sample.tsv é um catálogo com duas faixas; TSV significa valores separados por tabulações. As colunas são ID, título, segundos inteiros, preferência e caminho do áudio. Preferências maiores são consideradas primeiro. Um ID identifica uma gravação; títulos podem se repetir.
cat sample.tsv
playlist.hpp declara o objeto Track e as interfaces das duas funções. Cada Track reúne os cinco campos. Um vector<Track> armazena vários objetos em ordem. Leia a definição curta:
cat playlist.hpp
Compile o programa inicial. g++ compila C++; -std=c++17 escolhe C++17, -Wall -Wextra ativa avisos úteis e -o playlist define o nome do executável. Os três arquivos .cpp são compilados juntos. O aviso de que o orçamento não é usado no programa inicial é esperado.
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist executa o programa do diretório atual. Os argumentos são catálogo, orçamento em segundos inteiros entre 1 e 86400, inclusive, e caminho de saída. O analisador preparado aplica os mesmos limites às durações e preferências do catálogo; você não precisa implementá-lo. Execute a versão incompleta com o exemplo de 90 segundos:
./playlist sample.tsv 90 outputs/sample-90.m3u8
Ela imprime as duas gravações e Total: 120s / 90s. Esse é o defeito que você corrigirá. O arquivo inicial contém apenas títulos; carregá-lo agora em Player informa que falta o cabeçalho #EXTM3U. É uma falha de formato esperada, diferente de um arquivo de áudio ausente. A etapa 3 ensinará o formato de exportação.
Agora faça chooseTracks usar o orçamento restante. Passar tracks por valor cria uma cópia local, de modo que a ordenação posterior não muda o catálogo. const auto& track lê cada objeto existente sem copiá-lo nem alterá-lo. push_back acrescenta um objeto selecionado ao resultado. <= inclui uma faixa que cabe exatamente. Em seguida, -= subtrai sua duração. Uma faixa que não cabe é ignorada e o laço continua.
Substitua o arquivo curto pela função completa abaixo. > substitui esse arquivo-fonte. O here-document <<'CPP' envia ao cat todas as linhas até o CPP final; as aspas do delimitador preservam os caracteres C++ literalmente. Cole o bloco inteiro, inclusive o delimitador final sozinho em sua linha.
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
Recompile após editar o código e execute o mesmo exemplo novamente:
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist sample.tsv 90 outputs/sample-90.m3u8
Agora aparecem somente t01 | Morning Light | 50s e Total: 50s / 90s. Restam quarenta segundos; a gravação de 70 não cabe. Esta versão segue a ordem de entrada. A seguir, você tornará a ordem independente da disposição das linhas do catálogo.
Se um erro de compilação indicar uma linha, abra selection.cpp no editor ou repita o bloco completo de substituição e recompile. Executar um binário antigo após uma compilação malsucedida não verifica sua alteração.
Aplicar uma ordem de preferência previsível
Nesta etapa, você ordenará toda a biblioteca antes de escolher faixas. Comece em Terminal, em /home/labex/project/short-break. Examine o catálogo completo:
cat catalog.tsv
As linhas estão embaralhadas de propósito. Considere a preferência da maior para a menor e, em caso de empate, o ID da faixa em ordem alfabética crescente. Assim, t02 vem antes de t03, mesmo sendo mais longa. São decisões simples de preferência, não uma busca pela maior duração total possível.
Com um orçamento de 90 segundos, t01 utiliza 50. Nem t02 (95) nem t03 (70) cabem nos 40 restantes. Continue até t04 (30), que deixa 10. t05 (20) não cabe. O resultado é t01, t04, totalizando 80 segundos. Parar na primeira faixa longa demais faria perder uma escolha posterior válida.
std::sort usa o intervalo semiaberto de begin() a end(). A comparação retorna true quando a deve vir antes de b. [] introduz uma pequena função anônima, chamada lambda; seus dois parâmetros const Track& leem os objetos comparados. O teste != decide a comparação principal e o return final desempata pelo ID. Substitua selection.cpp pela versão 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
Recompile e execute os dois orçamentos:
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
O resultado para 90 segundos é t01, t04, com total de 80. Para 120, é t01, t03, exatamente 120: t02 é ignorada e a faixa de 70 cabe nos 70 restantes. As duas escolhas usam gravações inteiras. A saída continua sendo uma lista de títulos até a próxima etapa.
Exportar uma lista que você pode reproduzir
Nesta etapa, você salvará os objetos escolhidos como M3U estendido em UTF-8. Comece em Terminal. Um arquivo .m3u8 é uma lista de texto, não o áudio em si. A primeira linha é #EXTM3U. Para cada faixa escolhida, há uma linha de metadados #EXTINF:seconds,title, seguida do caminho do áudio.
Um caminho relativo parte do diretório da lista, não do diretório do catálogo nem do diretório atual do processo. Salve listas geradas diretamente em outputs/; os originais estão em outputs/audio/. Assim, uma entrada como audio/Morning Light.wav alcança a gravação ao lado da lista. Preserve essa relação entre pastas ao baixar ou compartilhar. Espaços e Unicode são aceitos; mantenha os caminhos fornecidos, sem quebras de linha, # inicial, caminhos absolutos, acesso à pasta superior ou instruções adicionais de lista.
std::ofstream abre um arquivo de saída. << escreve texto ou números; \n escreve uma quebra de linha. A função recebe o vector selecionado e o nome da saída por referências const, sem copiá-los nem alterá-los. Falhas de arquivo lançam uma exceção informada pelo programa principal preparado. O analisador valida o orçamento inteiro positivo, o catálogo e todo o áudio referenciado antes que esta função abra a saída, preservando uma saída existente nessas falhas de entrada.
Substitua 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
Recompile e gere novamente as duas listas com o exportador atualizado:
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
Examine o arquivo salvo para 90 segundos:
cat outputs/break-90.m3u8
O arquivo UTF-8 salvo contém:
#EXTM3U
#EXTINF:50,Morning Light
audio/Morning Light.wav
#EXTINF:30,夜の散歩
audio/夜の散歩.wav
O título e o nome de arquivo japoneses continuam como texto UTF-8. Alguns terminais mostram caracteres substitutos, embora os bytes salvos estejam intactos. Não renomeie os originais para corrigir a exibição; Player mostra o título e o caminho originais. As linhas de duração descrevem os arquivos completos: mudar um número aqui não encurta o áudio.
Mude para Player. Clique em Refresh files, selecione break-90.m3u8 e clique em Load playlist. Devem aparecer duas faixas e 80 segundos declarados. Clique em Play 1, mude a posição e pause pelos controles de áudio, depois clique em Play 2 para ouvir a segunda gravação. O navegador lê seu M3U8 salvo e exatamente os caminhos nele indicados. Uma fonte ausente gera Cannot play, sem substituir a faixa. Use Download playlist e Download current track para salvar os arquivos reais; a lista sozinha não contém o áudio.

Observe as duas linhas, suas durações e os controles de áudio abaixo. Depois de verificar a reprodução, volte a Terminal.
Reutilizar o programa e entender uma rejeição
Nesta etapa, você distinguirá um resultado curto válido de uma entrada inválida. Comece em Terminal. Um orçamento positivo menor que todas as faixas é válido: nenhuma gravação completa cabe. Gere uma lista para um segundo:
./playlist catalog.tsv 1 outputs/tiny.m3u8
O programa informa Total: 0s / 1s. O arquivo contém apenas #EXTM3U. Ao carregá-lo em Player, devem aparecer zero faixas. Esse é um resultado vazio correto, não uma falha de conexão ou análise.
Um orçamento como 90s é inválido porque a CLI espera dígitos para segundos inteiros. O próximo comando termina intencionalmente com status 1 e não deve substituir sua lista existente de 90 segundos. echo $? mostra imediatamente o status de saída do comando anterior (0 significa sucesso). Execute estes dois comandos em sequência:
./playlist catalog.tsv 90s outputs/break-90.m3u8
echo $?
O erro explica que orçamento e durações precisam ser segundos inteiros positivos, seguido do status 1. Não reinicie o ambiente: remova o sufixo e tente novamente com 90. Se digitar errado um caminho de entrada, Cannot open catalog aponta para o argumento do catálogo. Recupere-se com o cd absoluto da etapa 1 e o nome real catalog.tsv. Missing audio significa que um caminho do catálogo não alcança um arquivo; preserve os originais preparados e verifique o caminho antes de tentar outra vez.
Confirme que o arquivo correto existente ainda contém os dois pares adequados de metadados e caminho:
cat outputs/break-90.m3u8
Agora você pode usar o mesmo programa para diferentes orçamentos positivos válidos, sempre escrevendo diretamente em outputs/. Ele ordena por preferência e ID, ignora candidatos longos demais e nunca corta uma gravação. Saída em alto-falantes físicos e o comportamento de todos os reprodutores externos estão fora deste exercício; o navegador fornecido decodifica estes arquivos WAV reais.
Resumo
Você usou objetos C++ e um vector para ordenar gravações, escolher faixas completas dentro de um orçamento restante e escrever arquivos M3U8 UTF-8. Reproduziu a saída real, compreendeu caminhos relativos e distinguiu uma lista vazia válida de uma entrada inválida. O próximo desafio muda a biblioteca, mantendo essas regras.



