Sound: 2011 FRQ 1
A step-by-step solution to the 2011 AP CSA FRQ 1 (Sound), covering clamping array values with conditional logic and rebuilding an array to trim leading zeros in Java.
Digital audio gets modeled as nothing more than an array of integers in this AP Computer Science A free-response question, and the two methods you're asked to write don't even work together — they're two separate, unrelated operations on that same array.
What This FRQ Tests
- AP CSA units: Unit 3 (Boolean Expressions and if Statements), Unit 4 (Iteration), and Unit 6 (Array)
- Core skill: looping through an array while conditionally modifying values in place
- Secondary skill: building a brand-new array of a computed, shorter length and copying values into it
- Official category: "Methods and Control Structures" — always FRQ 1 on the AP CSA exam
The Setup
- The given
Soundclass has:private int[] samples— guaranteed notnull
- You're asked to write two unrelated methods:
int limitAmplitude(int limit)— clamps values whose absolute value exceedslimit, and reports how many it changedvoid trimSilenceFromBeginning()— removes leading zero values from the front ofsamples
Part (a): Writing limitAmplitude(int limit)
The Rule, Broken Down
- A value's "amplitude" is its absolute value.
- Any value greater than
limitgets replaced withlimit. - Any value less than
-limitgets replaced with-limit. - Every value that gets replaced this way counts toward the number this method returns.
Step-by-Step Approach
- Start a counter at
0. - Loop over every index of
samples. - If the current value is greater than
limit, set it tolimitand increment the counter. - Otherwise, if the current value is less than
-limit, set it to-limitand increment the counter. - After the loop finishes, return the counter.
The Code
public int limitAmplitude(int limit)
{
int count = 0;
for (int i = 0; i < samples.length; i++)
{
if (samples[i] > limit)
{
samples[i] = limit;
count++;
}
else if (samples[i] < -limit)
{
samples[i] = -limit;
count++;
}
}
return count;
}
Why Each Piece Matters
else if, not two separateifs — a single value can never be simultaneously greater than a non-negativelimitand less than its negation, so the two checks are mutually exclusive. Writing them aselse ifmakes that relationship explicit, though separateifs would also work here.limit >= 0(the stated precondition) keepslimitand-limitcorrectly ordered, so "greater thanlimit" and "less than-limit" never overlap or leave a gap.- Incrementing
countinside each branch, right where the value is actually changed, guarantees the counter only tracks values that were genuinely modified — not just values that happened to be examined.
Tracing the Example
Using the question's own array and limitAmplitude(2000):
| Index | Value | Rule applied | Result |
|---|---|---|---|
| 0 | 40 | within bounds | 40 |
| 1 | 2532 | > 2000 |
2000 |
| 2 | 17 | within bounds | 17 |
| 3 | -2300 | < -2000 |
-2000 |
| 4 | -17 | within bounds | -17 |
| 5 | -4000 | < -2000 |
-2000 |
| 6 | 2000 | not > 2000 (equal) |
2000 (unchanged) |
| 7 | 1048 | within bounds | 1048 |
| 8 | -420 | within bounds | -420 |
| 9 | 33 | within bounds | 33 |
| 10 | 15 | within bounds | 15 |
| 11 | -32 | within bounds | -32 |
| 12 | 2030 | > 2000 |
2000 |
| 13 | 3223 | > 2000 |
2000 |
Five values changed (indexes 1, 3, 5, 12, 13), so numChanges is 5, and the resulting array reads 40 2000 17 -2000 -17 -2000 2000 1048 -420 33 15 -32 2000 2000 — matching the question exactly, including index 6, which stays 2000 because the check is strictly >, not >=.
Common Mistakes to Avoid
- Using
>=/<=instead of>/<. A value exactly atlimit(like index 6's2000in the trace above) must be left alone. - Forgetting to increment
countin one of the two branches — both the "too high" and "too low" cases count toward the total. - Looping with
i <= samples.lengthinstead ofi < samples.length, which throws anArrayIndexOutOfBoundsExceptionon the last iteration.
Part (b): Writing trimSilenceFromBeginning()
The Rule, Broken Down
- "Silence" means a stored value of
0. - Every leading zero at the very start of
samplesmust be removed. - Everything from the first nonzero value onward is kept, in the same order.
samplesitself must end up pointing at this new, shorter array.
Step-by-Step Approach
- Walk forward from index
0until the first nonzero value is found — call that positionstart. - Create a new array sized
samples.length - start. - Copy values from
samples, beginning atstart, into the new array from its own beginning. - Reassign
samplesto refer to the new array.
The Code
public void trimSilenceFromBeginning()
{
int start = 0;
while (samples[start] == 0)
{
start++;
}
int[] trimmed = new int[samples.length - start];
for (int i = 0; i < trimmed.length; i++)
{
trimmed[i] = samples[start + i];
}
samples = trimmed;
}
Why Each Piece Matters
- Java arrays have a fixed length once created. There's no way to "delete from the front" of
samplesin place — the only option is building a new, correctly-sized array and pointing the field at it. - The stated precondition —
samplescontains at least one nonzero value — is what keeps thewhileloop safe. Without that guarantee, a fully-zero array would letstartwalk right off the end ofsamplesand throw an exception. samples[start + i]in the copy loop, notsamples[i]— the offset bystartis what actually skips the leading zeros; copying from indexialone would just duplicate the original leading silence.
Tracing the Example
Using the question's own 16-element array:
| Step | Result |
|---|---|
| Search for first nonzero | indexes 0-3 are 0; index 4 is -14 → start = 4 |
| New array size | 16 - 4 = 12 |
Copy samples[4..15] into trimmed[0..11] |
-14, 0, -35, -39, 0, -7, 16, 32, 37, 29, 0, 0 |
The resulting array — -14 0 -35 -39 0 -7 16 32 37 29 0 0 — matches the question's expected output exactly, including the zeros that survive inside the array (only leading zeros are removed).
Common Mistakes to Avoid
- Off-by-one on the new array's size, e.g.
samples.length - start - 1(loses the last element) orsamples.length - start + 1(reads past the end). - Copying with
trimmed[i] = samples[i]instead ofsamples[start + i]— this copies from the original beginning, defeating the whole purpose of the method. - Forgetting the final
samples = trimmed;reassignment, which leaves the instance variable pointing at the untouched original array.
Notes: A Method Not on the AP CSA Quick Reference Sheet
Math.max and Math.min aren't on the Java Quick Reference sheet (only abs, pow, sqrt, and random from the Math class are listed there), but they're completely valid to use on the real exam. They let part (a) collapse into a single clamping expression instead of two branches:
public int limitAmplitude(int limit)
{
int count = 0;
for (int i = 0; i < samples.length; i++)
{
int clamped = Math.max(-limit, Math.min(limit, samples[i]));
if (clamped != samples[i])
{
count++;
}
samples[i] = clamped;
}
return count;
}
Math.min(limit, samples[i])first caps the value so it's never abovelimit; wrapping that inMath.max(-limit, ...)then also raises it so it's never below-limit.- This isn't a case of one approach being "allowed" and the other not — both are correct Java, and AP CSA graders accept either. The only real tradeoff with
Math.max/Math.minis that you can't look their exact behavior up on the reference sheet during the exam if you second-guess yourself, the way you could with theif/else ifversion above.
Key Takeaways
- Clamping a value between a lower and upper bound is a two-branch
if/else ifpattern — or a single nestedMath.max(low, Math.min(high, value))expression if you're comfortable with it. - Java arrays can't be resized in place — "removing" elements from the front or back always means creating a new array of the right length and copying into it.
- Always trace a value that lands exactly on a boundary (like the
2000in this problem's own example) to confirm whether a comparison should be strict (>) or inclusive (>=).