blob: 9d17efbe86efb3cd0d6bf9bee238e057067d41e8 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// readaheadmanager.h
// Created 8/2/2009 by RJ Ryan (rryan@mit.edu)
#ifndef READAHEADMANGER_H
#define READAHEADMANGER_H
#include <QLinkedList>
#include <QList>
#include <QPair>
#include "util/types.h"
#include "util/math.h"
#include "engine/cachingreader/cachingreader.h"
class LoopingControl;
class RateControl;
// ReadAheadManager is a tool for keeping track of the engine's current position
// in a file. In the case that the engine needs to read ahead of the current
// play position (for example, to feed more samples into a library like
// SoundTouch) then this will keep track of how many samples the engine has
// consumed. The getNextSamples() method encapsulates the logic of determining
// whether to take a loop or jump into a single method. Whenever the Engine
// seeks or the current play position is invalidated somehow, the Engine must
// call notifySeek to inform the ReadAheadManager to reset itself to the seek
// point.
class ReadAheadManager {
public:
ReadAheadManager(); // Only for testing: ReadAheadManagerMock
ReadAheadManager(CachingReader* reader,
LoopingControl* pLoopingControl);
virtual ~ReadAheadManager();
// Call this method to fill buffer with requested_samples out of the
// lookahead buffer. Provide rate as dRate so that the manager knows the
// direction the audio is progressing in. Returns the total number of
// samples read into buffer. Note that it is very common that the total
// samples read is less than the requested number of samples.
virtual SINT getNextSamples(double dRate, CSAMPLE* buffer, SINT requested_samples);
// Used to add a new EngineControls that ReadAheadManager will use to decide
// which samples to return.
void addLoopingControl();
void addRateControl(RateControl* pRateControl);
// Get the current read-ahead position in samples.
// unused in Mixxx, but needed for testing
virtual inline double getPlaypos() const {
return m_currentPosition;
}
virtual void notifySeek(double seekPosition);
// hintReader allows the ReadAheadManager to provide hints to the reader to
// indicate that the given portion of a song is about to be read.
virtual void hintReader(double dRate, HintVector* hintList);
virtual double getFilePlaypositionFromLog(
double currentFilePlayposition,
double numConsumedSamples);
private:
// An entry in the read log indicates the virtual playposition the read
// began at and the virtual playposition it ended at.
struct ReadLogEntry {
double virtualPlaypositionStart;
double virtualPlaypositionEndNonInclusive;
ReadLogEntry(double virtualPlaypositionStart,
double virtualPlaypositionEndNonInclusive) {
this->virtualPlaypositionStart = virtualPlaypositionStart;
this->virtualPlaypositionEndNonInclusive =
virtualPlaypositionEndNonInclusive;
}
bool direction() const {
// NOTE(rryan): We try to avoid 0-length ReadLogEntry's when
// possible but they have happened in the past. We treat 0-length
// ReadLogEntry's as forward reads because this prevents them from
// being interpreted as a seek in the common case.
return virtualPlaypositionStart <= virtualPlaypositionEndNonInclusive;
}
double length() const {
return fabs(virtualPlaypositionEndNonInclusive -
virtualPlaypositionStart);
}
// Moves the start position forward or backward (depending on
// direction()) by numSamples.
// Caller should check if length() is 0 after consumption in
// order to expire the ReadLogEntry.
double advancePlayposition(double* pNumConsumedSamples) {
double available = math_min(*pNumConsumedSamples, length());
virtualPlaypositionStart += (direction() ? 1 : -1) * available;
*pNumConsumedSamples -= available;
return virtualPlaypositionStart;
}
bool merge(const ReadLogEntry& other) {
// Allow 0-length ReadLogEntry's to merge regardless of their
// direction if they have the right start point.
if ((other.length() == 0 || direction() == other.direction()) &&
virtualPlaypositionEndNonInclusive == other.virtualPlaypositionStart) {
virtualPlaypositionEndNonInclusive =
other.virtualPlaypositionEndNonInclusive;
return true;
}
return false;
}
};
// virtualPlaypositionEnd is the first sample in the direction that was read
// that was NOT read as part of this log entry. This is to simplify the
void addReadLogEntry(double virtualPlaypositionStart,
double virtualPlaypositionEndNonInclusive);
LoopingControl* m_pLoopingControl;
RateControl* m_pRateControl;
QLinkedList<ReadLogEntry> m_readAheadLog;
double m_currentPosition;
CachingReader* m_pReader;
CSAMPLE* m_pCrossFadeBuffer;
bool m_cacheMissHappened;
};
#endif // READAHEADMANGER_H
|