1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include <cmath>
19
20#include "EmulationTiming.hxx"
21
22namespace {
23 constexpr uInt32 AUDIO_HALF_FRAMES_PER_FRAGMENT = 1;
24
25 uInt32 discreteDivCeil(uInt32 n, uInt32 d)
26 {
27 return n / d + ((n % d == 0) ? 0 : 1);
28 }
29}
30
31// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32EmulationTiming::EmulationTiming(FrameLayout frameLayout, ConsoleTiming consoleTiming) :
33 myFrameLayout(frameLayout),
34 myConsoleTiming(consoleTiming),
35 myPlaybackRate(44100),
36 myPlaybackPeriod(512),
37 myAudioQueueExtraFragments(1),
38 myAudioQueueHeadroom(2),
39 mySpeedFactor(1)
40{
41 recalculate();
42}
43
44// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
45EmulationTiming& EmulationTiming::updateFrameLayout(FrameLayout frameLayout)
46{
47 myFrameLayout = frameLayout;
48 recalculate();
49
50 return *this;
51}
52
53// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
54EmulationTiming& EmulationTiming::updateConsoleTiming(ConsoleTiming consoleTiming)
55{
56 myConsoleTiming = consoleTiming;
57 recalculate();
58
59 return *this;
60}
61
62// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
63EmulationTiming& EmulationTiming::updatePlaybackRate(uInt32 playbackRate)
64{
65 myPlaybackRate = playbackRate;
66 recalculate();
67
68 return *this;
69}
70
71// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
72EmulationTiming& EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod)
73{
74 myPlaybackPeriod = playbackPeriod;
75 recalculate();
76
77 return *this;
78}
79
80// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
81EmulationTiming& EmulationTiming::updateAudioQueueExtraFragments(uInt32 audioQueueExtraFragments)
82{
83 myAudioQueueExtraFragments = audioQueueExtraFragments;
84 recalculate();
85
86 return *this;
87}
88
89// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
90EmulationTiming& EmulationTiming::updateAudioQueueHeadroom(uInt32 audioQueueHeadroom)
91{
92 myAudioQueueHeadroom = audioQueueHeadroom;
93 recalculate();
94
95 return *this;
96}
97
98// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
99EmulationTiming& EmulationTiming::updateSpeedFactor(float speedFactor)
100{
101 mySpeedFactor = static_cast<double>(speedFactor);
102 recalculate();
103
104 return *this;
105}
106
107// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
108uInt32 EmulationTiming::maxCyclesPerTimeslice() const
109{
110 return myMaxCyclesPerTimeslice;
111}
112
113// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114uInt32 EmulationTiming::minCyclesPerTimeslice() const
115{
116 return myMinCyclesPerTimeslice;
117}
118
119// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120uInt32 EmulationTiming::linesPerFrame() const
121{
122 return myLinesPerFrame;
123}
124
125// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
126uInt32 EmulationTiming::cyclesPerFrame() const
127{
128 return myCyclesPerFrame;
129}
130
131// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
132uInt32 EmulationTiming::cyclesPerSecond() const
133{
134 return myCyclesPerSecond;
135}
136
137// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
138uInt32 EmulationTiming::audioFragmentSize() const
139{
140 return myAudioFragmentSize;
141}
142
143// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
144uInt32 EmulationTiming::audioSampleRate() const
145{
146 return myAudioSampleRate;
147}
148
149// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
150uInt32 EmulationTiming::audioQueueCapacity() const
151{
152 return myAudioQueueCapacity;
153}
154
155// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156uInt32 EmulationTiming::prebufferFragmentCount() const
157{
158 return myPrebufferFragmentCount;
159}
160
161// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162void EmulationTiming::recalculate()
163{
164 switch (myFrameLayout) {
165 case FrameLayout::ntsc:
166 myLinesPerFrame = 262;
167 break;
168
169 case FrameLayout::pal:
170 myLinesPerFrame = 312;
171 break;
172
173 default:
174 throw runtime_error("invalid frame layout");
175 }
176
177 switch (myConsoleTiming) {
178 case ConsoleTiming::ntsc:
179 myAudioSampleRate = uInt32(round(mySpeedFactor * 262 * 76 * 60) / 38);
180 break;
181
182 case ConsoleTiming::pal:
183 case ConsoleTiming::secam:
184 myAudioSampleRate = uInt32(round(mySpeedFactor * 312 * 76 * 50) / 38);
185 break;
186
187 default:
188 throw runtime_error("invalid console timing");
189 }
190
191 myCyclesPerSecond = myAudioSampleRate * 38;
192
193 myCyclesPerFrame = 76 * myLinesPerFrame;
194 myMaxCyclesPerTimeslice = uInt32(round(mySpeedFactor * myCyclesPerFrame * 2));
195 myMinCyclesPerTimeslice = uInt32(round(mySpeedFactor * myCyclesPerFrame / 2));
196 myAudioFragmentSize = uInt32(round(mySpeedFactor * AUDIO_HALF_FRAMES_PER_FRAGMENT * myLinesPerFrame));
197
198 myPrebufferFragmentCount = discreteDivCeil(
199 myPlaybackPeriod * myAudioSampleRate,
200 myAudioFragmentSize * myPlaybackRate
201 ) + myAudioQueueHeadroom;
202
203 myAudioQueueCapacity = std::max(
204 myPrebufferFragmentCount,
205 discreteDivCeil(myMaxCyclesPerTimeslice * myAudioSampleRate, myAudioFragmentSize * myCyclesPerSecond)
206 ) + myAudioQueueExtraFragments;
207}
208