2010-09-10 2 views
4

Je suis en train d'implémenter une classe de pistes audio et j'ai besoin d'une bonne implémentation de buffer circulaire. J'utilise des shorts pour mes échantillons audio, donc je préférerais utiliser une classe ShortBuffer pour le buffer. Cette piste devra être thread-safe mais je peux garantir qu'un seul thread lira et un autre écrira sur la piste. Mon implémentation actuelle ressemble à ceci (elle ne gère pas le wrapping).ShortBuffer circulaire pour Audio en Java

public class Track { 
    //sample rate 44100, 2 channels with room for 4 seconds 
    private volatile ShortBuffer buffer = ShortBuffer.allocate((44100 * 2) * 4); 
    //keep count of the samples in the buffer 
    private AtomicInteger count = new AtomicInteger(0); 
    private ReentrantLock lock = new ReentrantLock(true); 
    private int readPosition = 0; 

    public int getSampleCount() { 
     int i = count.get(); 
     return i > 0 ? i/2 : 0; 
    } 

    public short[] getSamples(int sampleCount) { 
     short[] samples = new short[sampleCount]; 
     try { 
      lock.tryLock(10, TimeUnit.MILLISECONDS); 
      int writePosition = buffer.position(); 
      buffer.position(readPosition); 
      buffer.get(samples); 
      //set new read position 
      readPosition = buffer.position(); 
      // set back to write position 
      buffer.position(writePosition); 
      count.addAndGet(-sampleCount); 
     } catch (InterruptedException e) { 
      System.err.println("Exception getting samples" + e); 
     } finally { 
      if (lock.isHeldByCurrentThread()) { 
       lock.unlock(); 
      } 
     } 
     return samples; 
    } 

    public void pushSamples(short[] samples) { 
     try { 
      lock.tryLock(10, TimeUnit.MILLISECONDS); 
      buffer.put(samples); 
      count.addAndGet(samples.length); 
     } catch (InterruptedException e) { 
      System.err.println("Exception getting samples" + e); 
     } finally { 
      if (lock.isHeldByCurrentThread()) { 
       lock.unlock(); 
      } 
     } 
    } 
} 

Répondre

3

est ici la solution que je suis venu avec http://pastebin.com/2St01Wzf j'ai décidé qu'il était plus facile d'utiliser une tête et propriété de queue avec un tableau court, au lieu de simplement la position de lecture avec un ShortBuffer. J'ai également pris une idée des classes de collections Java pour détecter quand le tampon est plein. Voici la source, au cas où le pastebin disparaît:

public class Track { 

private static Logger log = LoggerFactory.getLogger(Track.class); 

private final long id = System.nanoTime(); 

// number of channels 
private int channelCount; 

// maximum seconds to buffer 
private int bufferedSeconds = 5; 

private AtomicInteger count = new AtomicInteger(0); 

private ReentrantLock lock; 

private volatile short[] buffer; 

private int capacity = 0; 

private int head = 0; 

private int tail = 0; 

public Track(int samplingRate, int channelCount) { 
    // set the number of channels 
    this.channelCount = channelCount; 
    // size the buffer 
    capacity = (samplingRate * channelCount) * bufferedSeconds; 
    buffer = new short[capacity]; 
    // use a "fair" lock 
    lock = new ReentrantLock(true); 
} 

/** 
* Returns the number of samples currently in the buffer. 
* 
* @return 
*/ 
public int getSamplesCount() { 
    int i = count.get(); 
    return i > 0 ? i/channelCount : 0; 
} 

/** 
* Removes and returns the next sample in the buffer. 
* 
* @return single sample or null if a buffer underflow occurs 
*/ 
public Short remove() { 
    Short sample = null; 
    if (count.get() > 0) { 
     // decrement sample counter 
     count.addAndGet(-1); 
     // reposition the head 
     head = (head + 1) % capacity; 
     // get the sample at the head 
     sample = buffer[head]; 
    } else { 
     log.debug("Buffer underflow"); 
    } 
    return sample; 
} 

/** 
* Adds a sample to the buffer. 
* 
* @param sample 
* @return true if added successfully and false otherwise 
*/ 
public boolean add(short sample) { 
    boolean result = false; 
    if ((count.get() + 1) < capacity) { 
     // increment sample counter 
     count.addAndGet(1); 
     // reposition the tail 
     tail = (tail + 1) % capacity; 
     // add the sample to the tail 
     buffer[tail] = sample; 
     // added! 
     result = true; 
    } else { 
     log.debug("Buffer overflow"); 
    } 
    return result; 
} 

/** 
* Offers the samples for addition to the buffer, if there is enough capacity to 
* contain them they will be added. 
* 
* @param samples 
* @return true if the samples can be added and false otherwise 
*/ 
public boolean offer(short[] samples) { 
    boolean result = false; 
    if ((count.get() + samples.length) <= capacity) { 
     pushSamples(samples); 
     result = true; 
    } 
    return result; 
} 

/** 
* Adds an array of samples to the buffer. 
* 
* @param samples 
*/ 
public void pushSamples(short[] samples) { 
    log.trace("[{}] pushSamples - count: {}", id, samples.length); 
    try { 
     lock.tryLock(10, TimeUnit.MILLISECONDS); 
     for (short sample : samples) { 
      log.trace("Position at write: {}", tail); 
      if (!add(sample)) { 
       log.warn("Sample could not be added"); 
       break; 
      } 
     } 
    } catch (InterruptedException e) { 
     log.warn("Exception getting samples", e); 
    } finally { 
     if (lock.isHeldByCurrentThread()) { 
      lock.unlock(); 
     } 
    } 
} 

/** 
* Returns a single from the buffer. 
* 
* @return 
*/ 
public Short popSample(int channel) { 
    log.trace("[{}] popSample - channel: {}", id, channel); 
    Short sample = null; 
    if (channel < channelCount) { 
     log.trace("Position at read: {}", head); 
     try { 
      lock.tryLock(10, TimeUnit.MILLISECONDS); 
      sample = remove(); 
     } catch (InterruptedException e) { 
      log.warn("Exception getting sample", e); 
     } finally { 
      if (lock.isHeldByCurrentThread()) { 
       lock.unlock(); 
      } 
     } 
    } 
    return sample; 
} 

}

+2

Ceci est vraiment bon. J'utilise ceci comme un exemple pour m'aider à construire une matrice circulaire thread-safe pour les échantillons audio dans Android. Merci d'avoir posté! – chaimp

+1

Pouvez-vous me désigner un code plus complet pour l'enregistrement dans un tampon circulaire? – stoefln