AudioCaptureService.java

package org.hammer.audio;

import javax.sound.sampled.AudioFormat;
import org.hammer.audio.buffer.AudioRingBuffer;
import org.hammer.audio.core.AudioBlock;
import org.hammer.audio.core.AudioFormatDescriptor;

/**
 * Service interface for audio capture and waveform data management.
 *
 * <p>This interface defines the contract for starting/stopping audio capture, retrieving the latest
 * waveform model snapshots, subscribing to the complete block stream and adjusting capture
 * parameters.
 *
 * <p>Thread-safety: Implementations must ensure thread-safe access to all methods. Model snapshots
 * returned by {@link #getLatestModel()} should be immutable or defensive copies to prevent
 * concurrent modification issues.
 *
 * @author refactoring
 */
public interface AudioCaptureService {

  /**
   * Start audio capture.
   *
   * <p>Initializes audio input device and begins capturing audio data in a background thread.
   *
   * @throws IllegalStateException if the service is already started or if the audio device cannot
   *     be initialized
   */
  void start();

  /**
   * Stop audio capture.
   *
   * <p>Gracefully stops the capture thread and releases audio device resources. This method should
   * be idempotent - calling it multiple times should be safe.
   */
  void stop();

  /** Check if audio capture is currently running. */
  boolean isRunning();

  /**
   * Get the latest waveform model snapshot.
   *
   * <p>Returns an immutable snapshot of the current waveform data. This method must be thread-safe
   * and return defensive copies to prevent concurrent modification.
   */
  WaveformModel getLatestModel();

  /** Get the JavaSound format used for capture, or {@code null} if not initialized. */
  AudioFormat getFormat();

  /**
   * Set the divisor for buffer size calculation.
   *
   * @param divisor the divisor value (must be >= 1)
   */
  void setDivisor(int divisor);

  /** Get the current divisor value. */
  int getDivisor();

  /** Recompute layout/coordinates based on current panel dimensions. */
  void recomputeLayout(int width, int height);

  /**
   * @return the audio-domain format descriptor, or {@code null} if the service has not been started
   *     yet. Unlike {@link #getFormat()}, this is platform-internal and free of JavaSound types.
   */
  default AudioFormatDescriptor getDescriptor() {
    return null;
  }

  /**
   * @return the most recently captured {@link AudioBlock}, or {@code null} if no audio has been
   *     captured yet. The returned block is immutable and safe to share.
   */
  default AudioBlock getLatestBlock() {
    return null;
  }

  /**
   * Subscribe to every block published after registration.
   *
   * <p>Listeners run on the source producer thread and must return promptly. A recorder therefore
   * only enqueues in the callback and performs serialization on its own worker.
   *
   * @throws UnsupportedOperationException if an implementation does not expose a complete stream
   */
  default AudioBlockSubscription subscribe(AudioBlockListener listener) {
    throw new UnsupportedOperationException(
        getClass().getName() + " does not support complete audio-block subscriptions");
  }

  /**
   * @return the producer/consumer ring buffer fed by the capture thread, or {@code null} if the
   *     service has not been started yet. Downstream DSP and analysis modules consume blocks from
   *     this buffer.
   */
  default AudioRingBuffer<AudioBlock> getRingBuffer() {
    return null;
  }
}