我正在开发一个iOS音乐应用程序(用C ++编写),我的模型或多或少看起来像这样:
--Song
----Track
----Track
------Pattern
------Pattern
--------Note
--------Note
--------Note
所以基本上Song
有多个Tracks
,Track
可以有多个Patterns
而Pattern
有多个Notes.
每个Note
那些东西由一个类表示,除了Song对象,它们都存储在向量中。
每个"frame"
都有一个Tracks
参数,以便我可以计算应该播放音符的时间。例如,如果我有44100个样本/秒并且特定音符的帧是132300,我知道我需要在第三秒开始时使用该音符。
我的问题是我应该如何代表这些笔记以获得最佳效果?现在我正在考虑将笔记存储在每个模式的向量数据中,而不是循环Song
的所有Patterns
,而不是查看Notes
而不是循环{{1查看哪个帧的数据大于132300且小于176400(第4秒的开始)。
正如你所知,这是一个很多循环,一首歌可能长达10分钟。所以我想知道这是否足够快以计算所有帧并按时将它们发送到缓冲区。
答案 0 :(得分:4)
您应该记住的一件事是,为了提高性能,通常需要增加内存消耗。在这种情况下,它也是相关的(并且是合理的),因为我认为您希望以不同的方式存储相同的数据两次。
首先,你应该有一首歌的基本结构:
map<Track, vector<Pattern>> tracks;
它将每个Track
映射到Pattern
s的向量。地图很好,因为你不关心轨道的顺序。
遍历Track
和Pattern
s应该很快,因为它们的金额不会很高(我假设)。主要的性能问题是循环数千个音符。以下是我建议解决的方法:
首先,对于每个Pattern
对象,您应该有vector<Note>
作为主要数据存储空间。您将首先将Pattern
内容的所有更改写入此vector<Note>
。
vector<Note> notes;
出于性能考虑,您可以采用第二种方式存储笔记:
map<int, vector<Notes>> measures;
这个会将Pattern
中的每个度量(按其数字)映射到此度量中包含的Note
s的向量。每当主notes
存储中的数据发生更改时,您都会对measures
中的数据应用相同的更改。您也可以在播放之前每次只执行一次,或者在播放时在单独的线程中执行此操作。
当然,您只能将度量存储在度量中,而无需同步两个数据源。但是当你必须对一连串音符进行大规模操作时,可能不太方便。
在播放期间,在下一个测量开始之前,会发生以下算法(粗略地):
pattern->startTime <= [current playback second] <= pattern->endTime
。vector<Notes>
地图获取相应度量的measures
。答案 1 :(得分:3)
保持这些矢量排序。
在播放过程中,您可以将指针(索引)保存到最后一个音符播放器的每个向量中。要搜索新笔记,您需要检查每个向量中的以下注释,不需要循环注释。
答案 2 :(得分:1)
保持你的矢量排序,并尝试一下 - 这更重要,你可以在这里得到任何答案。
对于您的所有问题,您应寻求以测试和原型回答,然后您就会知道您是否遇到问题。而且在尝试时,你会看到你通常不会仅仅通过理论看到的东西。
答案 3 :(得分:0)
我的模型或多或少看起来像这样:
您的模型中缺少几个至关重要的概念:
每个音符都有一个“frame”参数,以便我可以计算应该播放音符的时间。
您的模型中缺少几个至关重要的概念:
我建议你看一下lilypond。它是排版软件,但它也是以人类可读文本格式表示音乐的最精确方式之一。
我的问题是我应该如何代表这些笔记以获得最佳表现?
将它们全部放入std::map<Timestamp, Note>
并使用lower_bound / upper_bound查找要播放的片段。或者,只要数据已排序,您就可以在flat std :: vector中二进制搜索它们。
除非你想制作一个“蜂鸣器”,否则制作音乐应用程序要比你想象的要困难得多。我强烈建议尝试另一个项目。