M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp => module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp +6 -11
@@ 242,7 242,7 @@ namespace gui
onLoadCallback = [&](std::shared_ptr<AlarmsRecord> alarm) {
switch (itemName) {
case AlarmOptionItemName::Sound: {
- auto it = std::find_if(songsList.begin(), songsList.end(), [alarm](const audio::Tags &tag) {
+ auto it = std::find_if(songsList.begin(), songsList.end(), [alarm](const tags::fetcher::Tags &tag) {
return tag.filePath == alarm->path.c_str();
});
if (it == songsList.end()) {
@@ 308,22 308,17 @@ namespace gui
};
}
- std::vector<audio::Tags> AlarmOptionsItem::getMusicFilesList()
+ std::vector<tags::fetcher::Tags> AlarmOptionsItem::getMusicFilesList()
{
const auto musicFolder = (purefs::dir::getUserDiskPath() / "music").string();
- std::vector<audio::Tags> musicFiles;
+ std::vector<tags::fetcher::Tags> musicFiles;
LOG_INFO("Scanning music folder: %s", musicFolder.c_str());
for (const auto &ent : std::filesystem::directory_iterator(musicFolder)) {
if (!ent.is_directory()) {
const auto filePath = std::string(musicFolder) + "/" + ent.path().filename().c_str();
- auto fileTags = AudioServiceAPI::GetFileTags(application, filePath);
- if (fileTags) {
- musicFiles.push_back(*fileTags);
- LOG_DEBUG("file: %s found", ent.path().filename().c_str());
- }
- else {
- LOG_ERROR("Not an audio file %s", ent.path().filename().c_str());
- }
+ auto fileTags = tags::fetcher::fetchTags(filePath);
+ musicFiles.push_back(fileTags);
+ LOG_DEBUG("file: %s found", ent.path().filename().c_str());
}
}
LOG_INFO("Total number of music files found: %u", static_cast<unsigned int>(musicFiles.size()));
M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp => module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp +4 -5
@@ 9,7 9,7 @@
#include <Label.hpp>
#include <Image.hpp>
#include <BoxLayout.hpp>
-#include <Audio/decoder/Decoder.hpp>
+#include <tags_fetcher/TagsFetcher.hpp>
namespace gui
{
@@ 32,12 32,11 @@ namespace gui
gui::Image *rightArrow = nullptr;
AlarmOptionItemName itemName;
std::vector<std::string> optionsNames;
- std::vector<audio::Tags> songsList;
-
/// pointer to audio operations which allows to make audio preview
std::unique_ptr<app::AbstractAudioOperations> audioOperations;
- MusicStatus musicStatus = MusicStatus::Stop;
+ std::vector<tags::fetcher::Tags> songsList;
+ MusicStatus musicStatus = MusicStatus::Stop;
audio::Token currentlyPreviewedToken;
std::string currentlyPreviewedPath;
@@ 48,7 47,7 @@ namespace gui
std::function<void()> bottomBarRestoreFromTemporaryMode = nullptr;
void prepareOptionsNames();
void applyCallbacks();
- std::vector<audio::Tags> getMusicFilesList();
+ std::vector<tags::fetcher::Tags> getMusicFilesList();
public:
AlarmOptionsItem(app::Application *app,
M module-apps/application-music-player/ApplicationMusicPlayer.cpp => module-apps/application-music-player/ApplicationMusicPlayer.cpp +1 -1
@@ 31,7 31,7 @@ namespace app
};
} // namespace music_player::internal
- constexpr std::size_t applicationMusicPlayerStackSize = 4 * 1024;
+ constexpr std::size_t applicationMusicPlayerStackSize = 5 * 1024;
ApplicationMusicPlayer::ApplicationMusicPlayer(std::string name,
std::string parent,
M module-apps/application-music-player/CMakeLists.txt => module-apps/application-music-player/CMakeLists.txt +1 -0
@@ 46,6 46,7 @@ target_link_libraries(application-music-player
PUBLIC
apps-common
module-audio
+ tagsfetcher
)
if (${ENABLE_TESTS})
M module-apps/application-music-player/models/SongsRepository.cpp => module-apps/application-music-player/models/SongsRepository.cpp +8 -6
@@ 9,6 9,7 @@
#include <service-audio/AudioServiceName.hpp>
#include <time/ScopedTime.hpp>
#include <service-audio/AudioMessage.hpp>
+#include <tags_fetcher/TagsFetcher.hpp>
#include <filesystem>
@@ 17,9 18,9 @@ namespace app::music_player
ServiceAudioTagsFetcher::ServiceAudioTagsFetcher(Application *application) : application(application)
{}
- std::optional<audio::Tags> ServiceAudioTagsFetcher::getFileTags(const std::string &filePath) const
+ std::optional<tags::fetcher::Tags> ServiceAudioTagsFetcher::getFileTags(const std::string &filePath) const
{
- return AudioServiceAPI::GetFileTags(application, filePath);
+ return tags::fetcher::fetchTags(filePath);
}
SongsRepository::SongsRepository(std::unique_ptr<AbstractTagsFetcher> tagsFetcher, std::string musicFolderName)
@@ 45,14 46,15 @@ namespace app::music_player
}
}
}
- std::sort(musicFiles.begin(), musicFiles.end(), [](audio::Tags t1, audio::Tags t2) {
- return t1.filePath < t2.filePath;
- });
+ std::sort(
+ musicFiles.begin(), musicFiles.end(), [](const tags::fetcher::Tags &t1, const tags::fetcher::Tags &t2) {
+ return t1.filePath < t2.filePath;
+ });
}
LOG_INFO("Total number of music files found: %u", static_cast<unsigned int>(musicFiles.size()));
}
- std::vector<audio::Tags> SongsRepository::getMusicFilesList() const
+ std::vector<tags::fetcher::Tags> SongsRepository::getMusicFilesList() const
{
return musicFiles;
}
M module-apps/application-music-player/models/SongsRepository.hpp => module-apps/application-music-player/models/SongsRepository.hpp +6 -6
@@ 4,7 4,7 @@
#pragma once
#include <apps-common/Application.hpp>
-#include <Audio/decoder/Decoder.hpp>
+#include <tags_fetcher/TagsFetcher.hpp>
#include <purefs/filesystem_paths.hpp>
#include <memory>
@@ 21,7 21,7 @@ namespace app::music_player
public:
virtual ~AbstractTagsFetcher() noexcept = default;
- virtual std::optional<audio::Tags> getFileTags(const std::string &filePath) const = 0;
+ virtual std::optional<tags::fetcher::Tags> getFileTags(const std::string &filePath) const = 0;
};
class ServiceAudioTagsFetcher : public AbstractTagsFetcher
@@ 29,7 29,7 @@ namespace app::music_player
public:
explicit ServiceAudioTagsFetcher(Application *application);
- std::optional<audio::Tags> getFileTags(const std::string &filePath) const final;
+ std::optional<tags::fetcher::Tags> getFileTags(const std::string &filePath) const final;
private:
Application *application = nullptr;
@@ 41,7 41,7 @@ namespace app::music_player
virtual ~AbstractSongsRepository() noexcept = default;
virtual void scanMusicFilesList() = 0;
- virtual std::vector<audio::Tags> getMusicFilesList() const = 0;
+ virtual std::vector<tags::fetcher::Tags> getMusicFilesList() const = 0;
virtual std::size_t getFileIndex(const std::string &filePath) const = 0;
virtual std::string getNextFilePath(const std::string &filePath) const = 0;
virtual std::string getPreviousFilePath(const std::string &filePath) const = 0;
@@ 56,7 56,7 @@ namespace app::music_player
std::string musicFolderName = purefs::dir::getUserDiskPath() / musicSubfolderName);
void scanMusicFilesList() override;
- std::vector<audio::Tags> getMusicFilesList() const override;
+ std::vector<tags::fetcher::Tags> getMusicFilesList() const override;
std::size_t getFileIndex(const std::string &filePath) const override;
std::string getNextFilePath(const std::string &filePath) const override;
std::string getPreviousFilePath(const std::string &filePath) const override;
@@ 64,6 64,6 @@ namespace app::music_player
private:
std::unique_ptr<AbstractTagsFetcher> tagsFetcher;
std::string musicFolderName;
- std::vector<audio::Tags> musicFiles;
+ std::vector<tags::fetcher::Tags> musicFiles;
};
} // namespace app::music_player
M module-apps/application-music-player/tests/MockSongsRepository.hpp => module-apps/application-music-player/tests/MockSongsRepository.hpp +1 -1
@@ 18,7 18,7 @@ namespace testing::app::music_player
{
public:
MOCK_METHOD(void, scanMusicFilesList, (), (override));
- MOCK_METHOD(std::vector<audio::Tags>, getMusicFilesList, (), (const override));
+ MOCK_METHOD(std::vector<tags::fetcher::Tags>, getMusicFilesList, (), (const override));
MOCK_METHOD(std::size_t, getFileIndex, (const std::string &filePath), (const override));
MOCK_METHOD(std::string, getNextFilePath, (const std::string &filePath), (const override));
MOCK_METHOD(std::string, getPreviousFilePath, (const std::string &filePath), (const override));
M module-apps/application-music-player/tests/MockTagsFetcher.hpp => module-apps/application-music-player/tests/MockTagsFetcher.hpp +1 -1
@@ 15,6 15,6 @@ namespace testing::app::music_player
class MockTagsFetcher : public ::app::music_player::AbstractTagsFetcher
{
public:
- MOCK_METHOD(std::optional<audio::Tags>, getFileTags, (const std::string &filePath), (const override));
+ MOCK_METHOD(std::optional<tags::fetcher::Tags>, getFileTags, (const std::string &filePath), (const override));
};
}; // namespace testing::app::music_player
M module-apps/application-music-player/tests/unittest_songrepository.cpp => module-apps/application-music-player/tests/unittest_songrepository.cpp +5 -14
@@ 7,7 7,7 @@
#include "MockTagsFetcher.hpp"
#include <models/SongsRepository.hpp>
-
+#include <tags_fetcher/TagsFetcher.hpp>
#include <filesystem>
#include <fstream>
#include <stdexcept>
@@ 101,11 101,8 @@ TEST_F(SongsRepositoryFixture, ScanWithTagsReturn)
auto rawMock = tagsFetcherMock.get();
auto repo = std::make_unique<app::music_player::SongsRepository>(std::move(tagsFetcherMock), musicDirPath);
- auto fooTags = ::audio::Tags();
- auto barTags = ::audio::Tags();
-
- fooTags.title = "foo";
- barTags.title = "bar";
+ auto fooTags = tags::fetcher::Tags{"foo"};
+ auto barTags = tags::fetcher::Tags{"foo"};
ON_CALL(*rawMock, getFileTags(fs::path(musicDirPath / "foo").c_str())).WillByDefault(Return(fooTags));
ON_CALL(*rawMock, getFileTags(fs::path(musicDirPath / "bar").c_str())).WillByDefault(Return(barTags));
@@ 125,14 122,8 @@ TEST_F(SongsRepositoryFixture, FileIndex)
auto fooPath = musicDirPath / "foo";
auto barPath = musicDirPath / "bar";
- auto fooTags = ::audio::Tags();
- auto barTags = ::audio::Tags();
-
- fooTags.title = "foo";
- fooTags.filePath = fooPath.c_str();
-
- barTags.title = "bar";
- barTags.filePath = barPath.c_str();
+ auto fooTags = tags::fetcher::Tags(fooPath);
+ auto barTags = tags::fetcher::Tags(barPath);
ON_CALL(*rawMock, getFileTags(fs::path(musicDirPath / "foo").c_str())).WillByDefault(Return(fooTags));
ON_CALL(*rawMock, getFileTags(fs::path(musicDirPath / "bar").c_str())).WillByDefault(Return(barTags));
M module-apps/application-music-player/tests/unittest_songsmodel.cpp => module-apps/application-music-player/tests/unittest_songsmodel.cpp +1 -1
@@ 41,6 41,6 @@ TEST(SongsModel, createDataNoSongs)
auto model = SongsModel(mockRepo);
EXPECT_CALL(*mockRepo, scanMusicFilesList);
- EXPECT_CALL(*mockRepo, getMusicFilesList).WillRepeatedly(Return(std::vector<audio::Tags>()));
+ EXPECT_CALL(*mockRepo, getMusicFilesList).WillRepeatedly(Return(std::vector<tags::fetcher::Tags>()));
model.createData([](const std::string &) { return true; }, []() {}, [](const UTF8 &) {}, []() {});
}
M module-apps/application-settings/models/apps/SoundsModel.cpp => module-apps/application-settings/models/apps/SoundsModel.cpp +3 -4
@@ 9,6 9,7 @@
#include <ListView.hpp>
#include <purefs/filesystem_paths.hpp>
#include <service-audio/AudioServiceAPI.hpp>
+#include <tags_fetcher/TagsFetcher.hpp>
SoundsModel::SoundsModel(std::shared_ptr<AbstractSoundsPlayer> soundsPlayer) : soundsPlayer{std::move(soundsPlayer)}
{}
@@ 105,10 106,8 @@ void SoundsModel::applyItems(const std::vector<std::filesystem::path> &sounds,
}
std::string itemTitle;
- auto fileTags = AudioServiceAPI::GetFileTags(app, sound);
- if (fileTags) {
- itemTitle = fileTags->title;
- }
+ auto fileTags = tags::fetcher::fetchTags(sound);
+ itemTitle = fileTags.title;
if (itemTitle.empty()) {
itemTitle = sound.filename();
M module-audio/Audio/Audio.cpp => module-audio/Audio/Audio.cpp +0 -11
@@ 25,17 25,6 @@ namespace audio
return currentOperation->GetPosition();
}
- std::optional<Tags> Audio::GetFileTags(const char *filename)
- {
- auto ret = Decoder::Create(filename);
- if (ret == nullptr) {
- return {};
- }
- else {
- return *ret->fetchTags();
- };
- }
-
audio::RetCode Audio::SendEvent(std::shared_ptr<Event> evt)
{
audioSinkState.UpdateState(evt);
M module-audio/Audio/Audio.hpp => module-audio/Audio/Audio.hpp +0 -2
@@ 49,8 49,6 @@ namespace audio
return currentState;
}
- static std::optional<Tags> GetFileTags(const char *filename);
-
// Range 0-1
audio::RetCode SetOutputVolume(Volume vol);
M module-audio/Audio/Operation/PlaybackOperation.cpp => module-audio/Audio/Operation/PlaybackOperation.cpp +1 -2
@@ 36,7 36,6 @@ namespace audio
if (dec == nullptr) {
throw AudioInitException("Error during initializing decoder", RetCode::FileDoesntExist);
}
- tags = dec->fetchTags();
auto format = dec->getSourceFormat();
LOG_DEBUG("Source format: %s", format.toString().c_str());
@@ 183,7 182,7 @@ namespace audio
}
// adjust new profile with information from file's tags
- newProfile->SetSampleRate(tags->sample_rate);
+ newProfile->SetSampleRate(dec->getSourceFormat().getSampleRate());
newProfile->SetInOutFlags(static_cast<uint32_t>(audio::codec::Flags::OutputStereo));
/// profile change - (re)create output device; stop audio first by
M module-audio/Audio/Operation/PlaybackOperation.hpp => module-audio/Audio/Operation/PlaybackOperation.hpp +0 -1
@@ 47,7 47,6 @@ namespace audio
std::unique_ptr<Stream> dataStreamOut;
std::unique_ptr<Decoder> dec;
- std::unique_ptr<Tags> tags;
std::unique_ptr<StreamConnection> outputConnection;
DecoderWorker::EndOfFileCallback endOfFileCallback;
M module-audio/Audio/decoder/Decoder.cpp => module-audio/Audio/decoder/Decoder.cpp +8 -44
@@ 11,13 11,13 @@
#include "fileref.h"
#include "tag.h"
#include "tfilestream.h"
+#include <tags_fetcher/TagsFetcher.hpp>
namespace audio
{
Decoder::Decoder(const char *fileName)
- : filePath(fileName), workerBuffer(std::make_unique<int16_t[]>(workerBufferSize)), tag(std::make_unique<Tags>())
+ : filePath(fileName), workerBuffer(std::make_unique<int16_t[]>(workerBufferSize))
{
-
fd = std::fopen(fileName, "r");
if (fd == NULL) {
return;
@@ 26,6 26,8 @@ namespace audio
std::fseek(fd, 0, SEEK_END);
fileSize = std::ftell(fd);
std::rewind(fd);
+
+ tags = fetchTags();
}
Decoder::~Decoder()
@@ 39,46 41,9 @@ namespace audio
}
}
- std::unique_ptr<Tags> Decoder::fetchTags()
+ std::unique_ptr<tags::fetcher::Tags> Decoder::fetchTags()
{
- if (fd) {
- const auto inPos = std::ftell(fd);
- std::rewind(fd);
- TagLib::FileStream fileStream(fd);
- TagLib::FileRef tagReader(&fileStream);
- if (!tagReader.isNull() && tagReader.tag()) {
- TagLib::Tag *tags = tagReader.tag();
- TagLib::AudioProperties *properties = tagReader.audioProperties();
-
- constexpr auto unicode = true;
- tag->title = tags->title().to8Bit(unicode);
- tag->artist = tags->artist().to8Bit(unicode);
- tag->album = tags->album().to8Bit(unicode);
- tag->genre = tags->genre().to8Bit(unicode);
- tag->year = std::to_string(tags->year());
-
- tag->total_duration_s = properties->length();
- tag->duration_min = tag->total_duration_s / utils::secondsInMinute;
- tag->duration_hour = tag->duration_min / utils::secondsInMinute;
- tag->duration_sec = tag->total_duration_s % utils::secondsInMinute;
- tag->sample_rate = properties->sampleRate();
- tag->num_channel = properties->channels();
- tag->bitrate = properties->bitrate();
- }
- std::fseek(fd, inPos, SEEK_SET);
- }
-
- tag->filePath.append(filePath);
- // If title tag empty fill it with raw file name
- if (tag->title.size() == 0) {
- if (const auto pos = filePath.rfind("/"); pos == std::string::npos) {
- tag->title.append(filePath);
- }
- else {
- tag->title.append(&filePath[pos + 1]);
- }
- }
- return std::make_unique<Tags>(*tag);
+ return std::make_unique<tags::fetcher::Tags>(tags::fetcher::fetchTags(filePath));
}
std::unique_ptr<Decoder> Decoder::Create(const char *file)
@@ 127,8 92,8 @@ namespace audio
std::make_unique<DecoderWorker>(_stream,
this,
endOfFileCallback,
- tag->num_channel == 1 ? DecoderWorker::ChannelMode::ForceStereo
- : DecoderWorker::ChannelMode::NoConversion);
+ tags->num_channel == 1 ? DecoderWorker::ChannelMode::ForceStereo
+ : DecoderWorker::ChannelMode::NoConversion);
audioWorker->init();
audioWorker->run();
}
@@ 162,7 127,6 @@ namespace audio
auto Decoder::getSourceFormat() -> AudioFormat
{
- auto tags = fetchTags();
auto bitWidth = getBitWidth();
// this is a decoder mono to stereo hack, will be removed when proper
// transcoding implementation is added
M module-audio/Audio/decoder/Decoder.hpp => module-audio/Audio/decoder/Decoder.hpp +3 -50
@@ 17,6 17,7 @@
#include <cstring>
#include <cstdint>
+#include <tags_fetcher/TagsFetcher.hpp>
namespace audio
{
@@ 26,53 27,6 @@ namespace audio
constexpr inline auto stereoSound = 2;
} // namespace channel
- struct Tags
- {
-
- /* Total audio duration in seconds */
- uint32_t total_duration_s = 0;
- /* Audio duration - hours part */
- uint32_t duration_hour = 0;
- /* Audio duration - minutes part */
- uint32_t duration_min = 0;
- /* Audio duration - seconds part */
- uint32_t duration_sec = 0;
-
- /* Sample rate */
- uint32_t sample_rate = 0;
- /* Number of channels */
- uint32_t num_channel = 0;
- /* bitrate */
- uint32_t bitrate = 0;
-
- std::string artist = "";
- std::string genre = "";
- std::string title = "";
- std::string album = "";
- std::string year = "";
- std::string filePath = "";
-
- Tags()
- {}
-
- // Copy constructor
- Tags(const Tags &p2)
- {
- total_duration_s = p2.total_duration_s;
- duration_hour = p2.duration_hour;
- duration_min = p2.duration_min;
- duration_sec = p2.duration_sec;
- sample_rate = p2.sample_rate;
- num_channel = p2.num_channel;
- artist = p2.artist;
- genre = p2.genre;
- title = p2.title;
- album = p2.album;
- year = p2.year;
- filePath = p2.filePath;
- }
- };
-
class Decoder : public Source
{
@@ 83,8 37,6 @@ namespace audio
virtual uint32_t decode(uint32_t samplesToRead, int16_t *pcmData) = 0;
- std::unique_ptr<Tags> fetchTags();
-
// Range 0 - 1
virtual void setPosition(float pos) = 0;
@@ 120,6 72,7 @@ namespace audio
protected:
virtual auto getBitWidth() -> unsigned int = 0;
+ virtual std::unique_ptr<tags::fetcher::Tags> fetchTags();
void convertmono2stereo(int16_t *pcm, uint32_t samplecount);
@@ 135,7 88,7 @@ namespace audio
// Worker buffer used for converting mono stream to stereo
std::unique_ptr<int16_t[]> workerBuffer;
- std::unique_ptr<Tags> tag;
+ std::unique_ptr<tags::fetcher::Tags> tags;
bool isInitialized = false;
// decoding worker
M module-audio/Audio/decoder/decoderFLAC.cpp => module-audio/Audio/decoder/decoderFLAC.cpp +2 -28
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <Utils.hpp>
@@ 23,7 23,7 @@ namespace audio
return;
}
- flac = drflac_open_with_metadata(drflac_read, drflac_seek, drflac_meta, this);
+ flac = drflac_open(drflac_read, drflac_seek, this);
if (flac == NULL) {
return;
}
@@ 82,32 82,6 @@ namespace audio
return !std::fseek(userdata->fd, offset, origin == drflac_seek_origin_start ? SEEK_SET : SEEK_CUR);
}
- void decoderFLAC::drflac_meta(void *pUserData, drflac_metadata *pMetadata)
- {
- decoderFLAC *userdata = (decoderFLAC *)pUserData;
-
- if (pMetadata->type == DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO) {
- userdata->tag->total_duration_s =
- (pMetadata->data.streaminfo.totalSampleCount / (pMetadata->data.streaminfo.bitsPerSample / 8)) /
- pMetadata->data.streaminfo.sampleRate;
-
- if (pMetadata->data.streaminfo.channels == 1) {
- userdata->tag->total_duration_s *= 2;
- }
-
- userdata->tag->duration_min = userdata->tag->total_duration_s / utils::secondsInMinute;
- userdata->tag->duration_hour = userdata->tag->duration_min / utils::secondsInMinute;
- userdata->tag->duration_sec = userdata->tag->total_duration_s % utils::secondsInMinute;
- userdata->tag->sample_rate = pMetadata->data.streaminfo.sampleRate;
- userdata->tag->num_channel = pMetadata->data.streaminfo.channels;
-
- userdata->sampleRate = pMetadata->data.streaminfo.sampleRate;
- userdata->chanNumber = pMetadata->data.streaminfo.channels;
-
- userdata->totalSamplesCount = pMetadata->data.streaminfo.totalSampleCount;
- }
- }
-
auto decoderFLAC::getBitWidth() -> unsigned int
{
TagLib::FLAC::File flacFile(filePath.c_str());
M module-audio/Audio/decoder/decoderFLAC.hpp => module-audio/Audio/decoder/decoderFLAC.hpp +1 -4
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 55,9 55,6 @@ namespace audio
// determined by the "origin" parameter which will be either drflac_seek_origin_start or
// drflac_seek_origin_current.
static drflac_bool32 drflac_seek(void *pUserData, int offset, drflac_seek_origin origin);
-
- // Use pMetadata->type to determine which metadata block is being handled and how to read the data.
- static void drflac_meta(void *pUserData, drflac_metadata *pMetadata);
};
} // namespace audio
M module-audio/Audio/test/unittest_audio.cpp => module-audio/Audio/test/unittest_audio.cpp +17 -14
@@ 20,21 20,24 @@
using namespace audio;
-TEST_CASE("Test audio tags")
+TEST_CASE("Audio Decoder")
{
- SECTION(" Encoder tests ")
- {
- std::vector<std::string> testExtensions = {"flac", "wav", "mp3"};
- for (auto ext : testExtensions) {
- auto dec = audio::Decoder::Create(("testfiles/audio." + ext).c_str());
- REQUIRE(dec);
- auto tags = dec->fetchTags();
- REQUIRE(tags);
- REQUIRE(tags->title == ext + " Test track title - łąki");
- REQUIRE(tags->artist == ext + " Test artist name - łąki");
- REQUIRE(tags->album == ext + " Test album title - łąki");
- REQUIRE(tags->year == "2020");
- }
+ std::vector<std::string> testExtensions = {"flac", "wav", "mp3"};
+ for (auto ext : testExtensions) {
+ auto dec = audio::Decoder::Create(("testfiles/audio." + ext).c_str());
+ REQUIRE(dec);
+ }
+}
+
+TEST_CASE(" Tags fetcher ")
+{
+ std::vector<std::string> testExtensions = {"flac", "wav", "mp3"};
+ for (auto ext : testExtensions) {
+ auto tags = tags::fetcher::fetchTags(("testfiles/audio." + ext).c_str());
+ REQUIRE(tags.title == ext + " Test track title - łąki");
+ REQUIRE(tags.artist == ext + " Test artist name - łąki");
+ REQUIRE(tags.album == ext + " Test album title - łąki");
+ REQUIRE(tags.year == "2020");
}
}
M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +3 -0
@@ 72,8 72,11 @@ target_link_libraries(${PROJECT_NAME}
minimp3::minimp3
PRIVATE
math
+ tagsfetcher
)
+add_subdirectory(tags_fetcher)
+
if (${ENABLE_TESTS})
add_subdirectory(Audio/test)
endif ()
M module-audio/README.md => module-audio/README.md +0 -4
@@ 136,10 136,6 @@ User should be aware of following constraints:
Failing to adhere will end in returning appropriate error.
-##### std::optional<Tags> GetFileTags(const char *filename);
-
-Fetches metadata of specified audio file. It can be invoked in any time. If file exists and is supported, audio meta tags structure will be returned.
-
# Missing features
#### Bluetooth audio device support
Currently support for BT audio devices is marginal. There are some code parts related to BT but they absolutely cannot be treated as target implementation. Adding BT support in my opinion should be split into several steps:
A module-audio/tags_fetcher/CMakeLists.txt => module-audio/tags_fetcher/CMakeLists.txt +20 -0
@@ 0,0 1,20 @@
+add_library(tagsfetcher STATIC)
+
+module_is_test_entity(tagsfetcher)
+
+target_sources(tagsfetcher
+ PRIVATE
+ TagsFetcher.cpp
+ PUBLIC
+ TagsFetcher.hpp)
+
+target_include_directories(ucs2
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+)
+
+target_link_libraries(tagsfetcher
+ PRIVATE
+ tag
+ Microsoft.GSL::GSL
+)
A module-audio/tags_fetcher/TagsFetcher.cpp => module-audio/tags_fetcher/TagsFetcher.cpp +75 -0
@@ 0,0 1,75 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "TagsFetcher.hpp"
+
+#include <gsl/util>
+#include <Utils.hpp>
+
+#include "fileref.h"
+#include "tag.h"
+#include "tfilestream.h"
+
+namespace tags::fetcher
+{
+ std::optional<Tags> fetchTagsInternal(std::string filePath)
+ {
+ TagLib::FileRef tagReader(filePath.c_str());
+ if (!tagReader.isNull() && tagReader.tag()) {
+ TagLib::Tag *tags = tagReader.tag();
+ TagLib::AudioProperties *properties = tagReader.audioProperties();
+
+ constexpr auto unicode = true;
+
+ auto getTitle = [&]() -> std::string {
+ const auto title = tags->title().to8Bit(unicode);
+ // If title tag empty fill it with raw file name
+ if (title.size() == 0) {
+ if (const auto pos = filePath.rfind("/"); pos == std::string::npos) {
+ return filePath;
+ }
+ else {
+ return &filePath[pos + 1];
+ }
+ }
+ return title;
+ };
+
+ const auto artist = tags->artist().to8Bit(unicode);
+ const auto album = tags->album().to8Bit(unicode);
+ const auto genre = tags->genre().to8Bit(unicode);
+ const auto year = std::to_string(tags->year());
+
+ const uint32_t total_duration_s = properties->length();
+ const uint32_t duration_min = total_duration_s / utils::secondsInMinute;
+ const uint32_t duration_hour = duration_min / utils::secondsInMinute;
+ const uint32_t duration_sec = total_duration_s % utils::secondsInMinute;
+ const uint32_t sample_rate = properties->sampleRate();
+ const uint32_t num_channel = properties->channels();
+ const uint32_t bitrate = properties->bitrate();
+ const auto title = getTitle();
+
+ return Tags{total_duration_s,
+ duration_hour,
+ duration_min,
+ duration_sec,
+ sample_rate,
+ num_channel,
+ bitrate,
+ artist,
+ genre,
+ title,
+ album,
+ year,
+ filePath};
+ }
+
+ return {};
+ }
+
+ Tags fetchTags(std::string filePath)
+ {
+ return fetchTagsInternal(filePath).value_or(Tags{filePath});
+ }
+
+} // namespace tags::fetcher
A module-audio/tags_fetcher/TagsFetcher.hpp => module-audio/tags_fetcher/TagsFetcher.hpp +63 -0
@@ 0,0 1,63 @@
+
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace tags::fetcher
+{
+ struct Tags
+ {
+
+ /* Total audio duration in seconds */
+ uint32_t total_duration_s = 0;
+ /* Audio duration - hours part */
+ uint32_t duration_hour = 0;
+ /* Audio duration - minutes part */
+ uint32_t duration_min = 0;
+ /* Audio duration - seconds part */
+ uint32_t duration_sec = 0;
+
+ /* Sample rate */
+ uint32_t sample_rate = 0;
+ /* Number of channels */
+ uint32_t num_channel = 0;
+ /* bitrate */
+ uint32_t bitrate = 0;
+
+ std::string artist;
+ std::string genre;
+ std::string album;
+ std::string year;
+ std::string filePath;
+ std::string title;
+
+ Tags() = default;
+ Tags(uint32_t total_duration_s,
+ uint32_t duration_hour,
+ uint32_t duration_min,
+ uint32_t duration_sec,
+ uint32_t sample_rate,
+ uint32_t num_channel,
+ uint32_t bitrate,
+ std::string artist,
+ std::string genre,
+ std::string title,
+ std::string album,
+ std::string year,
+ std::string filePath)
+ : total_duration_s{total_duration_s}, duration_hour{duration_hour}, duration_min{duration_min},
+ duration_sec{duration_sec}, sample_rate{sample_rate}, num_channel{num_channel}, bitrate{bitrate},
+ artist{artist}, genre{genre}, album{album}, year{year}, filePath{filePath}, title{title}
+ {}
+
+ explicit Tags(std::string filePath) : filePath{filePath}, title{filePath}
+ {}
+ };
+
+ Tags fetchTags(std::string fileName);
+} // namespace tags::fetcher
M module-services/service-audio/AudioServiceAPI.cpp => module-services/service-audio/AudioServiceAPI.cpp +0 -13
@@ 200,19 200,6 @@ namespace AudioServiceAPI
}
}
- std::optional<Tags> GetFileTags(sys::Service *serv, const std::string &fileName)
- {
- auto msg = std::make_shared<AudioGetFileTagsRequest>(fileName);
-
- auto resp = SendAudioRequest(serv, msg);
- if (resp->retCode == audio::RetCode::Success) {
- return resp->tags;
- }
- else {
- return std::nullopt;
- }
- }
-
bool KeyPressed(sys::Service *serv, const int step)
{
auto msg = std::make_shared<AudioKeyPressedRequest>(step);
M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +0 -12
@@ 292,14 292,6 @@ void ServiceAudio::EnableContinuousVibration(std::optional<audio::AudioMux::Inpu
}
}
-std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleGetFileTags(const std::string &fileName)
-{
- if (auto tag = Audio::GetFileTags(fileName.c_str())) {
- return std::make_unique<AudioResponseMessage>(RetCode::Success, tag.value());
- }
- return std::make_unique<AudioResponseMessage>(RetCode::FileDoesntExist);
-}
-
std::unique_ptr<AudioResponseMessage> ServiceAudio::HandlePause(const Token &token)
{
auto input = audioMux.GetInput(token);
@@ 617,10 609,6 @@ sys::MessagePointer ServiceAudio::DataReceivedHandler(sys::DataMessage *msgl, sy
auto *msg = static_cast<AudioResumeRequest *>(msgl);
responseMsg = HandleResume(msg->token);
}
- else if (msgType == typeid(AudioGetFileTagsRequest)) {
- auto *msg = static_cast<AudioGetFileTagsRequest *>(msgl);
- responseMsg = HandleGetFileTags(msg->fileName);
- }
else if (msgType == typeid(AudioEventRequest)) {
auto *msg = static_cast<AudioEventRequest *>(msgl);
responseMsg = HandleSendEvent(msg->getEvent());
M module-services/service-audio/include/service-audio/AudioMessage.hpp => module-services/service-audio/include/service-audio/AudioMessage.hpp +2 -17
@@ 24,17 24,11 @@ class AudioMessage : public sys::DataMessage
class AudioResponseMessage : public sys::ResponseMessage
{
public:
- explicit AudioResponseMessage(audio::RetCode retCode = audio::RetCode::Success,
- const audio::Tags &tags = {},
- const std::string &val = {})
- : sys::ResponseMessage(), retCode(retCode), tags(tags), val(val)
- {}
-
- AudioResponseMessage(audio::RetCode retCode, const std::string &val) : AudioResponseMessage(retCode, {}, val)
+ explicit AudioResponseMessage(audio::RetCode retCode = audio::RetCode::Success, const std::string &val = {})
+ : sys::ResponseMessage(), retCode(retCode), val(val)
{}
const audio::RetCode retCode = audio::RetCode::Success;
- audio::Tags tags = {};
std::string val;
};
@@ 229,15 223,6 @@ class AudioResumeResponse : public AudioResponseMessage
const audio::Token token;
};
-class AudioGetFileTagsRequest : public AudioMessage
-{
- public:
- explicit AudioGetFileTagsRequest(const std::string &fileName) : fileName(fileName)
- {}
-
- const std::string fileName;
-};
-
class AudioEventRequest : public AudioMessage
{
public:
M module-services/service-audio/include/service-audio/AudioServiceAPI.hpp => module-services/service-audio/include/service-audio/AudioServiceAPI.hpp +0 -8
@@ 122,14 122,6 @@ namespace AudioServiceAPI
bool SendEvent(sys::Service *serv,
audio::EventType evt,
audio::Event::DeviceState state = audio::Event::DeviceState::Connected);
- /**
- * @brief Attempts to parse audio file for metatags.
- *
- * @param serv Requesting service.
- * @param fileName Path to file to be parsed.
- * @return audio::Tags on success, std::nullopt on failure
- */
- std::optional<audio::Tags> GetFileTags(sys::Service *serv, const std::string &fileName);
/** @brief Sets vibrations setting state
*
M module-services/service-audio/include/service-audio/ServiceAudio.hpp => module-services/service-audio/include/service-audio/ServiceAudio.hpp +0 -1
@@ 79,7 79,6 @@ class ServiceAudio : public sys::Service
auto HandlePause(const audio::Token &token) -> std::unique_ptr<AudioResponseMessage>;
auto HandlePause(std::optional<audio::AudioMux::Input *> input) -> std::unique_ptr<AudioResponseMessage>;
auto HandleResume(const audio::Token &token) -> std::unique_ptr<AudioResponseMessage>;
- auto HandleGetFileTags(const std::string &fileName) -> std::unique_ptr<AudioResponseMessage>;
void HandleEOF(const audio::Token &token);
auto HandleKeyPressed(const int step) -> sys::MessagePointer;
void MuteCurrentOperation();