Package ui.sound

Class SampledSound

java.lang.Object
ui.sound.SampledSound

public class SampledSound extends Object
An object to make some sampled sound according to SMSQ/E's SSSS specification. That takes 20 kHz sound, stereo, one byte for each channel.

Bytes are sent to this by "chunks". Each chunk represents a certain number of bytes corresponding to samples that are added to the SSSS. There is a minimum of 2 bytes per chunk (one left, one right) if a single sample is added, and a maximum of (SMSQE SSSS buffer size) bytes if multiple bytes are added to the SSSS.

Internally, each chunk is represented here by an array of bytes. This object creates a very primitive FIFO buffering. This is a achieved via a simple ArrayList of byte[]. If a sample is added to the SSSS, the byte array corresponding to that chunk is added to the end of the ArrayList, and upon playback the first element of the ArrayList is played and removed from the list. (There is an inefficiency in this since each time element 0 is removed from the ArrayList, all other elements are shuffled down. I'm not sure, however, whether this really merits that I implement a real FIFO queue).

Playback is via an independent PlayThread thread. This thread is created once and, when it has nothing to do (no sound is to be played) it will just go to sleep. A new thread is NOT created every time a sound is to be played. The thread feeds data to a SourceDataLine set up here.

The general contract with the SMSQE's SSSS is that whenever a sample is sent to it, SMSQE gets here via the corresponding trap (playsound) and the corresponding chunk is taken from SMSQE's memory and added to the arrayList. Then the independent thread is started or woken, which copies each chunk from this ArrayList to the SourceDataline buffer set up here.

There is a bug in the java SourceDataLine : if one repeatedly sends it small sound samples, the sound is repeated indefintely (in total or in part) until the dataline is closed/stopped. To try to get around this, a special flag may be set that tells that the sound should be stopped if the SSSS is empty, a new vector is introduced into the SSSS in SMSQ/E, that will cause the PlayThread to empty and STOP the SoundDataLine once the queue is empty.

This also handles resampling. The only sound format allowed for the SSSS is 20kHz stereo sound. Some SourceDataLines cannot handle that format (especially under OpenJDK). However, apparently all will handle 22.05 kHz (halve the CD rate), I resample the sound in that case.
  • Constructor Details

    • SampledSound

      public SampledSound(int volume, Warnings warn, String frequency)
      Creates this object, a DataLine object and an independent thread for filling the DataLine.
      Parameters:
      volume - the volume the sound is to have : 0 -100.
      warn - a flag, if true, warn if sound problems may arise in the future.
      frequency - either "22.05" or "20" (any other value will be set to 22.05) : frequency in KHz.
  • Method Details

    • fillPointers

      public void fillPointers(MC68000Cpu cpu)
      Fill in the pointers to the SSSS buffer. Called during SMSQ/E initialization of the SSSS.
      Parameters:
      cpu - the smsqmulator.cpu.MC68000Cpu used.
    • setVolume

      public void setVolume(int percentage)
      Sets the sound volume.
      Parameters:
      percentage - the volume, from 0 (no sound) to 100 (loudest).
    • killSound

      public void killSound(MC68000Cpu cpu)
      Tries to kill the currently played sound. This is called from the emulation thread.
      Parameters:
      cpu - the smsqmulator.cpu.MC68000Cpu used.
    • playSample

      public void playSample(MC68000Cpu cpu)
      This adds a chunk from the SSSS and plays it. It wakes up the PlayThread, if need be (by interrupting it). A1 points to the end of the queue. This is called from the emulation thread.
      Parameters:
      cpu - the cpu used.
    • addChunk

      public void addChunk(byte[] buff)
      Add a chunk.
      Parameters:
      buff - the chunk to add.
    • queryVolume

      public int queryVolume()
      Queries the current volume. NB contrary to documentation, this doesn't work.
      Returns:
      the volume or -1 if line no longer active.
    • getSample

      public final int getSample()
      Get the size of the sample still in the queue
      Returns:
      nbr of bytes still in the queue.
    • closeSound

      public void closeSound(boolean close)
      Signals that once queue is empty sound should be killed: This tries to get around a java bug.
      Parameters:
      close - set to true if the sound should be killed if queue empty.
    • querySoundClose

      public boolean querySoundClose()
      Checks whether sound is set to be killed when queue empty.
      Returns:
      true if the sound should be killed if queue empty.
    • isStillPlaying

      public boolean isStillPlaying(MC68000Cpu cpu)
      Returns true if sound is still playing.
      Parameters:
      cpu -
      Returns: