/** * Writes the given samples to the file. * * <p>Note: this function is non-blocking and can be called from within the process tread. * * @param startSample the file-position of the first sample. If the position larger than the * number of samples written so far, the gap will be filled with null samples. * @param audioArray the data to be written to file. */ public void putNext(int startSample, float[] audioArray) { synchronized (processingLock) { if (!started) { return; } if (startSample < samplesDelivered) { Arrays.fill(audioArray, 0.0F); logger.severe("\"startSample\" already delivered."); return; } samplesDelivered = startSample + audioArray.length; if (currentBufferProvider == null) { logger.severe("Current buffer is null."); return; } if (!currentBufferProvider.isDone()) { logger.warning("File buffer not ready."); overflowCount++; return; } SyncBuffer currentBuffer; try { FileWriteTaskResult result = currentBufferProvider.get(); currentBuffer = result.getbuffer(); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, null, ex); return; } FloatBuffer currentFloatBuffer = currentBuffer.asFloatBuffer(); int offset = startSample - samplesProcessed; int toBeWritten = offset + audioArray.length; // Do we need to write samples to the next buffer? int remainingSpace = currentFloatBuffer.remaining(); if (remainingSpace < toBeWritten) { switchBuffers(offset, audioArray, currentBuffer); return; } // Now we have checked all special conditions... we can proceed to the normal work. // 1) Pad with null samples. for (int i = 0; i < offset; i++) { currentFloatBuffer.put(0F); } // 2) append the audioArray currentFloatBuffer.put(audioArray); samplesProcessed = startSample + audioArray.length; } }
/** * Stops writing and starts to close the output file. * * <p>Note: this function is non-blocking and can be called from within the process tread. */ public WriterResult stop() { synchronized (processingLock) { if (!started) { throw new RuntimeException("Attempt to stop altough not started."); } startBuffer.flipFloats(); Future<FileChannel> readerChannel = null; FileChannel channel = null; if (startBufferDone) { // wait for the bussyBufferProvider to terminate streaming. // ToDo: this ought to be done inside LastFileWriteTask (beware of possible deadlocks) if (bussyBufferProvider != null) { if (!bussyBufferProvider.isDone()) { logger.warning("Waiting for a previous buffer to be streamed."); overflowCount++; } try { channel = bussyBufferProvider.get().getChannel(); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, null, ex); } } FileWriteTaskResult result = null; try { result = currentBufferProvider.get(); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, null, ex); } if (result != null) { SyncBuffer buffer = result.getbuffer(); LastFileWriteTask lastFileWriteTask = new LastFileWriteTask(outputFile, channel, buffer); readerChannel = executor.submit(lastFileWriteTask); } } WriterResult result = new WriterResult(startBuffer, readerChannel, samplesProcessed); currentBufferProvider = null; bussyBufferProvider = null; startBuffer = null; started = false; return result; } }
private void switchBuffers(int offset, float[] audioArray, SyncBuffer oldBuffer) { int toBePadded = offset; FloatBuffer oldFloatBuffer = oldBuffer.asFloatBuffer(); // 1) use the remaining space in the current buffer // ...pad the current buffer as much as fits. while ((toBePadded > 0) && (oldFloatBuffer.hasRemaining())) { oldFloatBuffer.put(0F); toBePadded--; samplesProcessed++; } // ...put as much as fits from the audioArray int audioArrayWrittenToOld = 0; if (oldFloatBuffer.hasRemaining() && toBePadded == 0) { audioArrayWrittenToOld = oldFloatBuffer.remaining(); oldFloatBuffer.put(audioArray, 0, audioArrayWrittenToOld); samplesProcessed += audioArrayWrittenToOld; } // 2) put the rest in a new buffer if (!startBufferDone) { // setup the first file buffer bussyBufferProvider = new AlwaysStreamedBuffer(new SyncBuffer(requestedFileBufferSizeFloat)); } else { if (!firstFileBufferDone) { // setup the second file buffer bussyBufferProvider = new AlwaysStreamedBuffer(new SyncBuffer(requestedFileBufferSizeFloat)); } } if (bussyBufferProvider == null) { logger.severe("Next buffer is null!!"); return; } if (!bussyBufferProvider.isDone()) { logger.warning("Next buffer not ready."); overflowCount++; return; } SyncBuffer newBuffer; FileChannel channel; try { FileWriteTaskResult result = bussyBufferProvider.get(); newBuffer = result.getbuffer(); channel = result.getChannel(); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, null, ex); return; } FloatBuffer newFloatBuffer = newBuffer.asFloatBuffer(); // int remainingInNewBuffer = newFloatBuffer.remaining(); int toBeWrittenToNew = toBePadded + audioArray.length - audioArrayWrittenToOld; if (toBeWrittenToNew > remainingInNewBuffer) { logger.severe("File buffer too small for this offset."); } else { for (int i = 0; i < toBePadded; i++) { newFloatBuffer.put(0F); samplesProcessed++; } int restAudioArray = audioArray.length - audioArrayWrittenToOld; newFloatBuffer.put(audioArray, audioArrayWrittenToOld, restAudioArray); samplesProcessed += restAudioArray; } if (startBufferDone) { FileWriteTask fileWriteTask = new FileWriteTask(outputFile, channel, oldBuffer); currentBufferProvider = bussyBufferProvider; bussyBufferProvider = executor.submit(fileWriteTask); firstFileBufferDone = true; } else { // we just have processed the first buffer: currentBufferProvider = bussyBufferProvider; bussyBufferProvider = null; startBufferDone = true; } }