private void updateWaveGraphs(AVClipNDir vc0, int vClapPosIdx, AVClipNDir acd, int aClapPosIdx)
		throws IOException {
		float vShift = (Float)controls().getVideoShift().getValue();
		WaveGraphPanel vGraphPanel = controls().getVideoGraphPanel();
		WaveGraphPanel aGraphPanel = controls().getAudioGraphPanel();
		vGraphPanel.nullifyLoader();
		aGraphPanel.nullifyLoader();
		AsyncLoader audioCacheCam = null, audioCacheExt = null;
		float vTrimStart = 0.0f, aTrimStart = 0.0f, trimDuration = -1.0f, vClipLen = -1.0f, aClipLen = -1.0f,
			graphTimeSpan = -1.0f, vStartGraphOffset = 0.0f, aStartGraphOffset = 0.0f;
		AVClipNDir vcd = null;
		AVEngine ave = getAVEngine();
		if (vc0 != null) {
			vcd = new AVClipNDir(new AVClip(vc0.clip, vc0.clip.getOffset(), vc0.clip.getDuration(),
				new int[]{vc0.clip.getMeta().findFirstIndex(ave.metaKeyName(AVEngine.MetaKey.CODEC_TYPE),
				ave.metaValueName(AVEngine.MetaValue.CODEC_TYPE_AUDIO))}), vc0.dir);
			vClipLen = AVClip.duration(vcd.clip, AVEngine.MetaValue.CODEC_TYPE_AUDIO, ave);
			_l.log(Level.FINE, String.format("vc0.clip.getDuration() = %.6f, vClipLen = %.6f", vc0.clip.getDuration(), vClipLen));
			if (acd == null) {
				trimDuration = vClipLen;
				graphTimeSpan = vClipLen;
			}
			File camAudio = File.createTempFile("pslm", "." + AsyncLoader.FILE_FORMAT);
			camAudio.deleteOnExit();
			_l.log(Level.FINE, "Transcode audio from " + vcd.dir.getPath() + "/" + vcd.clip.getName() + " to temporary file " + camAudio.getPath());
			AVClip sourceClip = new AVClip(vcd.clip, 0.0f); // extract the whole clip. In vcd.clip, the Offset represents the clap position.
			ave.transcode(vcd.dir, sourceClip, null, null, null, null, camAudio.getPath());
			_l.log(Level.FINE, "Create AsyncLoader for file "+camAudio.getPath());
			audioCacheCam = new AsyncLoader(new FileInputStream(camAudio.getPath()));
		}
		else _l.log(Level.FINE, "video clip is null");
		if (acd != null) {
			aClipLen = AVClip.duration(acd.clip, AVEngine.MetaValue.CODEC_TYPE_AUDIO, ave);
			if (vcd == null) {
				trimDuration = aClipLen;
				graphTimeSpan = aClipLen;
			}
			StringBuilder p = new StringBuilder(acd.dir.getPath());
			p.append(System.getProperty("file.separator"));
			p.append(acd.clip.getName());
			_l.log(Level.FINE, "Create AsyncLoader for file " + p.toString());
			audioCacheExt = new AsyncLoader(new FileInputStream(p.toString()));
		}
		else _l.log(Level.FINE, "audio clip is null");
		if (vcd != null && acd != null) {
			TrimValues trimValues = new TrimValues(vClipLen, vcd.clip.getOffset(),
					vShift,	aClipLen, acd.clip.getOffset());
			vTrimStart = trimValues.getVideoStart();
			aTrimStart = trimValues.getAudioStart();
			trimDuration = trimValues.getDuration();
			_l.log(Level.FINE, String.format("video trim start = %.6f, audio trim start = %.6f, trim duration = %.6f",
					vTrimStart, aTrimStart, trimDuration));
			float graphBeforeClap, graphAfterClap = Math.max(vClipLen - vcd.clip.getOffset(), aClipLen - acd.clip.getOffset());
			if (vcd.clip.getOffset() > acd.clip.getOffset()) {
				graphBeforeClap = vcd.clip.getOffset();
				aStartGraphOffset = vcd.clip.getOffset() - acd.clip.getOffset();
			}
			else {
				graphBeforeClap = acd.clip.getOffset();
				vStartGraphOffset = acd.clip.getOffset() - vcd.clip.getOffset();
			}
			graphTimeSpan = graphBeforeClap + graphAfterClap;
			_l.log(Level.FINE, String.format("graphTimeSpan = %f", graphTimeSpan));
		}
		if (vcd != null) {
			float[] vClapCandidates = getClapList(vc0.clip.getEvents());
			vGraphPanel.prepareGraph(audioCacheCam, vTrimStart, trimDuration,
				vStartGraphOffset, graphTimeSpan, vClapCandidates, vClapPosIdx);
		}
		if (acd != null) {
			float[] aClapCandidates = getClapList(acd.clip.getEvents());
			aGraphPanel.prepareGraph(audioCacheExt, aTrimStart, trimDuration,
				aStartGraphOffset, graphTimeSpan, aClapCandidates, aClapPosIdx);
		}
	}