Punch in/out

Hi,

Is is possible to start playback at some timeline position and drop in and out of recording later in the timeline? Essentially punch in / punch out, but in my case I know beforehand where recording should start and stop.

Of course I could just start recording earlier but I don’t want the new material to replace the contents of the track during the count-in/pre-roll.

I’m working on MIDI recording for the moment but I guess that the mechanics is essentially the same for audio recording as well?

Erik

3 Likes

I was going to ask a similar question (except I don’t know the punch out time before hand) :slight_smile:

@erikronstrom Similar, I was trying to get on the fly PunchInOut recording working. I did bump into something that might be what you want, though.

My Punch In/Out Problem: - Continuous playback with on demand start/stop recording
Using the supplied Recording Demo I found that if it’s already playing, hitting record would trigger the creation of hundreds of tiny (784bytes) wav files whilst grinding everything to a halt.

What I tried:
1. Rapidly stop playback and start recording

edit.getTransport().stop (false, false); //Sneakily stopping before recording.
edit.getTransport().record (false); //Starts playback and recording again.

This kind of works, but clearly isn’t right and also results in a tiny blip in audio playback.

2. Playing, then hitting record with PunchInOut
I also experimented with setting edit.recordingPunchInOut.setValue(true, nullptr), where I set the in and out points slightly later than the start time. By pressing play, then record, it would play fine until it got to the record in-point, then it would all grind to a halt again. So clearly thats not the answer for me either.

3. Moving punchInOut during record.
Finally I tried replacing Play with Record+PunchInOut and EditTimeRange(1000.f,1001.f). Then whilst it was playing having a test button suddenly move the EditTimeRange to 2.f and 4.f. I was hoping TE would notice this change and start recording in the newly moved range - kind of like a fake punchInOut recording. That didn’t happen so sadly this idea didn’t work at all.

I don’t know what else to try. I’m pretty sure I must be missing something obvious but I can’t see what.

@erikronstrom I might be misunderstanding but it seems like what you need is:

edit.recordingPunchInOut.setValue(true, nullptr);
edit.getTransport().setLoopRange(te::EditTimeRange(double start, double end))
edit.getTransport().record (false);

Note the lack of play command. It will still play though, and when it gets to the in point it should start recording.

Cheers,

Jeff

Thanks, this is what I was looking for! However, while it seems to work well with audio I can’t get it to work with MIDI recordings:

JUCE Assertion failure in juce_MidiMessageSequence.cpp:114
(lldb) bt
* thread #1, name = 'JUCE Message Thread', queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
    frame #0: 0x00000001001eff22 Gaudiamus`juce::MidiMessageSequence::getIndexOfMatchingKeyUp(this=0x0000000103f18f40, index=6) const at juce_MidiMessageSequence.cpp:114
    frame #1: 0x00000001001f08b6 Gaudiamus`juce::MidiMessageSequence::deleteEvent(this=0x0000000103f18f40, index=6, deleteMatchingNoteUp=true) at juce_MidiMessageSequence.cpp:187
    frame #2: 0x00000001010758ea Gaudiamus`tracktion_engine::MidiInputDeviceInstanceBase::applyLastRecordingToEdit(this=0x0000000103f18e40, recordedRange=(start = 4, end = 18.013242630385488), isLooping=false, loopRange=(start = 8, end = 14), discardRecordings=false, selectionManager=0x0000000000000000) at tracktion_MidiInputDevice.cpp:940
    frame #3: 0x0000000100f85e74 Gaudiamus`tracktion_engine::EditPlaybackContext::stopRecording(this=0x0000000103f41370, in=0x0000000103f18e40, recordedRange=(start = 4, end = 18.013242630385488), discardRecordings=false) at tracktion_EditPlaybackContext.cpp:636
    frame #4: 0x0000000100f86175 Gaudiamus`tracktion_engine::EditPlaybackContext::recordingFinished(this=0x0000000103f41370, recordedRange=(start = 4, end = 18.013242630385488), discardRecordings=false) at tracktion_EditPlaybackContext.cpp:649
    frame #5: 0x0000000100f9a15f Gaudiamus`tracktion_engine::TransportControl::performStop(this=0x0000000103d0d9c0) at tracktion_TransportControl.cpp:1472
    frame #6: 0x00000001010beaa8 Gaudiamus`tracktion_engine::TransportControl::TransportState::valueTreePropertyChanged(this=0x0000000117973c00, v=0x00007ffeefbfe220, i=0x0000000117973c28) at tracktion_TransportControl.cpp:235
    frame #7: 0x00000001005f538b Gaudiamus`juce::ValueTree::SharedObject::sendPropertyChangeMessage(this=0x00007ffeefbfe180, l=0x0000000117973c00)::'lambda'(juce::ValueTree::Listener&)::operator()(juce::ValueTree::Listener&) const at juce_ValueTree.cpp:100
    frame #8: 0x00000001005f534b Gaudiamus`void juce::ListenerList<juce::ValueTree::Listener, juce::Array<juce::ValueTree::Listener*, juce::DummyCriticalSection, 0> >::callExcluding<juce::ValueTree::SharedObject::sendPropertyChangeMessage(this=0x0000000117974248, listenerToExclude=0x0000000000000000, callback=0x00007ffeefbfe180)::'lambda'(juce::ValueTree::Listener&)&>(juce::ValueTree::Listener*, juce::ValueTree::SharedObject::sendPropertyChangeMessage(juce::Identifier const&, juce::ValueTree::Listener*)::'lambda'(juce::ValueTree::Listener&)&&&) at juce_ListenerList.h:140
    frame #9: 0x00000001005f527b Gaudiamus`void juce::ValueTree::SharedObject::callListeners<juce::ValueTree::SharedObject::sendPropertyChangeMessage(juce::Identifier const&, juce::ValueTree::Listener*)::'lambda'(juce::ValueTree::Listener&)>(this=0x00006000000a9fc0, listenerToExclude=0x0000000000000000, fn=(anonymous class) @ 0x00007ffeefbfe180)::'lambda'(juce::ValueTree::Listener&)) const at juce_ValueTree.cpp:85
    frame #10: 0x00000001005f5170 Gaudiamus`void juce::ValueTree::SharedObject::callListenersForAllParents<juce::ValueTree::SharedObject::sendPropertyChangeMessage(juce::Identifier const&, juce::ValueTree::Listener*)::'lambda'(juce::ValueTree::Listener&)>(this=0x00006000000a9fc0, listenerToExclude=0x0000000000000000, fn=(anonymous class) @ 0x00007ffeefbfe1d0)::'lambda'(juce::ValueTree::Listener&)) const at juce_ValueTree.cpp:94
    frame #11: 0x00000001005e5fd4 Gaudiamus`juce::ValueTree::SharedObject::sendPropertyChangeMessage(this=0x00006000000a9fc0, property=0x0000000117973c28, listenerToExclude=0x0000000000000000) at juce_ValueTree.cpp:100
    frame #12: 0x00000001005e40c8 Gaudiamus`juce::ValueTree::SharedObject::setProperty(this=0x00006000000a9fc0, name=0x0000000117973c28, newValue=0x00007ffeefbfe428, undoManager=0x0000000000000000, listenerToExclude=0x0000000000000000) at juce_ValueTree.cpp:138
    frame #13: 0x00000001005e4052 Gaudiamus`juce::ValueTree::setPropertyExcludingListener(this=0x0000000117973c10, listenerToExclude=0x0000000000000000, name=0x0000000117973c28, newValue=0x00007ffeefbfe428, undoManager=0x0000000000000000) at juce_ValueTree.cpp:769
    frame #14: 0x00000001005e3f3d Gaudiamus`juce::ValueTree::setProperty(this=0x0000000117973c10, name=0x0000000117973c28, newValue=0x00007ffeefbfe428, undoManager=0x0000000000000000) at juce_ValueTree.cpp:759
    frame #15: 0x0000000100137de9 Gaudiamus`juce::CachedValue<bool>::setValue(this=0x0000000117973c08, newValue=0x00007ffeefbfe4cb, undoManagerToUse=0x0000000000000000) at juce_CachedValue.h:251
    frame #16: 0x0000000100122eb3 Gaudiamus`juce::CachedValue<bool>::operator=(this=0x0000000117973c08, newValue=0x00007ffeefbfe4cb) at juce_CachedValue.h:241
    frame #17: 0x0000000100f93085 Gaudiamus`tracktion_engine::TransportControl::TransportState::stop(this=0x0000000117973c00, discardRecordings_=false, clearDevices_=false, canSendMMCStop_=true, invertReturnToStartPosSelection_=false) at tracktion_TransportControl.cpp:157
    frame #18: 0x0000000100f914dc Gaudiamus`tracktion_engine::TransportControl::stop(this=0x0000000103d0d9c0, discardRecordings=false, clearDevices=false, canSendMMCStop=true, invertReturnToStartPosSelection=false) at tracktion_TransportControl.cpp:817
  * frame #19: 0x000000010013ddfb Gaudiamus`MainComponent::MainComponent(this=0x000000010484bf68)::$_3::operator()() const at MainComponent.cpp:62
    frame #20: 0x000000010013dcad Gaudiamus`void std::__1::__invoke_void_return_wrapper<void>::__call<MainComponent::MainComponent()::$_3&>(MainComponent::MainComponent()::$_3&&&) [inlined] decltype(__f=0x000000010484bf68)::$_3&>(fp)(std::__1::forward<>(fp0))) std::__1::__invoke<MainComponent::MainComponent()::$_3&>(MainComponent::MainComponent()::$_3&&&) at type_traits:4428
    frame #21: 0x000000010013dc9c Gaudiamus`void std::__1::__invoke_void_return_wrapper<void>::__call<MainComponent::MainComponent(__args=0x000000010484bf68)::$_3&>(MainComponent::MainComponent()::$_3&&&) at __functional_base:349
    frame #22: 0x000000010013dbc9 Gaudiamus`std::__1::__function::__func<MainComponent::MainComponent()::$_3, std::__1::allocator<MainComponent::MainComponent()::$_3>, void ()>::operator(this=0x000000010484bf60)() at functional:1562
    frame #23: 0x0000000100143346 Gaudiamus`std::__1::function<void ()>::operator(this=0x000000010484bf60)() const at functional:1913
    frame #24: 0x0000000100803276 Gaudiamus`juce::Button::sendClickMessage(this=0x000000010484be90, modifiers=0x00007ffeefbfe888) at juce_Button.cpp:407
    frame #25: 0x00000001008039b4 Gaudiamus`juce::Button::internalClickCallback(this=0x000000010484be90, modifiers=0x00007ffeefbfe888) at juce_Button.cpp:350
    frame #26: 0x00000001008041d0 Gaudiamus`juce::Button::mouseUp(this=0x000000010484be90, e=0x00007ffeefbfe878) at juce_Button.cpp:470
    frame #27: 0x00000001007ef142 Gaudiamus`juce::Component::internalMouseUp(this=0x000000010484be90, source=MouseInputSource @ 0x00007ffeefbfe998, relativePos=(x = 99.4335937, y = 12.9570313), time=(millisSinceEpoch = 1631184091134), oldModifiers=(flags = 16), pressure=0, orientation=0, rotation=0, tiltX=0, tiltY=0) at juce_Component.cpp:2458
    frame #28: 0x00000001008faa2e Gaudiamus`juce::MouseInputSourceInternal::sendMouseUp(this=0x00006040001e0400, comp=0x000000010484be90, screenPos=(x = 941.433593, y = 59.9570313), time=(millisSinceEpoch = 1631184091134), oldMods=(flags = 16)) at juce_MouseInputSource.cpp:154
    frame #29: 0x00000001008f9c2b Gaudiamus`juce::MouseInputSourceInternal::setButtons(this=0x00006040001e0400, screenPos=(x = 941.433593, y = 59.9570313), time=(millisSinceEpoch = 1631184091134), newButtonState=(flags = 0)) at juce_MouseInputSource.cpp:196
    frame #30: 0x00000001007f4076 Gaudiamus`juce::MouseInputSourceInternal::handleEvent(this=0x00006040001e0400, newPeer=0x0000604000188130, positionWithinPeer=(x = 701.433593, y = 14.9570313), time=(millisSinceEpoch = 1631184091134), newMods=(flags = 0), newPressure=0, newOrientation=0, pen=(rotation = 0, tiltX = 0, tiltY = 0)) at juce_MouseInputSource.cpp:333
    frame #31: 0x00000001007f3dc2 Gaudiamus`juce::MouseInputSource::handleEvent(this=0x00007ffeefbfec68, peer=0x0000604000188130, pos=(x = 701.433593, y = 14.9570313), time=1631184091134, mods=(flags = 0), pressure=0, orientation=0, penDetails=0x00007ffeefbfecb8) at juce_MouseInputSource.cpp:633
    frame #32: 0x00000001008d7e5c Gaudiamus`juce::ComponentPeer::handleMouseEvent(this=0x0000604000188130, type=mouse, pos=(x = 701.433593, y = 14.9570313), newMods=(flags = 0), newPressure=0, newOrientation=0, time=1631184091134, pen=(rotation = 0, tiltX = 0, tiltY = 0), touchIndex=0) at juce_ComponentPeer.cpp:87
    frame #33: 0x00000001009891e2 Gaudiamus`juce::NSViewComponentPeer::sendMouseEvent(this=0x0000604000188130, ev=0x0000600000124c40) at juce_mac_NSViewComponentPeer.mm:747
    frame #34: 0x000000010098c5fe Gaudiamus`juce::NSViewComponentPeer::redirectMouseUp(this=0x0000604000188130, ev=0x0000600000124c40) at juce_mac_NSViewComponentPeer.mm:611
    frame #35: 0x0000000100987b89 Gaudiamus`juce::JuceNSViewClass::asyncMouseUp(self=0x0000604000122d00, (null)="mouseUp:", ev=0x0000600000124c40) at juce_mac_NSViewComponentPeer.mm:1796
    frame #36: 0x0000000100985c37 Gaudiamus`juce::JuceNSViewClass::mouseUp(self=0x0000604000122d00, s="mouseUp:", ev=0x0000600000124c40) at juce_mac_NSViewComponentPeer.mm:1782
    frame #37: 0x00007fff34392836 AppKit`-[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 1961
    frame #38: 0x00007fff34391c70 AppKit`-[NSWindow(NSEventRouting) sendEvent:] + 497
    frame #39: 0x00007fff341f3236 AppKit`-[NSApplication(NSEvent) sendEvent:] + 2462
    frame #40: 0x00007fff33a5387d AppKit`-[NSApplication run] + 812
    frame #41: 0x00000001006a2052 Gaudiamus`juce::MessageManager::runDispatchLoop(this=0x0000604000247410) at juce_mac_MessageManager.mm:362
    frame #42: 0x00000001006a1e44 Gaudiamus`juce::JUCEApplicationBase::main() at juce_ApplicationBase.cpp:263
    frame #43: 0x00000001006a1a0c Gaudiamus`juce::JUCEApplicationBase::main(argc=3, argv=0x00007ffeefbff770) at juce_ApplicationBase.cpp:241
    frame #44: 0x0000000100180db3 Gaudiamus`main(argc=3, argv=0x00007ffeefbff770) at Main.cpp:160
    frame #45: 0x00007fff5e48e015 libdyld.dylib`start + 1

Actually after more testing it does work with MIDI, apart from this intermittent crash!

Thanks, this sounds like an issue we need to look in to.


This is the correct way to punch record if you know the times before hand.

If you don’t know the times before had you have to start recording like so:
transportControl.record (false, true)
and then when you arm an input it will punch in. Stopping recording or disarming the input will stop recording.


@erikronstrom this isn’t a crash but an assertion. It’s telling you that there is a note-off event with no note-on. Could this be because you’ve started recording whilst holding a note?
Apart from the assertion, I think the recording will complete but that note will be discarded.

1 Like

@dave96 thanks so much! Gah I should have thought of simply arming/disarming during the record. :man_facepalming:

I’ll try it as soon as I wake up properly though I suspect this will likely work.

I appreciate the answer and the clarifications. Awesome :slight_smile:
Cheers,

Jeff

this isn’t a crash but an assertion. It’s telling you that there is a note-off event with no note-on. Could this be because you’ve started recording whilst holding a note?

Possibly – I will try and verify that.

Apart from the assertion, I think the recording will complete but that note will be discarded.

Does that mean that I can/should just comment out the assertion?

Well you can do but it just wont be there in release builds so you don’t have to.
That’s of course if that is the reason the assertion is being triggered. If it’s something else, commenting out the assertion will hide that cause.

But this could well happen during normal usage, couldn’t it? If the user starts recording with a MIDI key pressed. But of course I could try and avoid that during development…

Yes, which is why you can just continue past it.
The problem is the assertion is there to catch other possibly unexpected behaviour but it’s catching this perhaps expected behaviour.

There’s also an argument that perhaps if a key is held when recording starts that it should create a note-on event at that time. I’d have to do some research as to whether that’s the best/canonical approach though which I don’t really have the capacity to do at the moment.