diff audioio/AudioCallbackPlaySource.cpp @ 4:5865094175ea

* Fix update and play limits for play-selection mode when not looping * Fix playback in loop mode when no selection -- but the GUI update for this is still wrong on the flyback * Various fixes and improvements to making selections, particularly during playback * Draw selection under non-opaque non-scrollable layers, so as to improve cacheing * Show selection limits as text when drawing selection * Allow user to find missing audio files when loading session * Cross-fade selections when in play-selection mode -- mostly. We don't cross-fade on a processing block boundary, and unfortunately with short selections the selection boundary is quite likely to coincide with a block boundary.
author Chris Cannam
date Wed, 25 Jan 2006 17:46:28 +0000
parents 75c3ea1c3a32
children 2edc0757ca75
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp	Tue Jan 24 16:20:58 2006 +0000
+++ b/audioio/AudioCallbackPlaySource.cpp	Wed Jan 25 17:46:28 2006 +0000
@@ -35,6 +35,7 @@
     m_playing(false),
     m_exiting(false),
     m_bufferedToFrame(0),
+    m_lastModelEndFrame(0),
     m_outputLeft(0.0),
     m_outputRight(0.0),
     m_slowdownCounter(0),
@@ -75,6 +76,9 @@
     m_mutex.lock();
 
     m_models.insert(model);
+    if (model->getEndFrame() > m_lastModelEndFrame) {
+	m_lastModelEndFrame = model->getEndFrame();
+    }
 
     bool buffersChanged = false, srChanged = false;
 
@@ -164,6 +168,13 @@
 	m_sourceSampleRate = 0;
     }
 
+    size_t lastEnd = 0;
+    for (std::set<Model *>::const_iterator i = m_models.begin();
+	 i != m_models.end(); ++i) {
+	if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame();
+    }
+    m_lastModelEndFrame = lastEnd;
+
     m_audioGenerator->removeModel(model);
 
     m_mutex.unlock();
@@ -181,6 +192,8 @@
 	m_converter = 0;
     }
 
+    m_lastModelEndFrame = 0;
+
     m_audioGenerator->clearModels();
 
     m_sourceSampleRate = 0;
@@ -191,6 +204,22 @@
 void
 AudioCallbackPlaySource::play(size_t startFrame)
 {
+    if (m_viewManager->getPlaySelectionMode()) {
+	ViewManager::SelectionList selections = m_viewManager->getSelections();
+	ViewManager::SelectionList::iterator i = selections.begin();
+	if (i != selections.end()) {
+	    if (startFrame < i->getStartFrame()) {
+		startFrame = i->getStartFrame();
+	    } else {
+		ViewManager::SelectionList::iterator j = selections.end();
+		--j;
+		if (startFrame >= j->getEndFrame()) {
+		    startFrame = i->getStartFrame();
+		}
+	    }
+	}
+    }
+
     // The fill thread will automatically empty its buffers before
     // starting again if we have not so far been playing, but not if
     // we're just re-seeking.
@@ -211,6 +240,7 @@
 
     m_playing = true;
     m_condition.wakeAll();
+    emit playStatusChanged(m_playing);
 }
 
 void
@@ -218,6 +248,7 @@
 {
     m_playing = false;
     m_condition.wakeAll();
+    emit playStatusChanged(m_playing);
 }
 
 void
@@ -229,17 +260,13 @@
 	    getRingBuffer(c).reset();
 	}
 	m_mutex.unlock();
+	m_condition.wakeAll();
     }
 }
 
 void
 AudioCallbackPlaySource::playLoopModeChanged()
 {
-    m_mutex.lock();
-    for (size_t c = 0; c < m_bufferCount; ++c) {
-	getRingBuffer(c).reset();
-    }
-    m_mutex.unlock();
 }
 
 void
@@ -251,6 +278,7 @@
 	    getRingBuffer(c).reset();
 	}
 	m_mutex.unlock();
+	m_condition.wakeAll();
     }
 }
 
@@ -317,7 +345,15 @@
 
     size_t framePlaying = bufferedFrame;
     if (framePlaying > latency) framePlaying -= latency;
-    else framePlaying = 0;
+    else {
+	//!!! Not right
+	if (m_viewManager->getPlayLoopMode() &&
+	    !m_viewManager->getPlaySelectionMode()) {
+	    framePlaying += m_lastModelEndFrame;
+	    if (framePlaying > latency) framePlaying -= latency;
+	    else framePlaying = 0;
+	}
+    }
 
     if (!m_viewManager->getPlaySelectionMode()) {
 	return framePlaying;
@@ -330,26 +366,41 @@
 
     ViewManager::SelectionList::const_iterator i;
 
+    i = selections.begin();
+    size_t rangeStart = i->getStartFrame();
+
+    i = selections.end();
+    --i;
+    size_t rangeEnd = i->getEndFrame();
+
     for (i = selections.begin(); i != selections.end(); ++i) {
 	if (i->contains(bufferedFrame)) break;
     }
 
     size_t f = bufferedFrame;
 
-    std::cerr << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << std::endl;
+//    std::cerr << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl;
 
     if (i == selections.end()) {
 	--i;
 	if (i->getEndFrame() + latency < f) {
-	    return framePlaying;
+//    std::cerr << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl;
+
+	    if (!m_viewManager->getPlayLoopMode() && (framePlaying > rangeEnd)) {
+//		std::cerr << "STOPPING" << std::endl;
+		stop();
+		return rangeEnd;
+	    } else {
+		return framePlaying;
+	    }
 	} else {
-	    std::cerr << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl;
+//	    std::cerr << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl;
 	    latency -= (f - i->getEndFrame());
 	    f = i->getEndFrame();
 	}
     }
 
-    std::cerr << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl;
+//    std::cerr << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl;
 
     while (latency > 0) {
 	size_t offset = f - i->getStartFrame();
@@ -615,7 +666,7 @@
     }
     
     if (space == 0) return;
-    
+
 #ifdef DEBUG_AUDIO_PLAY_SOURCE
     std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl;
 #endif
@@ -818,6 +869,8 @@
 	chunkSize = count - processed;
 	nextChunkStart = chunkStart + chunkSize;
 
+	size_t fadeIn = 0, fadeOut = 0;
+
 	if (useSelection) {
 	    
 	    Selection selection =
@@ -827,6 +880,7 @@
 		if (m_viewManager->getPlayLoopMode()) {
 		    selection = *m_viewManager->getSelections().begin();
 		    chunkStart = selection.getStartFrame();
+		    fadeIn = 50;
 		}
 	    }
 
@@ -839,13 +893,29 @@
 
 		if (chunkStart < selection.getStartFrame()) {
 		    chunkStart = selection.getStartFrame();
+		    fadeIn = 50;
 		}
 
-		nextChunkStart = std::min(chunkStart + chunkSize,
-					  selection.getEndFrame());
+		nextChunkStart = chunkStart + chunkSize;
+
+		if (nextChunkStart > selection.getEndFrame()) {
+		    nextChunkStart = selection.getEndFrame();
+		    fadeOut = 50;
+		}
 
 		chunkSize = nextChunkStart - chunkStart;
 	    }
+	
+	} else if (m_viewManager->getPlayLoopMode() &&
+		   m_lastModelEndFrame > 0) {
+
+	    if (chunkStart >= m_lastModelEndFrame) {
+		chunkStart = 0;
+	    }
+	    if (chunkSize > m_lastModelEndFrame - chunkStart) {
+		chunkSize = m_lastModelEndFrame - chunkStart;
+	    }
+	    nextChunkStart = chunkStart + chunkSize;
 	}
 	
 	if (!chunkSize) {
@@ -865,11 +935,32 @@
 
 	size_t got = 0;
 
+	if (chunkSize < 100) {
+	    fadeIn = 0;
+	    fadeOut = 0;
+	} else if (chunkSize < 300) {
+	    if (fadeIn > 0) fadeIn = 10;
+	    if (fadeOut > 0) fadeOut = 10;
+	}
+
+	if (fadeIn > 0) {
+	    if (processed * 2 < fadeIn) {
+		fadeIn = processed * 2;
+	    }
+	}
+
+	if (fadeOut > 0) {
+	    if ((count - processed) * 2 < fadeOut) {
+		fadeOut = (count - processed) * 2;
+	    }
+	}
+
 	for (std::set<Model *>::iterator mi = m_models.begin();
 	     mi != m_models.end(); ++mi) {
 	    
 	    got = m_audioGenerator->mixModel(*mi, chunkStart, 
-					     chunkSize, chunkBufferPtrs);
+					     chunkSize, chunkBufferPtrs,
+					     fadeIn, fadeOut);
 	}
 
 	for (size_t c = 0; c < channels; ++c) {
@@ -911,12 +1002,13 @@
 	}
 
 	if (!s.m_playing) ms *= 10;
+	ms = ms / 8;
 
 #ifdef DEBUG_AUDIO_PLAY_SOURCE
-	std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms/4 << "ms..." << std::endl;
+	std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << std::endl;
 #endif
 
-	s.m_condition.wait(&s.m_mutex, size_t(ms / 4));
+	s.m_condition.wait(&s.m_mutex, size_t(ms));
 
 #ifdef DEBUG_AUDIO_PLAY_SOURCE
 	std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl;