Codebase of 0.9.5 26.06.2024 found on my OneDrive
This commit is contained in:
133
source/music/Mp3.cpp
Normal file
133
source/music/Mp3.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include <renderd7/music/Mp3.hpp>
|
||||
|
||||
#ifdef RENDERD7_MUSICDEC
|
||||
// MP3 ID3V1
|
||||
std::string rd7i_id3v1_to_string(const std::string& in) {
|
||||
std::string ret = "";
|
||||
if(in.length() < 31) ret = in;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// For MP3 ID3V2 tags
|
||||
// Split up a number of lines separated by \n, \r, both or just zero byte
|
||||
// and print out each line with specified prefix.
|
||||
std::string rd7i_id3v2_to_string(const std::string& prefix, mpg123_string* inlines) {
|
||||
size_t i;
|
||||
int hadcr = 0, hadlf = 0;
|
||||
std::string lines;
|
||||
std::string line;
|
||||
size_t len = 0;
|
||||
|
||||
if(inlines == nullptr) return "";
|
||||
|
||||
if (inlines->fill > 0) {
|
||||
lines = inlines->p;
|
||||
len = inlines->fill;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
line = lines;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (lines[i] == '\n' || lines[i] == '\r' || lines[i] == 0) {
|
||||
char save = lines[i];
|
||||
if (save == '\n') {
|
||||
++hadlf;
|
||||
}
|
||||
if (save == '\r') {
|
||||
++hadcr;
|
||||
}
|
||||
if ((hadcr || hadlf) && (hadlf % 2 == 0) && (hadcr % 2 == 0)) {
|
||||
line = "";
|
||||
}
|
||||
|
||||
if (!line.empty()) {
|
||||
lines[i] = 0;
|
||||
std::string result;
|
||||
result = prefix + line;
|
||||
line = "";
|
||||
lines[i] = save;
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
hadlf = hadcr = 0;
|
||||
if (line.empty()) {
|
||||
line = lines.substr(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
namespace RenderD7 {
|
||||
int Mp3Decoder::_init(const std::string &path, MusicMeta& meta) {
|
||||
int ret = 0;
|
||||
int encoding = 0;
|
||||
if ((ret = mpg123_init() != MPG123_OK)) return ret;
|
||||
if ((hnd = mpg123_new(NULL, &ret)) == NULL) {
|
||||
printf("[MPG123]: ERR -> %s\n", mpg123_plain_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
if (mpg123_open(hnd, path.c_str()) != MPG123_OK ||
|
||||
mpg123_getformat(hnd, &rate, &channels, &encoding) != MPG123_OK) {
|
||||
printf("[MPG123]: ERR -> %s\n", mpg123_strerror(hnd));
|
||||
return -1;
|
||||
}
|
||||
|
||||
mpg123_id3v1* v1;
|
||||
mpg123_id3v2* v2;
|
||||
mpg123_seek(hnd, 0, SEEK_SET);
|
||||
if(mpg123_meta_check(hnd) & MPG123_ID3 && mpg123_id3(hnd, &v1, &v2) == MPG123_OK) {
|
||||
if(v1 != nullptr) {
|
||||
///// POSSIBLE CRASH /////
|
||||
//meta.title(rd7i_id3v1_to_string(v1->title));
|
||||
//meta.album(rd7i_id3v1_to_string(v1->album));
|
||||
//meta.artist(rd7i_id3v1_to_string(v1->artist));
|
||||
//meta.year(rd7i_id3v1_to_string(v1->year));
|
||||
meta.mdt("mpg123_id3v1");
|
||||
}
|
||||
if(v2 != nullptr) {
|
||||
meta.title(rd7i_id3v2_to_string("", v2->title));
|
||||
meta.album(rd7i_id3v2_to_string("", v2->album));
|
||||
meta.artist(rd7i_id3v2_to_string("", v2->artist));
|
||||
meta.year(rd7i_id3v2_to_string("", v2->year));
|
||||
meta.mdt("mpg123_id3v2");
|
||||
}
|
||||
}
|
||||
|
||||
mpg123_format_none(hnd);
|
||||
mpg123_format(hnd, rate, channels, encoding);
|
||||
buf_size = mpg123_outblock(hnd) * 16;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int Mp3Decoder::_getSampleRate() { return rate; }
|
||||
|
||||
unsigned char Mp3Decoder::_getChannels(){return channels; }
|
||||
|
||||
size_t Mp3Decoder::_getBufSize() {
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
unsigned long long Mp3Decoder::_decode(signed short*buf_addr) {
|
||||
size_t done = 0;
|
||||
mpg123_read(hnd, buf_addr, buf_size, &done);
|
||||
return done / (sizeof(int16_t));
|
||||
}
|
||||
|
||||
void Mp3Decoder::_deinit() {
|
||||
mpg123_close(hnd);
|
||||
mpg123_delete(hnd);
|
||||
mpg123_exit();
|
||||
}
|
||||
|
||||
size_t Mp3Decoder::_getFileSamples() {
|
||||
off_t len = mpg123_length(hnd);
|
||||
if (len != MPG123_ERR) return len * (size_t)channels;
|
||||
return -1; // Not Exist
|
||||
}
|
||||
|
||||
} // namespace RenderD7
|
||||
|
||||
#endif
|
279
source/music/Music.cpp
Normal file
279
source/music/Music.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
#include <3ds.h>
|
||||
|
||||
#ifdef RENDERD7_MUSICDEC
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <renderd7/Message.hpp>
|
||||
#include <renderd7/music/Mp3.hpp>
|
||||
#include <renderd7/music/Music.hpp>
|
||||
#include <renderd7/music/Vorbis.hpp>
|
||||
#include <renderd7/thread.hpp>
|
||||
|
||||
extern bool isndspinit;
|
||||
|
||||
struct PlayerData {
|
||||
RenderD7::MusicMeta meta;
|
||||
size_t samples_total = 0;
|
||||
size_t samples_played = 0;
|
||||
size_t samples_per_sec = 0;
|
||||
|
||||
// Song Info
|
||||
std::string file = "";
|
||||
|
||||
signed short *buf1 = NULL;
|
||||
signed short *buf2 = NULL;
|
||||
ndspWaveBuf waveBuf[2] = {0};
|
||||
bool last_buf = false;
|
||||
int ret = -1;
|
||||
bool done = false;
|
||||
bool playing = false;
|
||||
|
||||
bool stop = false;
|
||||
};
|
||||
|
||||
int rd7i_player_channel = 8;
|
||||
PlayerData rd7i_mp_internal_data;
|
||||
RenderD7::Thread rd7i_mph;
|
||||
|
||||
void rd7i_player_init() {
|
||||
rd7i_mp_internal_data.samples_total = 0;
|
||||
rd7i_mp_internal_data.samples_played = 0;
|
||||
rd7i_mp_internal_data.samples_per_sec = 0;
|
||||
rd7i_mp_internal_data.buf1 = NULL;
|
||||
rd7i_mp_internal_data.buf2 = NULL;
|
||||
rd7i_mp_internal_data.last_buf = false;
|
||||
rd7i_mp_internal_data.ret = -1;
|
||||
rd7i_mp_internal_data.stop = false;
|
||||
rd7i_mp_internal_data.done = false;
|
||||
rd7i_mp_internal_data.playing = true;
|
||||
rd7i_mp_internal_data.file = "";
|
||||
}
|
||||
|
||||
// Non Public Check funcs
|
||||
extern int rd7i_check_vorbis(const std::string &path);
|
||||
extern int rd7i_check_flac(const std::string &path);
|
||||
|
||||
namespace RenderD7 {
|
||||
MusicDecoder *MusicDecoder::decoder = nullptr;
|
||||
|
||||
void MusicDecoder::LoadFile(const std::string &path) {
|
||||
rd7i_mp_internal_data.meta = MusicMeta(); // AutoClean
|
||||
std::fstream f(path, std::ios::in | std::ios::binary);
|
||||
if (!f.is_open()) return;
|
||||
if (!f.good()) return;
|
||||
// File Signature
|
||||
unsigned int sig;
|
||||
f.read(reinterpret_cast<char *>(&sig), sizeof(unsigned int));
|
||||
if (f.tellg() != 4) {
|
||||
f.close();
|
||||
return;
|
||||
}
|
||||
f.close();
|
||||
switch (sig) {
|
||||
// "OGG Collection"
|
||||
case 0x5367674F:
|
||||
if (rd7i_check_vorbis(path) == 0) {
|
||||
decoder = new VorbisDecoder();
|
||||
if (init(path, rd7i_mp_internal_data.meta)) {
|
||||
MusicDecoder::CleanUp();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
return; // Err
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((sig << 16) == 0xFBFF0000 || (sig << 16) == 0xFAFF0000 ||
|
||||
(sig << 8) == 0x33444900) {
|
||||
decoder = new Mp3Decoder();
|
||||
if (init(path, rd7i_mp_internal_data.meta)) {
|
||||
RenderD7::PushMessage(
|
||||
RenderD7::Message("Music Player", "Failed to load MP3"));
|
||||
MusicDecoder::CleanUp();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MusicDecoder::CleanUp() {
|
||||
if (decoder != nullptr) delete decoder;
|
||||
decoder = nullptr;
|
||||
}
|
||||
|
||||
static void rd7i_player_cleaner() {
|
||||
MusicDecoder::deinit();
|
||||
MusicDecoder::CleanUp();
|
||||
ndspChnWaveBufClear(rd7i_player_channel);
|
||||
linearFree(rd7i_mp_internal_data.buf1);
|
||||
linearFree(rd7i_mp_internal_data.buf2);
|
||||
rd7i_mp_internal_data.ret = -1;
|
||||
// Also clean ret data
|
||||
rd7i_mp_internal_data.samples_per_sec = 0;
|
||||
rd7i_mp_internal_data.samples_total = 0;
|
||||
rd7i_mp_internal_data.samples_played = 0;
|
||||
rd7i_mp_internal_data.file = "";
|
||||
}
|
||||
|
||||
static void rd7i_mplayer(RenderD7::Parameter param) {
|
||||
MusicDecoder::LoadFile(param.get<std::string>());
|
||||
if (MusicDecoder::decoder == nullptr) return;
|
||||
|
||||
rd7i_mp_internal_data.samples_total = 0;
|
||||
rd7i_mp_internal_data.samples_played = 0;
|
||||
rd7i_mp_internal_data.samples_per_sec = 0;
|
||||
rd7i_mp_internal_data.buf1 = NULL;
|
||||
rd7i_mp_internal_data.buf2 = NULL;
|
||||
rd7i_mp_internal_data.last_buf = false;
|
||||
rd7i_mp_internal_data.ret = -1;
|
||||
rd7i_mp_internal_data.stop = false;
|
||||
rd7i_mp_internal_data.done = false;
|
||||
rd7i_mp_internal_data.playing = true;
|
||||
rd7i_mp_internal_data.file = "";
|
||||
|
||||
if (MusicDecoder::getFileSamples() != (size_t)-1)
|
||||
rd7i_mp_internal_data.samples_total = MusicDecoder::getFileSamples();
|
||||
|
||||
rd7i_mp_internal_data.samples_per_sec =
|
||||
MusicDecoder::getSampleRate() * MusicDecoder::getChannels();
|
||||
rd7i_mp_internal_data.buf1 = (signed short *)linearAlloc(
|
||||
MusicDecoder::getBufSize() * sizeof(signed short));
|
||||
rd7i_mp_internal_data.buf2 = (signed short *)linearAlloc(
|
||||
MusicDecoder::getBufSize() * sizeof(signed short));
|
||||
rd7i_mp_internal_data.file = param.get<std::string>();
|
||||
|
||||
ndspChnReset(rd7i_player_channel);
|
||||
ndspChnWaveBufClear(rd7i_player_channel);
|
||||
ndspSetOutputMode(NDSP_OUTPUT_STEREO);
|
||||
ndspChnSetInterp(rd7i_player_channel, NDSP_INTERP_POLYPHASE);
|
||||
ndspChnSetRate(rd7i_player_channel, MusicDecoder::getSampleRate());
|
||||
ndspChnSetFormat(rd7i_player_channel, MusicDecoder::getChannels() == 2
|
||||
? NDSP_FORMAT_STEREO_PCM16
|
||||
: NDSP_FORMAT_MONO_PCM16);
|
||||
|
||||
memset(rd7i_mp_internal_data.waveBuf, 0,
|
||||
sizeof(rd7i_mp_internal_data.waveBuf));
|
||||
rd7i_mp_internal_data.waveBuf[0].nsamples =
|
||||
MusicDecoder::decode(&rd7i_mp_internal_data.buf1[0]) /
|
||||
MusicDecoder::getChannels();
|
||||
rd7i_mp_internal_data.waveBuf[0].data_vaddr = &rd7i_mp_internal_data.buf1[0];
|
||||
ndspChnWaveBufAdd(rd7i_player_channel, &rd7i_mp_internal_data.waveBuf[0]);
|
||||
|
||||
rd7i_mp_internal_data.waveBuf[1].nsamples =
|
||||
MusicDecoder::decode(&rd7i_mp_internal_data.buf2[0]) /
|
||||
MusicDecoder::getChannels();
|
||||
rd7i_mp_internal_data.waveBuf[1].data_vaddr = &rd7i_mp_internal_data.buf2[0];
|
||||
ndspChnWaveBufAdd(rd7i_player_channel, &rd7i_mp_internal_data.waveBuf[1]);
|
||||
|
||||
// Wait for music to start playing
|
||||
while (ndspChnIsPlaying(rd7i_player_channel) == false) {
|
||||
// Waiting
|
||||
}
|
||||
|
||||
while (rd7i_mp_internal_data.stop == false) {
|
||||
svcSleepThread(100 * 1000);
|
||||
if (rd7i_mp_internal_data.last_buf == true &&
|
||||
rd7i_mp_internal_data.waveBuf[0].status == NDSP_WBUF_DONE &&
|
||||
rd7i_mp_internal_data.waveBuf[1].status == NDSP_WBUF_DONE)
|
||||
break;
|
||||
|
||||
if (ndspChnIsPaused(rd7i_player_channel) == true ||
|
||||
rd7i_mp_internal_data.last_buf == true)
|
||||
continue;
|
||||
|
||||
if (rd7i_mp_internal_data.waveBuf[0].status == NDSP_WBUF_DONE) {
|
||||
size_t read = MusicDecoder::decode(&rd7i_mp_internal_data.buf1[0]);
|
||||
/* The previous block of samples have finished playing,
|
||||
* so accumulate them here. */
|
||||
rd7i_mp_internal_data.samples_played +=
|
||||
rd7i_mp_internal_data.waveBuf[0].nsamples *
|
||||
MusicDecoder::getChannels();
|
||||
|
||||
if (read <= 0) {
|
||||
rd7i_mp_internal_data.last_buf = true;
|
||||
continue;
|
||||
} else if (read < MusicDecoder::getBufSize())
|
||||
rd7i_mp_internal_data.waveBuf[0].nsamples =
|
||||
read / MusicDecoder::getChannels();
|
||||
|
||||
ndspChnWaveBufAdd(rd7i_player_channel, &rd7i_mp_internal_data.waveBuf[0]);
|
||||
}
|
||||
|
||||
if (rd7i_mp_internal_data.waveBuf[1].status == NDSP_WBUF_DONE) {
|
||||
size_t read = MusicDecoder::decode(&rd7i_mp_internal_data.buf2[0]);
|
||||
rd7i_mp_internal_data.samples_played +=
|
||||
rd7i_mp_internal_data.waveBuf[0].nsamples *
|
||||
MusicDecoder::getChannels();
|
||||
|
||||
if (read <= 0) {
|
||||
rd7i_mp_internal_data.last_buf = true;
|
||||
continue;
|
||||
} else if (read < MusicDecoder::getBufSize())
|
||||
rd7i_mp_internal_data.waveBuf[1].nsamples =
|
||||
read / MusicDecoder::getChannels();
|
||||
|
||||
ndspChnWaveBufAdd(rd7i_player_channel, &rd7i_mp_internal_data.waveBuf[1]);
|
||||
}
|
||||
|
||||
DSP_FlushDataCache(rd7i_mp_internal_data.buf1,
|
||||
MusicDecoder::getBufSize() * sizeof(int16_t));
|
||||
DSP_FlushDataCache(rd7i_mp_internal_data.buf2,
|
||||
MusicDecoder::getBufSize() * sizeof(int16_t));
|
||||
}
|
||||
rd7i_mp_internal_data.samples_played +=
|
||||
rd7i_mp_internal_data.waveBuf[0].nsamples * MusicDecoder::getChannels();
|
||||
rd7i_mp_internal_data.samples_played +=
|
||||
rd7i_mp_internal_data.waveBuf[0].nsamples * MusicDecoder::getChannels();
|
||||
rd7i_player_cleaner();
|
||||
rd7i_mp_internal_data.playing = false;
|
||||
rd7i_mp_internal_data.done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void MusicPlayer::PlayFile(const std::string &path) {
|
||||
if (MusicPlayer::IsRunning()) {
|
||||
rd7i_mp_internal_data.stop = true;
|
||||
}
|
||||
while (rd7i_mp_internal_data.playing) {
|
||||
}
|
||||
RenderD7::Thread::sleep(100);
|
||||
rd7i_mph.initialize(rd7i_mplayer, path);
|
||||
rd7i_mph.start(true);
|
||||
}
|
||||
|
||||
void MusicPlayer::Play() {
|
||||
if (!rd7i_mp_internal_data.playing) {
|
||||
ndspChnSetPaused(rd7i_player_channel, false);
|
||||
rd7i_mp_internal_data.playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MusicPlayer::Pause() {
|
||||
if (rd7i_mp_internal_data.playing) {
|
||||
ndspChnSetPaused(rd7i_player_channel, true);
|
||||
rd7i_mp_internal_data.playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MusicPlayer::Stop() { rd7i_mp_internal_data.stop = true; }
|
||||
|
||||
bool MusicPlayer::IsPlaying() { return rd7i_mp_internal_data.playing; }
|
||||
|
||||
bool MusicPlayer::IsRunning() { return rd7i_mph.isRunning(); }
|
||||
|
||||
int MusicPlayer::PosCurrent() { return rd7i_mp_internal_data.samples_played; }
|
||||
|
||||
int MusicPlayer::Total() { return rd7i_mp_internal_data.samples_total; }
|
||||
|
||||
int MusicPlayer::SampleRate() { return rd7i_mp_internal_data.samples_per_sec; }
|
||||
|
||||
MusicMeta MusicPlayer::GetMeta() { return rd7i_mp_internal_data.meta; }
|
||||
|
||||
} // namespace RenderD7
|
||||
|
||||
#endif
|
74
source/music/Vorbis.cpp
Normal file
74
source/music/Vorbis.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include <renderd7/music/Vorbis.hpp>
|
||||
|
||||
#ifdef RENDERD7_MUSICDEC
|
||||
|
||||
int rd7i_check_vorbis(const std::string &path) {
|
||||
// As always forced by libvorbis to not use fstream :(
|
||||
FILE *ft = fopen(path.c_str(), "r");
|
||||
OggVorbis_File testvf;
|
||||
int err;
|
||||
|
||||
if (ft == NULL) return -1;
|
||||
|
||||
err = ov_test(ft, &testvf, NULL, 0);
|
||||
|
||||
ov_clear(&testvf);
|
||||
fclose(ft);
|
||||
return err;
|
||||
}
|
||||
|
||||
namespace RenderD7 {
|
||||
int VorbisDecoder::_init(const std::string &path, MusicMeta& meta) {
|
||||
meta.path(path);
|
||||
if(path.find_last_of('/') != path.npos)
|
||||
meta.name(path.substr(path.find_last_of('/')));
|
||||
else
|
||||
meta.name(path);
|
||||
int ret = -1;
|
||||
if ((f = fopen(path.c_str(), "rb")) == NULL) return ret;
|
||||
if (ov_open(f, &vorbis_file, NULL, 0) < 0) return ret;
|
||||
if ((vi = ov_info(&vorbis_file, -1)) == NULL) return ret;
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int VorbisDecoder::_getSampleRate() { return vi->rate; }
|
||||
|
||||
unsigned char VorbisDecoder::_getChannels() { return vi->channels; }
|
||||
|
||||
size_t VorbisDecoder::_getBufSize() { return buf_size; }
|
||||
|
||||
unsigned long long VorbisDecoder::_decode(signed short*buf_addr) {
|
||||
uint64_t samples_read = 0;
|
||||
int samples2read = buf_size;
|
||||
char *buf = reinterpret_cast<char *>(buf_addr);
|
||||
|
||||
while (samples2read > 0) {
|
||||
int cur_section;
|
||||
int samples_just_read =
|
||||
ov_read(&vorbis_file, buf, samples2read > 4096 ? 4096 : samples2read,
|
||||
&cur_section);
|
||||
if (samples_just_read < 0)
|
||||
return samples_just_read;
|
||||
else if (samples_just_read == 0)
|
||||
break; // End of File
|
||||
samples_read += samples_just_read;
|
||||
samples2read -= samples_just_read;
|
||||
buf += samples_just_read;
|
||||
}
|
||||
// Aka / sizeof(int16_t)
|
||||
return samples_read / sizeof(signed short);
|
||||
}
|
||||
|
||||
void VorbisDecoder::_deinit() {
|
||||
ov_clear(&vorbis_file);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
size_t VorbisDecoder::_getFileSamples() {
|
||||
return ov_pcm_total(&vorbis_file, -1)*vi->channels;
|
||||
}
|
||||
|
||||
} // namespace RenderD7
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user