【问题标题】:Efficient way of creating a sorted music library创建排序音乐库的有效方法
【发布时间】:2019-08-21 06:34:05
【问题描述】:

我正在尝试在 qt/c++ 中创建自己的音乐播放器/库。 我已经完成的是扫描音乐文件的文件夹,使用 taglib 读取音乐文件的标签,从文件中创建一个 Track 结构,并将 Track 放入 @ 987654325@ 的曲目。这是我到目前为止的代码:

这会扫描文件夹:

void LibraryScanner::scanFolder (std::string scanFolder) {
    if (filesystem::exists (scanFolder) && filesystem::is_directory (scanFolder))
    {
        filesystem::recursive_directory_iterator recursiveIterator (scanFolder);

        for (const auto& entry : filesystem::recursive_directory_iterator (scanFolder)) {
            std::wstring path = entry.path().wstring ();
            if (hasEnding (path, L".flac") || hasEnding (path, L".wav") || hasEnding (path, L".alac") || hasEnding (path, L".mp3") || hasEnding (path, L".m4a") || hasEnding (path, L".wma") || hasEnding (path, L".opus") || hasEnding (path, L".ogg") || hasEnding (path, L".aiff")) { ///TODO: ignore capital letters
                scanFile (path);
            }
        }
    }
}

每个音频文件然后在 scanFile 方法中进一步处理:

void LibraryScanner::scanFile (const std::wstring filePath)
{
    Track track = Track ();
    TagLib::FileRef f (filePath.c_str());
    if(!f.isNull()){
        track.filePath = filePath.c_str ();
        track.Title = f.tag()->title().toWString();
        track.Artists = f.tag()->artist().toWString();
        tracks.push_back (track);
    }else {
        std::wcout <<  "File is null." << '\n';
    }
}

每条轨道都放入这个向量中:

std::vector<Track> tracks;

这是Track 结构:

#pragma once
#include<string>

struct Track{
    std::wstring filePath, Title, Artists;
};

所以现在,我希望我的音乐播放器能够像这样工作:

music player

sort

有一个按字母顺序排序的艺术家列表(图片左侧),每个艺术家都有不同的曲目(右侧),这些曲目也按他们的专辑排序。

我还应该注意,每个曲目可以有多个艺术家,因此 2 个艺术家可以共享相同的曲目。

而且,我不仅可以按艺术家排序,还可以按专辑或其他标签排序(您可以在第二张图片中看到)。在这个程序中可以立即在按艺术家/专辑排序之间切换。

那么我如何在我自己的程序中实现这样的东西,它也非常高效? 我正在考虑在扫描期间对所有内容进行预分类。所以在我将每个Track 放入Tracks 的vector 之后,我创建了一个vectorSet 的Artists,每个艺术家都包含曲目或专辑。所以我创建了一个像这样的Artist 结构:

#pragma once
#include <vector>
#include "Track.h"
#include <string>
class Artist
{
public:
    Artist (std::wstring artistName);
    ~Artist ();
    std::wstring artistName;
    std::vector<Track*> tracks;
};

由于多个艺术家可以共享曲目,我认为让曲目矢量只包含 Track 的指针是个好主意。

所以我预先排序的艺术家列表将是std::vector&lt;Artist&gt; artists;,并且每个艺术家都包含自己的具有相同艺术家标签的曲目(稍后我可以按专辑排序)。

然后,如果我想按专辑排序,我有另一个像这样的向量:std::vector&lt;Album&gt; albums;,每个 Album 都包含自己的曲目。 如果我想切换排序模式,我只需更改哪个向量显示为列表即可。

我的问题是,是否有更高效/更高效的方式来完成这一切。 这看起来会占用大量内存并且需要花费大量时间进行排序,因为我基本上会在扫描后进行多次排序(每种排序模式一个向量)。

另外,我需要检查是否有重复的艺术家。另外,每个曲目/艺术家/专辑都应该有一个我想要显示的封面图片。我也需要检查那些重复项

所以这种方式会占用大量资源。 如果有人知道如何有效地做到这一点,我将非常感激,并且也许也有一些代码示例。 如果有人知道一种更高效的方式来扫描文件夹中的文件或我在发布的代码 sn-ps 中所做的事情,那也会有所帮助。

谢谢!

【问题讨论】:

  • 也许你想为你的数据使用 sqlite。
  • 由于多个艺术家可以共享曲目 -- 建议使用std::vector&lt;std::shared_ptr&lt;Track&gt;&gt;而不是std::vector&lt;Track*&gt;

标签: c++ qt sorting vector set


【解决方案1】:

使用数据库: 使用 mysql 之类的数据库来存储所有的音频信息和元数据。排序和重复检查将更容易实现。

对音频文件进行分类: 根据流派将文件分类到单独的文件夹中。如果音频文件的数量很大,您可以一次只加载一个页面,在排序后,然后继续执行后续页面。

在播放列表中使用 smart_pointer: 使用 std::shared_ptr 从媒体文件夹加载到播放列表。这将进一步减少内存的消耗。例如,如果在音频文件之间插入广告,则这两个部分可能仍具有相同的字段。

【讨论】:

  • 我认为在这种情况下sqlite会比mysql更好。
【解决方案2】:

您应该尝试查看模型视图控制器框架类,例如 https://doc.qt.io/qt-5/model-view-programming.html

他们有代理模型的概念,而不是基于内存或文件系统或基于 sqlite 数据的模型,这些代理模型具有排序和过滤等功能。

创建 GUI 也是自动的,因为您只需要将列表视图或树视图与代理模型连接起来。

这些是高度优化的模型,排序和过滤也非常有效,代码更少。

【讨论】:

    猜你喜欢
    • 2020-09-03
    • 1970-01-01
    • 2013-07-30
    • 2012-08-30
    • 1970-01-01
    • 2013-06-02
    • 1970-01-01
    • 2011-05-24
    • 2017-09-07
    相关资源
    最近更新 更多