CompSci.rocks
FRQcsapa

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 Sound class has:
    • private int[] samples — guaranteed not null
  • You're asked to write two unrelated methods:
    • int limitAmplitude(int limit) — clamps values whose absolute value exceeds limit, and reports how many it changed
    • void trimSilenceFromBeginning() — removes leading zero values from the front of samples

Part (a): Writing limitAmplitude(int limit)

The Rule, Broken Down

  1. A value's "amplitude" is its absolute value.
  2. Any value greater than limit gets replaced with limit.
  3. Any value less than -limit gets replaced with -limit.
  4. Every value that gets replaced this way counts toward the number this method returns.

Step-by-Step Approach

  1. Start a counter at 0.
  2. Loop over every index of samples.
  3. If the current value is greater than limit, set it to limit and increment the counter.
  4. Otherwise, if the current value is less than -limit, set it to -limit and increment the counter.
  5. 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 separate ifs — a single value can never be simultaneously greater than a non-negative limit and less than its negation, so the two checks are mutually exclusive. Writing them as else if makes that relationship explicit, though separate ifs would also work here.
  • limit >= 0 (the stated precondition) keeps limit and -limit correctly ordered, so "greater than limit" and "less than -limit" never overlap or leave a gap.
  • Incrementing count inside 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 at limit (like index 6's 2000 in the trace above) must be left alone.
  • Forgetting to increment count in one of the two branches — both the "too high" and "too low" cases count toward the total.
  • Looping with i <= samples.length instead of i < samples.length, which throws an ArrayIndexOutOfBoundsException on the last iteration.

Part (b): Writing trimSilenceFromBeginning()

The Rule, Broken Down

  1. "Silence" means a stored value of 0.
  2. Every leading zero at the very start of samples must be removed.
  3. Everything from the first nonzero value onward is kept, in the same order.
  4. samples itself must end up pointing at this new, shorter array.

Step-by-Step Approach

  1. Walk forward from index 0 until the first nonzero value is found — call that position start.
  2. Create a new array sized samples.length - start.
  3. Copy values from samples, beginning at start, into the new array from its own beginning.
  4. Reassign samples to 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 samples in place — the only option is building a new, correctly-sized array and pointing the field at it.
  • The stated precondition — samples contains at least one nonzero value — is what keeps the while loop safe. Without that guarantee, a fully-zero array would let start walk right off the end of samples and throw an exception.
  • samples[start + i] in the copy loop, not samples[i] — the offset by start is what actually skips the leading zeros; copying from index i alone 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 -14start = 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) or samples.length - start + 1 (reads past the end).
  • Copying with trimmed[i] = samples[i] instead of samples[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 above limit; wrapping that in Math.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.min is 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 the if/else if version above.

Key Takeaways

  • Clamping a value between a lower and upper bound is a two-branch if/else if pattern — or a single nested Math.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 2000 in this problem's own example) to confirm whether a comparison should be strict (>) or inclusive (>=).

Related FRQs