448. Design Audio Buffers Across Different Stages
Asked in
Design Audio Buffers Across Different Stages
Design an in-memory system that moves audio buffers through an ordered sequence of processing stages. Every stage can process at most a fixed number of audio frames during each second.

A buffer enters the first stage and moves through every stage in order. It is completed after all of its frames leave the final stage.

Class

AudioBufferPipeline

Constructor

Create Audio Buffer Pipeline

public AudioBufferPipeline(int framesPerSecond, List<String> stageNames)

  • framesPerSecond is the maximum number of frames that each stage can process during one second.
  • stageNames contains the processing stages in the order in which every audio frame must visit them.
  • Initializes the pipeline with no audio buffers.

Methods

Add Audio Buffer

public boolean addBuffer(String bufferId, int frameCount)

  • Adds a new audio buffer containing frameCount frames to the end of the first stage's queue.
  • Returns true when the buffer is added.
  • Returns false when bufferId has already been used. In this case, the pipeline remains unchanged.

Process One Second

public List<String> processSecond()

  • Advances the pipeline by exactly one second.
  • Every stage processes exactly the smaller of framesPerSecond and the number of frames that were waiting at that stage when the method started.
  • Returns the identifiers of buffers completed during this second.
  • If several buffers complete together, their identifiers are returned in their original submission order.
  • Returns an empty list when no buffer completes.

Get Stage Loads

public List<String> getStageLoads()

  • Returns the current number of waiting frames at every stage.
  • Each result uses the format "stageName,queuedFrames".
  • Results are returned in the same order as stageNames.
  • This method does not change the pipeline.

Processing Rules

  • Buffers and their frames are processed in first-in, first-out order.
  • A later buffer cannot move ahead of an earlier buffer.
  • All stages operate simultaneously during a call to processSecond.
  • Frames processed by one stage are transferred to the next stage only at the end of the current second.
  • Transferred frames cannot be processed by the next stage until the next call to processSecond.
  • If a stage cannot process an entire buffer during one second, it processes as many frames as its remaining capacity allows. The other frames remain at the front of its queue.
  • Different portions of the same buffer may be present at different stages.
  • Processing a buffer does not change its number of frames or their order.
  • A buffer completes only when its final frame leaves the last stage.
  • A buffer identifier remains reserved after the buffer completes and cannot be used again.
  • If a stage finishes one buffer and still has capacity, it continues processing the next buffer that was waiting when the method started.
  • Stage queues have no capacity limit in this version of the system.
  • The system does not simulate frame loss, processing failures, or audio content changes.
  • All public method calls are made sequentially.

Constraints

  • 1 ≤ framesPerSecond ≤ 1,000,000,000
  • 1 ≤ stageNames.size() ≤ 50
  • 1 ≤ stageNames.get(i).length() ≤ 100
  • 1 ≤ bufferId.length() ≤ 100
  • 1 ≤ frameCount ≤ 1,000,000,000
  • All stage names are distinct.
  • Stage names and buffer identifiers contain only letters, digits, underscores, and hyphens.
  • Stage names and buffer identifiers are case-sensitive.
  • At most 100,000 buffers will be successfully added.
  • At most 100,000 calls will be made to processSecond.
  • Input parameters are never null.
  • Accumulated frame counts must be stored using 64-bit integers.

Examples

Example 1

AudioBufferPipeline(framesPerSecond = 4, stageNames = ["decode", "filter", "playback"])

Creates an empty three-stage pipeline in which each stage processes at most four frames per second.

addBuffer(bufferId = "voice", frameCount = 6)

Output: true

processSecond()

Output: []

Four frames move from decode to filter.

getStageLoads()

Output: ["decode,2", "filter,4", "playback,0"]

processSecond()

Output: []

getStageLoads()

Output: ["decode,0", "filter,2", "playback,4"]

processSecond()

Output: []

The first four frames leave the final stage, while the remaining two frames enter it.

processSecond()

Output: ["voice"]

The final two frames leave playback, so the buffer completes.

Example 2

AudioBufferPipeline(framesPerSecond = 5, stageNames = ["clean", "encode"])

addBuffer(bufferId = "intro", frameCount = 2)

Output: true

addBuffer(bufferId = "speech", frameCount = 3)

Output: true

processSecond()

Output: []

Both buffers move from clean to encode.

processSecond()

Output: ["intro", "speech"]

Both buffers leave the final stage. Their identifiers are returned in submission order.

addBuffer(bufferId = "intro", frameCount = 1)

Output: false

The identifier "intro" cannot be reused after completion.

Example 3

AudioBufferPipeline(framesPerSecond = 3, stageNames = ["playback"])

addBuffer(bufferId = "music", frameCount = 7)

Output: true

processSecond()

Output: []

getStageLoads()

Output: ["playback,4"]

processSecond()

Output: []

getStageLoads()

Output: ["playback,1"]

processSecond()

Output: ["music"]

The seventh and final frame leaves the only stage during the third second.



Please use Laptop/Desktop or any other large screen to add/edit code.