package ch.retorte.intervalmusiccompositor.compilation;
import static ch.retorte.intervalmusiccompositor.commons.ArrayHelper.arrayMerge16bit;
import static ch.retorte.intervalmusiccompositor.commons.Utf8Bundle.getBundle;
import static ch.retorte.intervalmusiccompositor.list.BlendMode.CROSS;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import ch.retorte.intervalmusiccompositor.audiofile.IAudioFile;
import ch.retorte.intervalmusiccompositor.commons.MessageFormatBundle;
import ch.retorte.intervalmusiccompositor.messagebus.DebugMessage;
import ch.retorte.intervalmusiccompositor.playlist.Playlist;
import ch.retorte.intervalmusiccompositor.playlist.PlaylistItem;
import ch.retorte.intervalmusiccompositor.spi.messagebus.MessageProducer;
import ch.retorte.intervalmusiccompositor.util.SoundHelper;
/**
* The {@link Compilation} collects all data needed for the final compilation, that is, the desired intervals, the files, etc, and offers means for generating
* the compilation out of this data.
*
* @author nw
*/
public class Compilation {
private MessageFormatBundle bundle = getBundle("core_imc");
private final SoundHelper soundHelper;
private MessageProducer messageProducer;
public Compilation(SoundHelper soundhelper, MessageProducer messageProducer) {
this.soundHelper = soundhelper;
this.messageProducer = messageProducer;
}
synchronized byte[] generateCompilation(Playlist playlist) throws IOException {
byte[] result = new byte[soundHelper.getSamplesFromSeconds(playlist.getTotalLengthInSeconds())];
addDebugMessage("Generate compilation of length: " + playlist.getTotalLengthInSeconds() + " s");
int currentPosition = 0;
for (PlaylistItem playlistItem : playlist) {
byte[] playlistItemByteArray = getByteArrayFrom(playlistItem);
byte[] blendedPlaylistItemByteArray = soundHelper.linearBlend(playlistItemByteArray, playlist.getBlendTime());
arrayMerge16bit(blendedPlaylistItemByteArray, 0, result, currentPosition, blendedPlaylistItemByteArray.length);
addDebugMessage("Adding to compilation at " + soundHelper.getSecondsFromSamples(currentPosition) + ": " + playlistItem);
currentPosition += blendedPlaylistItemByteArray.length;
if (playlist.getBlendMode() == CROSS) {
currentPosition -= soundHelper.getSamplesFromSeconds(0.5 * playlist.getBlendTime());
}
}
return result;
}
private byte[] getByteArrayFrom(PlaylistItem playlistItem) throws IOException {
if (playlistItem.isSilentBreak()) {
return soundHelper.generateSilenceOfLength((playlistItem.getExtractDurationInSeconds()));
}
AudioInputStream leveledStream = soundHelper.getLeveledStream(playlistItem.getAudioFile().getAudioInputStream(), getVolumeRatioOf(playlistItem));
return soundHelper.getStreamPart(leveledStream, playlistItem.getExtractStartInMilliseconds(), playlistItem.getExtractDurationInMilliseconds());
}
/**
* We try to calculate the volume ratio of the extract we are going to insert into the compilation. This leads to a more homogeneous volume over the
* compilation.
*/
private float getVolumeRatioOf(PlaylistItem playlistItem) {
int maximalAverageAmplitude = Integer.parseInt(bundle.getString("imc.audio.volume.max_average_amplitude"));
int averageAmplitudeWindowSize = Integer.parseInt(bundle.getString("imc.audio.volume.window_size"));
IAudioFile audioFile = playlistItem.getAudioFile();
try {
AudioInputStream streamExtract = soundHelper.getStreamExtract(audioFile.getAudioInputStream(), (int) playlistItem.getExtractStartInSeconds(),
(int) playlistItem.getExtractDurationInSeconds());
float averageAmplitude = soundHelper.getAvgAmplitude(streamExtract, averageAmplitudeWindowSize);
return maximalAverageAmplitude / averageAmplitude;
}
catch (IOException e) {
addDebugMessage("Problems when calculating amplitude: " + e.getMessage());
}
return audioFile.getVolumeRatio();
}
private void addDebugMessage(String message) {
messageProducer.send(new DebugMessage(this, message));
}
}