feat: add lyricResolve for resole .lrc file
feat: add backgroundDisplay for play ascii video in terminal feat: add musicPlayer for play music in terminal
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// musicPlayer.cpp
|
||||
//
|
||||
// miniaudio-backed playback of mp3 / wav / flac files. See musicPlayer.h.
|
||||
//
|
||||
// MINIAUDIO_IMPLEMENTATION is defined in this one translation unit so the
|
||||
// large miniaudio implementation is compiled exactly once per binary.
|
||||
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
|
||||
#include "musicPlayer.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
// When built with ALSA support, silence ALSA's own error printing: with no
|
||||
// sound card present it dumps a lot of "cannot find card '0'" chatter to
|
||||
// stderr, which would corrupt the TUI during playback.
|
||||
#ifdef HMS_HAS_ALSA
|
||||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
|
||||
namespace mp {
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef HMS_HAS_ALSA
|
||||
void alsaErrorHandler(const char*, int, const char*, int, const char*, ...) {
|
||||
// swallow ALSA's verbose configuration/device errors
|
||||
}
|
||||
#endif
|
||||
|
||||
struct PlaybackUser {
|
||||
ma_decoder* decoder;
|
||||
const std::atomic<bool>* stop;
|
||||
std::atomic<bool> ended{false};
|
||||
};
|
||||
|
||||
void dataCallback(ma_device* device, void* out, const void* /*in*/,
|
||||
ma_uint32 frameCount) {
|
||||
auto* user = static_cast<PlaybackUser*>(device->pUserData);
|
||||
ma_decoder* decoder = user->decoder;
|
||||
const size_t bytesPerFrame =
|
||||
ma_get_bytes_per_frame(decoder->outputFormat, decoder->outputChannels);
|
||||
|
||||
if (user->stop && user->stop->load()) {
|
||||
// Stop requested: feed silence and let the main loop tear the device
|
||||
// down.
|
||||
memset(out, 0, static_cast<size_t>(frameCount) * bytesPerFrame);
|
||||
user->ended.store(true);
|
||||
return;
|
||||
}
|
||||
|
||||
ma_uint64 framesRead = 0;
|
||||
const ma_result result =
|
||||
ma_decoder_read_pcm_frames(decoder, out, frameCount, &framesRead);
|
||||
|
||||
if (framesRead > 0 && framesRead < frameCount) {
|
||||
// Underrun / near EOF: zero-pad the rest of the buffer.
|
||||
memset(static_cast<char*>(out) + framesRead * bytesPerFrame, 0,
|
||||
static_cast<size_t>(frameCount - framesRead) * bytesPerFrame);
|
||||
} else if (framesRead == 0) {
|
||||
memset(out, 0, static_cast<size_t>(frameCount) * bytesPerFrame);
|
||||
}
|
||||
|
||||
// A short read means the file is exhausted (MA_AT_END); any other
|
||||
// non-success result is a decode error. Both end the playback loop.
|
||||
if (framesRead < frameCount || (result != MA_SUCCESS && result != MA_AT_END)) {
|
||||
user->ended.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int play(const std::string& path, std::atomic<bool>* stop) {
|
||||
if (path.empty()) return 1;
|
||||
|
||||
ma_decoder_config decoderConfig = ma_decoder_config_init_default();
|
||||
ma_decoder decoder;
|
||||
if (ma_decoder_init_file(path.c_str(), &decoderConfig, &decoder) != MA_SUCCESS) {
|
||||
return 2; // cannot open / no decoder for the format
|
||||
}
|
||||
|
||||
#ifdef HMS_HAS_ALSA
|
||||
snd_lib_error_set_handler(alsaErrorHandler);
|
||||
#endif
|
||||
|
||||
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||
deviceConfig.playback.format = decoder.outputFormat;
|
||||
deviceConfig.playback.channels = decoder.outputChannels;
|
||||
deviceConfig.sampleRate = decoder.outputSampleRate;
|
||||
deviceConfig.dataCallback = dataCallback;
|
||||
PlaybackUser user{&decoder, stop};
|
||||
deviceConfig.pUserData = &user;
|
||||
|
||||
ma_device device;
|
||||
if (ma_device_init(nullptr, &deviceConfig, &device) != MA_SUCCESS) {
|
||||
ma_decoder_uninit(&decoder);
|
||||
return 3; // no audio backend / device available
|
||||
}
|
||||
|
||||
ma_device_start(&device);
|
||||
while (!user.ended.load() && !(stop && stop->load())) {
|
||||
ma_sleep(50);
|
||||
}
|
||||
ma_device_uninit(&device);
|
||||
ma_decoder_uninit(&decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
Reference in New Issue
Block a user