diff options
author | Tim E. Real <termtech@rogers.com> | 2010-01-06 09:27:34 +0000 |
---|---|---|
committer | Tim E. Real <termtech@rogers.com> | 2010-01-06 09:27:34 +0000 |
commit | 9eabbfa460b5e4a2f24ace6146df6df59ddcfdbd (patch) | |
tree | 16eb2e743ef4c61f7da0c7f84fdb2235ab1c6de2 | |
parent | 9f66d4ea7410ac4bca7a03498f3e13bab9df83c0 (diff) |
See ChangeLog
-rw-r--r-- | muse/ChangeLog | 4 | ||||
-rw-r--r-- | muse/muse/Makefile.am | 1 | ||||
-rw-r--r-- | muse/muse/audio.cpp | 25 | ||||
-rw-r--r-- | muse/muse/audioconvert.cpp | 836 | ||||
-rw-r--r-- | muse/muse/audioconvert.h | 95 | ||||
-rw-r--r-- | muse/muse/audioprefetch.cpp | 57 | ||||
-rw-r--r-- | muse/muse/audioprefetch.h | 6 | ||||
-rw-r--r-- | muse/muse/conf.cpp | 4 | ||||
-rw-r--r-- | muse/muse/driver/jack.cpp | 218 | ||||
-rw-r--r-- | muse/muse/driver/jackaudio.h | 8 | ||||
-rw-r--r-- | muse/muse/event.cpp | 122 | ||||
-rw-r--r-- | muse/muse/event.h | 10 | ||||
-rw-r--r-- | muse/muse/eventbase.h | 12 | ||||
-rw-r--r-- | muse/muse/midiport.cpp | 14 | ||||
-rw-r--r-- | muse/muse/sync.cpp | 1 | ||||
-rw-r--r-- | muse/muse/sync.h | 1 | ||||
-rw-r--r-- | muse/muse/thread.cpp | 5 | ||||
-rw-r--r-- | muse/muse/wave.cpp | 187 | ||||
-rw-r--r-- | muse/muse/wave.h | 8 | ||||
-rw-r--r-- | muse/muse/waveevent.cpp | 304 | ||||
-rw-r--r-- | muse/muse/waveevent.h | 12 | ||||
-rw-r--r-- | muse/muse/widgets/citem.cpp | 4 | ||||
-rw-r--r-- | muse/muse/widgets/citem.h | 4 | ||||
-rw-r--r-- | muse/muse/widgets/midisync.ui | 93 | ||||
-rw-r--r-- | muse/muse/widgets/midisyncimpl.cpp | 17 |
25 files changed, 1762 insertions, 286 deletions
diff --git a/muse/ChangeLog b/muse/ChangeLog index 90cdb908..efadbae6 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,7 @@ +06.01.2010 + * Feature: Jack transport enable/disable in Midi Sync settings window. Stores setting per-song. (T356) + - Should be Ok to use and test. Needs a bit more work. See jack.cpp and jackaudio.h + * Fixed: Speedups of audio pre-fetch especially when moving the cursor around (seeking). (T356) 23.12.2009: * Added: DeicsOnze2 initial import. (ng) * Fix: deicsonzepreset.cpp for Xml muse 1 API diff --git a/muse/muse/Makefile.am b/muse/muse/Makefile.am index c08031ee..c58f20ff 100644 --- a/muse/muse/Makefile.am +++ b/muse/muse/Makefile.am @@ -39,6 +39,7 @@ dist_muse_SOURCES = \ thread.cpp thread.h \ audio.cpp audio.h \ audioprefetch.cpp audioprefetch.h \ + audioconvert.cpp audioconvert.h \ globals.cpp globals.h \ sync.cpp sync.h \ midiport.cpp midiport.h \ diff --git a/muse/muse/audio.cpp b/muse/muse/audio.cpp index 02a3c549..9a716054 100644 --- a/muse/muse/audio.cpp +++ b/muse/muse/audio.cpp @@ -227,7 +227,10 @@ void Audio::stop(bool) bool Audio::sync(int jackState, unsigned frame) { -// printf("sync state %s jackState %s frame %d\n", audioStates[state], audioStates[jackState], frame); + // Added by Tim. p3.3.20 + if(debugMsg) + printf("Audio::sync state %s jackState %s frame %d\n", audioStates[state], audioStates[jackState], frame); + bool done = true; if (state == LOOP1) state = LOOP2; @@ -238,8 +241,14 @@ bool Audio::sync(int jackState, unsigned frame) } state = State(jackState); if (!_freewheel) - done = audioPrefetch->seekDone; + //done = audioPrefetch->seekDone; + done = audioPrefetch->seekDone(); } + + // Added by Tim. p3.3.20 + //if(debugMsg) + // printf("Audio::sync done:%d state %s\n", done, audioStates[state]); + return done; } @@ -296,6 +305,10 @@ void Audio::process(unsigned frames) int jackState = audioDevice->getState(); + // Added by Tim. p3.3.20 + //if(debugMsg) + // printf("Audio::process Current state:%s jackState:%s\n", audioStates[state], audioStates[jackState]); + if (state == START_PLAY && jackState == PLAY) { _loopCount = 0; startRolling(); @@ -367,6 +380,10 @@ void Audio::process(unsigned frames) && !(song->record() || _bounce || song->loop())) { + // Added by Tim. p3.3.20 + //if(debugMsg) + // printf("Audio::process curTickPos >= song->len\n"); + audioDevice->stopTransport(); return; } @@ -1026,6 +1043,10 @@ void Audio::startRolling() void Audio::stopRolling() { + // Added by Tim. p3.3.20 + //if(debugMsg) + // printf("Audio::stopRolling state %s\n", audioStates[state]); + state = STOP; midiSeq->msgStop(); diff --git a/muse/muse/audioconvert.cpp b/muse/muse/audioconvert.cpp new file mode 100644 index 00000000..47159140 --- /dev/null +++ b/muse/muse/audioconvert.cpp @@ -0,0 +1,836 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: audioconvert.cpp,v 1.1.1.1 2009/12/28 16:07:33 terminator356 Exp $ +// +// (C) Copyright 1999-2009 Werner Schweer (ws@seh.de) +// +// Audio converter module created by Tim terminator356 +//========================================================= + +#include <math.h> + +#include "wave.h" +#include "globals.h" +#include "audioconvert.h" + +//#define AUDIOCONVERT_DEBUG +//#define AUDIOCONVERT_DEBUG_PRC + +//--------------------------------------------------------- +// AudioConverter +//--------------------------------------------------------- + +AudioConverter::AudioConverter() +{ + #ifdef AUDIOCONVERT_DEBUG + printf("AudioConverter::AudioConverter this:%p\n", this); + #endif + + _refCount = 1; +} + +AudioConverter::~AudioConverter() +{ + #ifdef AUDIOCONVERT_DEBUG + printf("AudioConverter::~AudioConverter this:%p\n", this); + #endif +} + +AudioConverter* AudioConverter::reference() +{ + _refCount += 1; + #ifdef AUDIOCONVERT_DEBUG + printf("AudioConverter::reference this:%p current refcount:%d\n", this, _refCount); + #endif + return this; +} + +AudioConverter* AudioConverter::release(AudioConverter* cv) +{ + if(!cv) + return 0; + #ifdef AUDIOCONVERT_DEBUG + printf("AudioConverter::release converter:%p\n", cv); + #endif + //if(cv->incRefCount(-1) <= 0) + if((cv->_refCount -= 1) <= 0) + { + #ifdef AUDIOCONVERT_DEBUG + printf("AudioConverter::release deleting converter:%p\n", cv); + #endif + delete cv; + cv = 0; + } + return cv; +} + +off_t AudioConverter::readAudio(SndFileR& f, off_t sfCurFrame, unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) +{ + if(f.isNull()) + return sfCurFrame; + + // Added by Tim. p3.3.17 + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf("AudioConverter::process %s audConv:%p sfCurFrame:%ld offset:%u channel:%d fchan:%d n:%d\n", + // f.name().latin1(), this, sfCurFrame, offset, channel, f.channels(), n); + //#endif + + off_t frame = offset; // _spos is added before the call. + unsigned fsrate = f.samplerate(); + bool resample = isValid() && ((unsigned)sampleRate != fsrate); + + // No resampling needed? + if(!resample) + { + // Sample rates are the same. Just a regular seek + read, no conversion. + sfCurFrame = f.seek(frame, 0); + return sfCurFrame + f.read(channel, buffer, n, overwrite); + } + + // Is a 'transport' seek requested? (Not to be requested with every read! Should only be for 'first read' seeks, or positional 'transport' seeks.) + // Due to the support of sound file references in MusE, seek must ALWAYS be done before read, as before, + // except now we alter the seek position if sample rate conversion is being used and remember the seek positions. + if(doSeek) + { + // Sample rates are different. Seek to a calculated 'sample rate ratio factored' position. + + double srcratio = (double)fsrate / (double)sampleRate; + //long inSize = long((double)frames * _src_ratio) + 1 // From MusE-2 file converter. + off_t newfr = (off_t)floor(((double)frame * srcratio)); // From simplesynth. + + sfCurFrame = f.seek(newfr, 0); + + // Added by Tim. p3.3.17 + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf("AudioConverter::process Seek frame:%ld converted to frame:%ld sfCurFrame:%ld\n", frame, newfr, sfCurFrame); + //#endif + + // Reset the converter. Its current state is meaningless now. + reset(); + } + else + { + // No seek requested. + // Added by Tim. p3.3.17 + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf("AudioConverter::process No 'transport' seek, rates different. Seeking to sfCurFrame:%ld\n", sfCurFrame); + //#endif + + // Sample rates are different. We can't just tell seek to go to an absolute calculated position, + // since the last position can vary - it might not be what the calculated position is. + // We must use the last position left by SRC conversion, ie. let the file position progress on its own. + sfCurFrame = f.seek(sfCurFrame, 0); + } + + /* + int fchan = f.channels(); + long outFrames = n; + long outSize = outFrames * fchan; + float outbuffer[outSize]; + */ + + //sfCurFrame = process(f, sfCurFrame, offset, &outbuffer[0], channel, n); +// sfCurFrame = process(f, sfCurFrame, outbuffer, channel, n); + sfCurFrame = process(f, sfCurFrame, buffer, channel, n, overwrite); + + /* + float* poutbuf = &outbuffer[0]; + if(fchan == channel) + { + if(overwrite) + //for (size_t i = 0; i < rn; ++i) + for (int i = 0; i < n; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) = *poutbuf++; + } + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) += *poutbuf++; + } + } + else if((fchan == 2) && (channel == 1)) + { + // stereo to mono + if(overwrite) + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + *(buffer[0] + i) = poutbuf[i + i] + poutbuf[i + i + 1]; + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + *(buffer[0] + i) += poutbuf[i + i] + poutbuf[i + i + 1]; + } + else if((fchan == 1) && (channel == 2)) + { + // mono to stereo + if(overwrite) + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) = data; + *(buffer[1]+i) = data; + } + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) += data; + *(buffer[1]+i) += data; + } + } + else + { + #ifdef AUDIOCONVERT_DEBUG + printf("AudioConverter::readAudio Channel mismatch: source chans:%d -> dst chans:%d\n", fchan, channel); + #endif + } + */ + + return sfCurFrame; +} + +//--------------------------------------------------------- +// SRCAudioConverter +//--------------------------------------------------------- + +SRCAudioConverter::SRCAudioConverter(int channels, int type) : AudioConverter() +{ + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::SRCAudioConverter this:%p channels:%d type:%d\n", this, channels, type); + #endif + + _type = type; + _src_state = 0; + _channels = channels; + + int srcerr; + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::SRCaudioConverter Creating samplerate converter type:%d with %d channels\n", _type, _channels); + #endif + _src_state = src_new(_type, _channels, &srcerr); + if(!_src_state) + printf("SRCAudioConverter::SRCaudioConverter Creation of samplerate converter type:%d with %d channels failed:%s\n", _type, _channels, src_strerror(srcerr)); +} + +SRCAudioConverter::~SRCAudioConverter() +{ + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::~SRCAudioConverter this:%p\n", this); + #endif + if(_src_state) + src_delete(_src_state); +} + +void SRCAudioConverter::setChannels(int ch) +{ + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::setChannels this:%p channels:%d\n", this, ch); + #endif + if(_src_state) + src_delete(_src_state); + _src_state = 0; + + _channels = ch; + int srcerr; + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::setChannels Creating samplerate converter type:%d with %d channels\n", _type, ch); + #endif + _src_state = src_new(_type, ch, &srcerr); + if(!_src_state) + printf("SRCAudioConverter::setChannels of samplerate converter type:%d with %d channels failed:%s\n", _type, ch, src_strerror(srcerr)); + return; +} + +void SRCAudioConverter::reset() +{ + if(!_src_state) + return; + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::reset this:%p\n", this); + #endif + int srcerr = src_reset(_src_state); + if(srcerr != 0) + printf("SRCAudioConverter::reset Converter reset failed: %s\n", src_strerror(srcerr)); + return; +} + +off_t SRCAudioConverter::process(SndFileR& f, off_t sfCurFrame, float** buffer, int channel, int n, bool overwrite) +{ + //return src_process(_src_state, sd); + + if(f.isNull()) + //return; + return sfCurFrame; + + // Added by Tim. p3.3.17 + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf("AudioConverter::process %s audConv:%p sfCurFrame:%ld offset:%u channel:%d fchan:%d n:%d\n", + // f.name().latin1(), this, sfCurFrame, offset, channel, f.channels(), n); + //#endif + +// off_t frame = offset; // _spos is added before the call. + unsigned fsrate = f.samplerate(); + //bool resample = src_state && ((unsigned)sampleRate != fsrate); +// bool resample = isValid() && ((unsigned)sampleRate != fsrate); + + if((sampleRate == 0) || (fsrate == 0)) + { + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::process Error: sampleRate or file samplerate is zero!\n"); + #endif + return sfCurFrame; + } + + SRC_DATA srcdata; + int fchan = f.channels(); + // Ratio is defined as output sample rate over input samplerate. + double srcratio = (double)sampleRate / (double)fsrate; + // Extra input compensation. + long inComp = 1; + + long outFrames = n; + //long outSize = outFrames * channel; + long outSize = outFrames * fchan; + + //long inSize = long(outSize * srcratio) + 1 // From MusE-2 file converter. + //long inSize = (long)floor(((double)outSize / srcratio)); // From simplesynth. + //long inFrames = (long)floor(((double)outFrames / srcratio)); // From simplesynth. + long inFrames = (long)ceil(((double)outFrames / srcratio)) + inComp; // From simplesynth. + //long inFrames = (long)floor(double(outFrames * sfinfo.samplerate) / double(sampleRate)); // From simplesynth. + + long inSize = inFrames * fchan; + //long inSize = inFrames * channel; + + // Start with buffers at expected sizes. We won't need anything larger than this, but add 4 for good luck. + float inbuffer[inSize + 4]; + float outbuffer[outSize]; + + //size_t sfTotalRead = 0; + size_t rn = 0; + long totalOutFrames = 0; + + srcdata.data_in = inbuffer; + srcdata.data_out = outbuffer; +// srcdata.data_out = buffer; + + // Set some kind of limit on the number of attempts to completely fill the output buffer, + // in case something is really screwed up - we don't want to get stuck in a loop here. + int attempts = 10; + for(int attempt = 0; attempt < attempts; ++attempt) + { + rn = f.readDirect(inbuffer, inFrames); + //sfTotalRead += rn; + + // convert + //srcdata.data_in = inbuffer; + //srcdata.data_out = outbuffer; + //srcdata.data_out = poutbuf; + //srcdata.input_frames = inSize; + srcdata.input_frames = rn; + srcdata.output_frames = outFrames; + srcdata.end_of_input = ((long)rn != inFrames); + srcdata.src_ratio = srcratio; + + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf("AudioConverter::process attempt:%d inFrames:%ld outFrames:%ld rn:%d data in:%p out:%p", + // attempt, inFrames, outFrames, rn, srcdata.data_in, srcdata.data_out); + //#endif + + int srcerr = src_process(_src_state, &srcdata); + if(srcerr != 0) + { + printf("\nSRCAudioConverter::process SampleRate converter process failed: %s\n", src_strerror(srcerr)); + return sfCurFrame += rn; + } + + totalOutFrames += srcdata.output_frames_gen; + + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf(" frames used in:%ld out:%ld totalOutFrames:%ld data in:%p out:%p\n", srcdata.input_frames_used, srcdata.output_frames_gen, totalOutFrames, srcdata.data_in, srcdata.data_out); + //#endif + + #ifdef AUDIOCONVERT_DEBUG + if(srcdata.output_frames_gen != outFrames) + printf("SRCAudioConverter::process %s output_frames_gen:%ld != outFrames:%ld inFrames:%ld srcdata.input_frames_used:%ld rn:%d\n", + f.name().latin1(), srcdata.output_frames_gen, outFrames, inFrames, srcdata.input_frames_used, rn); + #endif + + // If the number of frames read by the soundfile equals the input frames, go back. + // Otherwise we have reached the end of the file, so going back is useless since + // there shouldn't be any further calls. + if((long)rn == inFrames) + { + // Go back by the amount of unused frames. + sf_count_t seekn = inFrames - srcdata.input_frames_used; + if(seekn != 0) + { + #ifdef AUDIOCONVERT_DEBUG_PRC + printf("SRCAudioConverter::process Seek-back by:%d\n", seekn); + #endif + sfCurFrame = f.seek(-seekn, SEEK_CUR); + } + else + sfCurFrame += rn; + + if(totalOutFrames == n) + { + // We got our desired number of output frames. Stop attempting. + break; + } + else + { + // No point in continuing if on last attempt. + if(attempt == (attempts - 1)) + break; + + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::process %s attempt:%d totalOutFrames:%ld != n:%d try again\n", f.name().latin1(), attempt, totalOutFrames, n); + #endif + + // SRC didn't give us the number of frames we requested. + // This can occasionally be radically different from the requested frames, or zero, + // even when ample excess input frames are supplied. + // Move the src output pointer to a new position. + srcdata.data_out += srcdata.output_frames_gen * channel; + // Set new number of maximum out frames. + outFrames -= srcdata.output_frames_gen; + // Calculate the new number of file input frames required. + inFrames = (long)ceil(((double)outFrames / srcratio)) + inComp; + // Keep trying. + continue; + } + } + else + { + sfCurFrame += rn; + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::process %s rn:%zd != inFrames:%ld output_frames_gen:%ld outFrames:%ld srcdata.input_frames_used:%ld\n", + f.name().latin1(), rn, inFrames, srcdata.output_frames_gen, outFrames, srcdata.input_frames_used); + #endif + + // We've reached the end of the file. Convert the number of frames read. + //rn = (double)rn * srcratio + 1; + //rn = (long)floor((double)rn * srcratio); + //if(rn > (size_t)outFrames) + // rn = outFrames; + // Stop attempting. + break; + } + } + + // If we still didn't get the desired number of output frames. + if(totalOutFrames != n) + { + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::process %s totalOutFrames:%ld != n:%d\n", f.name().latin1(), totalOutFrames, n); + #endif + + // Let's zero the rest of it. + long b = totalOutFrames * channel; + long e = n * channel; + for(long i = b; i < e; ++i) + outbuffer[i] = 0.0f; + //buffer[i] = 0.0f; + } + + //float* poutbuf = &outbuffer[0]; + float* poutbuf = outbuffer; + if(fchan == channel) + { + if(overwrite) + //for (size_t i = 0; i < rn; ++i) + for (int i = 0; i < n; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) = *poutbuf++; + } + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) += *poutbuf++; + } + } + else if((fchan == 2) && (channel == 1)) + { + // stereo to mono + if(overwrite) + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + *(buffer[0] + i) = poutbuf[i + i] + poutbuf[i + i + 1]; + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + *(buffer[0] + i) += poutbuf[i + i] + poutbuf[i + i + 1]; + } + else if((fchan == 1) && (channel == 2)) + { + // mono to stereo + if(overwrite) + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) = data; + *(buffer[1]+i) = data; + } + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) += data; + *(buffer[1]+i) += data; + } + } + else + { + #ifdef AUDIOCONVERT_DEBUG + printf("SRCAudioConverter::process Channel mismatch: source chans:%d -> dst chans:%d\n", fchan, channel); + #endif + } + + return sfCurFrame; +} + +#ifdef RUBBERBAND_SUPPORT + +//--------------------------------------------------------- +// RubberBandAudioConverter +//--------------------------------------------------------- + +RubberBandAudioConverter::RubberBandAudioConverter(int channels, int options) : AudioConverter() +{ + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::RubberBandAudioConverter this:%p channels:%d options:%x\n", this, channels, options); + #endif + + _options = options; + _rbs = 0; + _channels = channels; + + _rbs = new RubberBandStretcher(sampleRate, _channels, _options); // , initialTimeRatio = 1.0, initialPitchScale = 1.0 +} + +RubberBandAudioConverter::~RubberBandAudioConverter() +{ + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::~RubberBandAudioConverter this:%p\n", this); + #endif + if(_rbs) + delete _rbs; +} + +void RubberBandAudioConverter::setChannels(int ch) +{ + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::setChannels this:%p channels:%d\n", this, ch); + #endif + if(_rbs) + delete _rbs; + _rbs = 0; + + _channels = ch; + _rbs = new RubberBandStretcher(sampleRate, _channels, _options); // , initialTimeRatio = 1.0, initialPitchScale = 1.0 +} + +void RubberBandAudioConverter::reset() +{ + if(!_rbs) + return; + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::reset this:%p\n", this); + #endif + _rbs->reset(); + return; +} + +///////////////////////////////// +// TODO: Not finished yet.. +//////////////////////////////// +off_t RubberBandAudioConverter::process(SndFileR& f, off_t sfCurFrame, float** buffer, int channel, int n, bool overwrite) +{ + //return src_process(_src_state, sd); + + if(f.isNull()) + //return; + return sfCurFrame; + + // Added by Tim. p3.3.17 + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf("AudioConverter::process %s audConv:%p sfCurFrame:%ld offset:%u channel:%d fchan:%d n:%d\n", + // f.name().latin1(), this, sfCurFrame, offset, channel, f.channels(), n); + //#endif + +// off_t frame = offset; // _spos is added before the call. + unsigned fsrate = f.samplerate(); + //bool resample = src_state && ((unsigned)sampleRate != fsrate); +// bool resample = isValid() && ((unsigned)sampleRate != fsrate); + + if((sampleRate == 0) || (fsrate == 0)) + { + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::process Error: sampleRate or file samplerate is zero!\n"); + #endif + return sfCurFrame; + } + +// SRC_DATA srcdata; + int fchan = f.channels(); + // Ratio is defined as output sample rate over input samplerate. + double srcratio = (double)sampleRate / (double)fsrate; + // Extra input compensation. + long inComp = 1; + + long outFrames = n; + //long outSize = outFrames * channel; + long outSize = outFrames * fchan; + + //long inSize = long(outSize * srcratio) + 1 // From MusE-2 file converter. + //long inSize = (long)floor(((double)outSize / srcratio)); // From simplesynth. + //long inFrames = (long)floor(((double)outFrames / srcratio)); // From simplesynth. + long inFrames = (long)ceil(((double)outFrames / srcratio)) + inComp; // From simplesynth. + //long inFrames = (long)floor(double(outFrames * sfinfo.samplerate) / double(sampleRate)); // From simplesynth. + + long inSize = inFrames * fchan; + //long inSize = inFrames * channel; + + // Start with buffers at expected sizes. We won't need anything larger than this, but add 4 for good luck. + float inbuffer[inSize]; // +4 +// float outbuffer[outSize]; + + //float* rbinbuffer[fchan]; + //float rbindata[inSize]; + //for (int i = 0; i < fchan; ++i) + // rbinbuffer[i] = rbindata + i * inFrames; + + float* rboutbuffer[fchan]; + float rboutdata[outSize]; + for (int i = 0; i < fchan; ++i) + rboutbuffer[i] = rboutdata + i * outFrames; + + //size_t sfTotalRead = 0; + size_t rn = 0; + long totalOutFrames = 0; + +// srcdata.data_in = inbuffer; + //srcdata.data_out = outbuffer; +// srcdata.data_out = buffer; + float** data_out = rboutbuffer; + + // For just sample rate conversion, apply same ratio to both time and pitch. + _rbs->setTimeRatio(srcratio); + _rbs->setPitchScale(srcratio); + + // Set some kind of limit on the number of attempts to completely fill the output buffer, + // in case something is really screwed up - we don't want to get stuck in a loop here. + int attempts = 10; + for(int attempt = 0; attempt < attempts; ++attempt) + { + size_t sreq = _rbs->getSamplesRequired(); + + size_t rbinSize = sreq * fchan; + float* rbinbuffer[fchan]; + float rbindata[rbinSize]; + for(int i = 0; i < fchan; ++i) + rbinbuffer[i] = rbindata + i * sreq; + +// rn = f.readDirect(inbuffer, inFrames); + rn = f.readDirect(inbuffer, sreq); + //sfTotalRead += rn; + + // Must de-interleave soundfile data to feed to rubberband. + for(size_t i = 0; i < rn; ++i) + { + for(int ch = 0; ch < fchan; ++ch) + *(rbinbuffer[ch] + i) = *inbuffer++; + } + + _rbs->process(rbinbuffer, rn, (long)rn != inFrames); + + // "This function returns -1 if all data has been fully processed and all output read, and the stretch process is now finished." + int savail = _rbs->available(); + + + // convert + //srcdata.data_in = inbuffer; + //srcdata.data_out = outbuffer; + //srcdata.data_out = poutbuf; + //srcdata.input_frames = inSize; + srcdata.input_frames = rn; + srcdata.output_frames = outFrames; + srcdata.end_of_input = ((long)rn != inFrames); + srcdata.src_ratio = srcratio; + + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf("AudioConverter::process attempt:%d inFrames:%ld outFrames:%ld rn:%d data in:%p out:%p", + // attempt, inFrames, outFrames, rn, srcdata.data_in, srcdata.data_out); + //#endif + + int srcerr = src_process(_src_state, &srcdata); + if(srcerr != 0) + { + printf("\RubberBandAudioConverter::process SampleRate converter process failed: %s\n", src_strerror(srcerr)); + return sfCurFrame += rn; + } + + totalOutFrames += srcdata.output_frames_gen; + + //#ifdef AUDIOCONVERT_DEBUG_PRC + //printf(" frames used in:%ld out:%ld totalOutFrames:%ld data in:%p out:%p\n", srcdata.input_frames_used, srcdata.output_frames_gen, totalOutFrames, srcdata.data_in, srcdata.data_out); + //#endif + + #ifdef AUDIOCONVERT_DEBUG + if(srcdata.output_frames_gen != outFrames) + printf("RubberBandAudioConverter::process %s output_frames_gen:%ld != outFrames:%ld inFrames:%ld srcdata.input_frames_used:%ld rn:%d\n", + f.name().latin1(), srcdata.output_frames_gen, outFrames, inFrames, srcdata.input_frames_used, rn); + #endif + + // If the number of frames read by the soundfile equals the input frames, go back. + // Otherwise we have reached the end of the file, so going back is useless since + // there shouldn't be any further calls. + if((long)rn == inFrames) + { + // Go back by the amount of unused frames. + sf_count_t seekn = inFrames - srcdata.input_frames_used; + if(seekn != 0) + { + #ifdef AUDIOCONVERT_DEBUG_PRC + printf("RubberBandAudioConverter::process Seek-back by:%d\n", seekn); + #endif + sfCurFrame = f.seek(-seekn, SEEK_CUR); + } + else + sfCurFrame += rn; + + if(totalOutFrames == n) + { + // We got our desired number of output frames. Stop attempting. + break; + } + else + { + // No point in continuing if on last attempt. + if(attempt == (attempts - 1)) + break; + + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::process %s attempt:%d totalOutFrames:%ld != n:%d try again\n", f.name().latin1(), attempt, totalOutFrames, n); + #endif + + // We didn't get the number of frames we requested. + // This can occasionally be radically different from the requested frames, or zero, + // even when ample excess input frames are supplied. + // Move the src output pointer to a new position. + srcdata.data_out += srcdata.output_frames_gen * channel; + // Set new number of maximum out frames. + outFrames -= srcdata.output_frames_gen; + // Calculate the new number of file input frames required. + inFrames = (long)ceil(((double)outFrames / srcratio)) + inComp; + // Keep trying. + continue; + } + } + else + { + sfCurFrame += rn; + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::process %s rn:%zd != inFrames:%ld output_frames_gen:%ld outFrames:%ld srcdata.input_frames_used:%ld\n", + f.name().latin1(), rn, inFrames, srcdata.output_frames_gen, outFrames, srcdata.input_frames_used); + #endif + + // We've reached the end of the file. Convert the number of frames read. + //rn = (double)rn * srcratio + 1; + //rn = (long)floor((double)rn * srcratio); + //if(rn > (size_t)outFrames) + // rn = outFrames; + // Stop attempting. + break; + } + } + + // If we still didn't get the desired number of output frames. + if(totalOutFrames != n) + { + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::process %s totalOutFrames:%ld != n:%d\n", f.name().latin1(), totalOutFrames, n); + #endif + + // Let's zero the rest of it. + long b = totalOutFrames * channel; + long e = n * channel; + for(long i = b; i < e; ++i) + //outbuffer[i] = 0.0f; + buffer[i] = 0.0f; + } + + //float* poutbuf = &outbuffer[0]; + float* poutbuf = outbuffer; + if(fchan == channel) + { + if(overwrite) + //for (size_t i = 0; i < rn; ++i) + for (int i = 0; i < n; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) = *poutbuf++; + } + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) += *poutbuf++; + } + } + else if((fchan == 2) && (channel == 1)) + { + // stereo to mono + if(overwrite) + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + *(buffer[0] + i) = poutbuf[i + i] + poutbuf[i + i + 1]; + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + *(buffer[0] + i) += poutbuf[i + i] + poutbuf[i + i + 1]; + } + else if((fchan == 1) && (channel == 2)) + { + // mono to stereo + if(overwrite) + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) = data; + *(buffer[1]+i) = data; + } + else + //for(size_t i = 0; i < rn; ++i) + for(int i = 0; i < n; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) += data; + *(buffer[1]+i) += data; + } + } + else + { + #ifdef AUDIOCONVERT_DEBUG + printf("RubberBandAudioConverter::process Channel mismatch: source chans:%d -> dst chans:%d\n", fchan, channel); + #endif + } + + return sfCurFrame; +} + +#endif // RUBBERBAND_SUPPORT diff --git a/muse/muse/audioconvert.h b/muse/muse/audioconvert.h new file mode 100644 index 00000000..ca0c3f88 --- /dev/null +++ b/muse/muse/audioconvert.h @@ -0,0 +1,95 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: audioconvert.h,v 1.1.1.1 2009/12/28 16:07:33 terminator356 Exp $ +// +// (C) Copyright 1999-2009 Werner Schweer (ws@seh.de) +// +// Audio converter module created by Tim terminator356 +//========================================================= + +#ifndef __AUDIOCONVERT_H__ +#define __AUDIOCONVERT_H__ + +#ifdef RUBBERBAND_SUPPORT +#include <RubberBandStretcher.h> +#endif + +#include <samplerate.h> +#include <sys/types.h> + +class SndFileR; + +//--------------------------------------------------------- +// AudioConverter +//--------------------------------------------------------- + +class AudioConverter +{ + int _refCount; + + public: + AudioConverter(); + ~AudioConverter(); + + AudioConverter* reference(); + static AudioConverter* release(AudioConverter* cv); + + off_t readAudio(SndFileR& /*sf*/, off_t /*sfCurFrame*/, unsigned /*offset*/, float** /*buffer*/, + int /*channels*/, int /*frames*/, bool /*doSeek*/, bool /*overwrite*/); + + virtual bool isValid() = 0; + virtual void reset() = 0; + virtual void setChannels(int ch) = 0; + virtual off_t process(SndFileR& /*sf*/, off_t /*sfCurFrame*/, float** /*buffer*/, + int /*channels*/, int /*frames*/, bool /*overwrite*/) = 0; // Interleaved buffer if stereo. +}; + +//--------------------------------------------------------- +// SRCAudioConverter +//--------------------------------------------------------- + +class SRCAudioConverter : public AudioConverter +{ + int _type; + int _channels; + SRC_STATE* _src_state; + + public: + SRCAudioConverter(int channels, int type); + ~SRCAudioConverter(); + + virtual bool isValid() { return _src_state != 0; } + virtual void reset(); + virtual void setChannels(int ch); + virtual off_t process(SndFileR& /*sf*/, off_t /*sfCurFrame*/, float** /*buffer*/, + int /*channels*/, int /*frames*/, bool /*overwrite*/); // Interleaved buffer if stereo. +}; + +#ifdef RUBBERBAND_SUPPORT + +//--------------------------------------------------------- +// RubberBandAudioConverter +//--------------------------------------------------------- + +class RubberBandAudioConverter : public AudioConverter +{ + int _options; + int _channels; + RubberBandStretcher* _rbs; + + public: + RubberBandAudioConverter(int channels, int options); + ~RubberBandAudioConverter(); + + virtual bool isValid() { return _rbs != 0; } + virtual void reset(); + virtual void setChannels(int ch); + virtual off_t process(SndFileR& /*sf*/, off_t /*sfCurFrame*/, float** /*buffer*/, + int /*channels*/, int /*frames*/, bool /*overwrite*/); // Interleaved buffer if stereo. +}; + +#endif // RUBBERBAND_SUPPORT + +#endif + diff --git a/muse/muse/audioprefetch.cpp b/muse/muse/audioprefetch.cpp index 99a64972..dbfbd614 100644 --- a/muse/muse/audioprefetch.cpp +++ b/muse/muse/audioprefetch.cpp @@ -18,6 +18,9 @@ #include "audio.h" #include "sync.h" +// Added by Tim. p3.3.20 +//#define AUDIOPREFETCH_DEBUG + enum { PREFETCH_TICK, PREFETCH_SEEK }; @@ -42,7 +45,8 @@ AudioPrefetch::AudioPrefetch(const char* name) { seekPos = ~0; writePos = ~0; - seekDone = true; + //seekDone = true; + seekCount = 0; } //--------------------------------------------------------- @@ -97,6 +101,10 @@ void AudioPrefetch::processMsg1(const void* m) seekPos = ~0; // invalidate cached last seek position break; case PREFETCH_SEEK: + #ifdef AUDIOPREFETCH_DEBUG + printf("AudioPrefetch::processMsg1 PREFETCH_SEEK msg->pos:%d\n", msg->pos); + #endif + // process seek in background seek(msg->pos); break; @@ -126,10 +134,17 @@ void AudioPrefetch::msgTick() void AudioPrefetch::msgSeek(unsigned samplePos, bool force) { if (samplePos == seekPos && !force) { - seekDone = true; + //seekDone = true; return; } - seekDone = false; + + ++seekCount; + //seekDone = false; + + #ifdef AUDIOPREFETCH_DEBUG + printf("AudioPrefetch::msgSeek samplePos:%u force:%d seekCount:%d\n", samplePos, force, seekCount); + #endif + PrefetchMsg msg; msg.id = PREFETCH_SEEK; msg.pos = samplePos; @@ -147,7 +162,7 @@ void AudioPrefetch::msgSeek(unsigned samplePos, bool force) void AudioPrefetch::prefetch(bool doSeek) { if (writePos == ~0U) { - printf("prefetch(): invalid write position\n"); + printf("AudioPrefetch::prefetch: invalid write position\n"); return; } if (song->loop() && !audio->bounce() && !extSyncFlag.value()) { @@ -169,8 +184,7 @@ void AudioPrefetch::prefetch(bool doSeek) float* bp[ch]; // printf("prefetch %d\n", writePos); if (track->prefetchFifo()->getWriteBuffer(ch, segmentSize, bp, writePos)) { - // Too many of these. Chokes muse. Turn on later. (muse works OK anyway). - //printf("Prefetch: NO BUFFER\n"); + printf("AudioPrefetch::prefetch No write buffer!\n"); continue; } //track->fetchData(writePos, segmentSize, bp); @@ -186,6 +200,26 @@ void AudioPrefetch::prefetch(bool doSeek) void AudioPrefetch::seek(unsigned seekTo) { // printf("seek %d\n", seekTo); + #ifdef AUDIOPREFETCH_DEBUG + printf("AudioPrefetch::seek to:%u seekCount:%d\n", seekTo, seekCount); + #endif + + // Speedup: More than one seek message pending? + // Eat up seek messages until we get to the very LATEST one, + // because all the rest which came before it are irrelevant now, + // and processing them all was taking extreme time, especially with + // resampling enabled. + // In particular, when the user 'slides' the play cursor back and forth + // there are MANY seek messages in the pipe, and with resampling enabled + // it was taking minutes to finish seeking. If the user hit play during that time, + // things were messed up (FIFO underruns, choppy intermittent sound etc). + // Added by Tim. p3.3.20 + if(seekCount > 1) + { + --seekCount; + return; + } + writePos = seekTo; WaveTrackList* tl = song->waves(); for (iWaveTrack it = tl->begin(); it != tl->end(); ++it) { @@ -202,9 +236,18 @@ void AudioPrefetch::seek(unsigned seekTo) prefetch(isFirstPrefetch); isFirstPrefetch = false; + + // To help speed things up even more, check the count again. Return if more seek messages are pending. + // Added by Tim. p3.3.20 + if(seekCount > 1) + { + --seekCount; + return; + } } seekPos = seekTo; - seekDone = true; + //seekDone = true; + --seekCount; } diff --git a/muse/muse/audioprefetch.h b/muse/muse/audioprefetch.h index ebd46c03..dda4d895 100644 --- a/muse/muse/audioprefetch.h +++ b/muse/muse/audioprefetch.h @@ -24,6 +24,8 @@ class AudioPrefetch : public Thread { void prefetch(bool doSeek); void seek(unsigned pos); + volatile int seekCount; + public: //AudioPrefetch(int prio, const char* name); AudioPrefetch(const char* name); @@ -34,7 +36,9 @@ class AudioPrefetch : public Thread { void msgTick(); void msgSeek(unsigned samplePos, bool force=false); - volatile bool seekDone; + + //volatile bool seekDone; + bool seekDone() const { return seekCount == 0; } }; extern AudioPrefetch* audioPrefetch; diff --git a/muse/muse/conf.cpp b/muse/muse/conf.cpp index 9398a7d3..13ef712c 100644 --- a/muse/muse/conf.cpp +++ b/muse/muse/conf.cpp @@ -638,6 +638,8 @@ void readConfiguration(Xml& xml, bool readOnlySequencer) mtcType= xml.parseInt(); else if (tag == "extSync") extSyncFlag.setValue(xml.parseInt()); + else if (tag == "useJackTransport") + useJackTransport = xml.parseInt(); else if (tag == "syncgentype") { // for compatibility //int syncGenType= xml.parseInt(); @@ -1058,6 +1060,7 @@ void MusE::writeGlobalConfiguration(int level, Xml& xml) const xml.nput(level, "<mtcoffset>%02d:%02d:%02d:%02d:%02d</mtcoffset>\n", mtcOffset.h(), mtcOffset.m(), mtcOffset.s(), mtcOffset.f(), mtcOffset.sf()); + //xml.intTag(level, "useJackTransport", useJackTransport); extSyncFlag.save(level, xml); // xml.intTag(level, "genMTCSync", genMTCSync); @@ -1159,6 +1162,7 @@ void MusE::writeConfiguration(int level, Xml& xml) const xml.nput(level, "<mtcoffset>%02d:%02d:%02d:%02d:%02d</mtcoffset>\n", mtcOffset.h(), mtcOffset.m(), mtcOffset.s(), mtcOffset.f(), mtcOffset.sf()); + xml.intTag(level, "useJackTransport", useJackTransport); extSyncFlag.save(level, xml); // xml.intTag(level, "genMTCSync", genMTCSync); diff --git a/muse/muse/driver/jack.cpp b/muse/muse/driver/jack.cpp index ef58bbfa..6a2c7e8a 100644 --- a/muse/muse/driver/jack.cpp +++ b/muse/muse/driver/jack.cpp @@ -9,6 +9,8 @@ #include <stdio.h> #include <errno.h> #include <stdarg.h> +//#include <time.h> +#include <unistd.h> #include "audio.h" #include "globals.h" @@ -17,6 +19,8 @@ #include "track.h" #include "pos.h" #include "tempo.h" +#include "sync.h" +#include "utils.h" #define JACK_DEBUG 0 @@ -131,6 +135,9 @@ static int processSync(jack_transport_state_t state, jack_position_t* pos, void* if (JACK_DEBUG) printf("processSync()\n"); + if(!useJackTransport) + return 1; + int audioState = Audio::STOP; switch (state) { case JackTransportStopped: @@ -243,6 +250,8 @@ JackAudioDevice::JackAudioDevice(jack_client_t* cl, char * name) //JackAudioDevice::jackStarted=false; strcpy(jackRegisteredName, name); _client = cl; + dummyState = Audio::STOP; + dummyPos = 0; } //--------------------------------------------------------- @@ -590,6 +599,12 @@ void JackAudioDevice::registerClient() if(!checkJackClient(_client)) return; jack_set_process_callback(_client, processAudio, 0); jack_set_sync_callback(_client, processSync, 0); + // FIXME: FIXME: + // Added by Tim. p3.3.20 + // Did not help. Seek during play: Jack keeps switching to STOP state after about 1-2 seconds timeout if sync is holding it up. + // Nothing in MusE seems to be telling it to stop. + //jack_set_sync_timeout(_client, 5000000); // Change default 2 to 5 second sync timeout because prefetch may be very slow esp. with resampling ! + jack_on_shutdown(_client, processShutdown, 0); jack_set_buffer_size_callback(_client, bufsize_callback, 0); jack_set_sample_rate_callback(_client, srate_callback, 0); @@ -735,6 +750,7 @@ void JackAudioDevice::start(int /*priority*/) } } } + undoSetuid(); //MUSE_DEBUG("JackAudioDevice::start()\n"); @@ -758,13 +774,53 @@ void JackAudioDevice::stop() } //--------------------------------------------------------- +// transportQuery +//--------------------------------------------------------- + +jack_transport_state_t JackAudioDevice::transportQuery(jack_position_t* pos) +{ + if (JACK_DEBUG) + printf("JackAudioDevice::transportQuery pos:%d\n", (unsigned int)pos->frame); + + // TODO: Compose and return a state if MusE is disengaged from Jack transport. + + return jack_transport_query(_client, pos); +} + +//--------------------------------------------------------- +// getCurFrame +//--------------------------------------------------------- + +unsigned int JackAudioDevice::getCurFrame() +{ + if (JACK_DEBUG) + printf("JackAudioDevice::getCurFrame pos.frame:%d\n", pos.frame); + + if(!useJackTransport) + return (unsigned int)dummyPos; + + return pos.frame; +} + +//--------------------------------------------------------- // framePos //--------------------------------------------------------- int JackAudioDevice::framePos() const { + //if(!useJackTransport) + //{ + // if (JACK_DEBUG) + // printf("JackAudioDevice::framePos dummyPos:%d\n", dummyPos); + // return dummyPos; + //} + if(!checkJackClient(_client)) return 0; jack_nframes_t n = jack_frame_time(_client); + + if (JACK_DEBUG) + printf("JackAudioDevice::framePos jack frame:%d\n", (int)n); + return (int)n; } @@ -877,10 +933,24 @@ void JackAudioDevice::unregisterPort(void* p) int JackAudioDevice::getState() { + // If we're not using Jack's transport, just return current state. + if(!useJackTransport) + { + //pos.valid = jack_position_bits_t(0); + //pos.frame = audio->pos().frame(); + //return audio->getState(); + if (JACK_DEBUG) + printf("JackAudioDevice::getState dummyState:%d\n", dummyState); + return dummyState; + } + //if (JACK_DEBUG) // printf("JackAudioDevice::getState ()\n"); if(!checkJackClient(_client)) return 0; transportState = jack_transport_query(_client, &pos); + if (JACK_DEBUG) + printf("JackAudioDevice::getState transportState:%d\n", transportState); + switch (transportState) { case JackTransportStopped: return Audio::STOP; @@ -920,46 +990,136 @@ void JackAudioDevice::setFreewheel(bool f) } //--------------------------------------------------------- +// dummySync +//--------------------------------------------------------- + +bool JackAudioDevice::dummySync(int state) +{ + // Roughly segment time length. + //timespec ts = { 0, (1000000000 * segmentSize) / sampleRate }; // In nanoseconds. + unsigned int sl = (1000000 * segmentSize) / sampleRate; // In microseconds. + + double ct = curTime(); + // Wait for a default maximum of 5 seconds. + // Similar to how Jack is supposed to wait a default of 2 seconds for slow clients. + // TODO: Make this timeout a 'settings' option so it can be applied both to Jack and here. + while((curTime() - ct) < 5.0) + { + // Is MusE audio ready to roll? + if(audio->sync(state, dummyPos)) + return true; + + // Not ready. Wait a 'segment', try again... + //nanosleep(&ts, NULL); + usleep(sl); // usleep is supposed to be obsolete! + } + + //if(JACK_DEBUG) + printf("JackAudioDevice::dummySync Sync timeout - audio not ready!\n"); + + return false; +} + +//--------------------------------------------------------- // startTransport //--------------------------------------------------------- void JackAudioDevice::startTransport() - { + { if (JACK_DEBUG) printf("JackAudioDevice::startTransport()\n"); + + // If we're not using Jack's transport, just pass PLAY and current frame along + // as if processSync was called. + if(!useJackTransport) + { + //dummyState = Audio::START_PLAY; + + // Is MusE audio ready to roll? + //if(dummySync(dummyState)) + if(dummySync(Audio::START_PLAY)) + { + // MusE audio is ready to roll. Let's play. + dummyState = Audio::PLAY; + return; + } + + // Ready or not, we gotta roll. Similar to how Jack is supposed to roll anyway. + dummyState = Audio::PLAY; + return; + } + if(!checkJackClient(_client)) return; // printf("JACK: startTransport\n"); jack_transport_start(_client); - } + } //--------------------------------------------------------- // stopTransport //--------------------------------------------------------- void JackAudioDevice::stopTransport() - { + { if (JACK_DEBUG) printf("JackAudioDevice::stopTransport()\n"); + + dummyState = Audio::STOP; + + if(!useJackTransport) + { + //dummyState = Audio::STOP; + return; + } + if(!checkJackClient(_client)) return; if (transportState != JackTransportStopped) { // printf("JACK: stopTransport\n"); jack_transport_stop(_client); transportState=JackTransportStopped; } - } + } //--------------------------------------------------------- // seekTransport //--------------------------------------------------------- void JackAudioDevice::seekTransport(unsigned frame) - { + { if (JACK_DEBUG) printf("JackAudioDevice::seekTransport() frame:%d\n", frame); + + dummyPos = frame; + if(!useJackTransport) + { + // If we're not using Jack's transport, just pass the current state and new frame along + // as if processSync was called. + //dummyPos = frame; + int tempState = dummyState; + //dummyState = Audio::START_PLAY; + + // Is MusE audio ready yet? + //audio->sync(dummyState, dummyPos); + //if(dummySync(dummyState)) + if(dummySync(Audio::START_PLAY)) + { + dummyState = tempState; + return; + } + + // Not ready, resume previous state anyway. + // FIXME: Observed: Seek during play: Jack transport STOPs on timeout. + // Docs say when starting play, transport will roll anyway, ready or not (observed), + // but don't mention what should happen on seek during play. + // And setting the slow-sync timeout doesn't seem to do anything! + //dummyState = tempState; + dummyState = Audio::STOP; + return; + } + if(!checkJackClient(_client)) return; // printf("JACK: seekTransport %d\n", frame); jack_transport_locate(_client, frame); - } + } //--------------------------------------------------------- // seekTransport @@ -969,6 +1129,32 @@ void JackAudioDevice::seekTransport(const Pos &p) { if (JACK_DEBUG) printf("JackAudioDevice::seekTransport() frame:%d\n", p.frame()); + + dummyPos = p.frame(); + if(!useJackTransport) + { + // If we're not using Jack's transport, just pass the current state and new frame along + // as if processSync was called. + //dummyPos = p.frame(); + int tempState = dummyState; + //dummyState = Audio::START_PLAY; + + // Is MusE audio ready yet? + //audio->sync(dummyState, dummyPos); + //if(dummySync(dummyState)) + if(dummySync(Audio::START_PLAY)) + { + dummyState = tempState; + return; + } + + // Not ready, resume previous state anyway. + // FIXME: See fixme in other seekTransport... + //dummyState = tempState; + dummyState = Audio::STOP; + return; + } + if(!checkJackClient(_client)) return; /* @@ -1016,13 +1202,21 @@ int JackAudioDevice::setMaster(bool f) int r = 0; if(f) { - // Make Muse the Jack timebase master. Do it unconditionally (second param = 0). - r = jack_set_timebase_callback(_client, 0, (JackTimebaseCallback) timebase_callback, 0); - if(debugMsg || JACK_DEBUG) + if(useJackTransport) { - if(r) - printf("JackAudioDevice::setMaster jack_set_timebase_callback failed: result:%d\n", r); - } + // Make Muse the Jack timebase master. Do it unconditionally (second param = 0). + r = jack_set_timebase_callback(_client, 0, (JackTimebaseCallback) timebase_callback, 0); + if(debugMsg || JACK_DEBUG) + { + if(r) + printf("JackAudioDevice::setMaster jack_set_timebase_callback failed: result:%d\n", r); + } + } + else + { + r = 1; + printf("JackAudioDevice::setMaster cannot set master because useJackTransport is false\n"); + } } else { diff --git a/muse/muse/driver/jackaudio.h b/muse/muse/driver/jackaudio.h index 78e88313..7a73eaf5 100644 --- a/muse/muse/driver/jackaudio.h +++ b/muse/muse/driver/jackaudio.h @@ -24,6 +24,8 @@ class JackAudioDevice : public AudioDevice { jack_transport_state_t transportState; jack_position_t pos; char jackRegisteredName[8]; + int dummyState; + int dummyPos; public: JackAudioDevice(jack_client_t* cl, char * jack_id_string); @@ -33,6 +35,7 @@ class JackAudioDevice : public AudioDevice { //virtual void start(); virtual void start(int); virtual void stop (); + virtual bool dummySync(int state); // Artificial sync when not using Jack transport. virtual int framePos() const; @@ -57,7 +60,7 @@ class JackAudioDevice : public AudioDevice { virtual void* findPort(const char* name); virtual QString portName(void* port); virtual int getState(); - virtual unsigned int getCurFrame() { return pos.frame; } + virtual unsigned int getCurFrame(); virtual bool isRealtime() { return jack_is_realtime(_client); } virtual int realtimePriority() const; virtual void startTransport(); @@ -65,8 +68,7 @@ class JackAudioDevice : public AudioDevice { virtual void seekTransport(unsigned frame); virtual void seekTransport(const Pos &p); virtual void setFreewheel(bool f); - jack_transport_state_t transportQuery(jack_position_t* pos) - { return jack_transport_query(_client, pos); } + jack_transport_state_t transportQuery(jack_position_t* pos); void graphChanged(); virtual int setMaster(bool f); diff --git a/muse/muse/event.cpp b/muse/muse/event.cpp index 704335a5..16a5c581 100644 --- a/muse/muse/event.cpp +++ b/muse/muse/event.cpp @@ -8,10 +8,15 @@ #include <stdio.h> // #include <memory.h> +#include "audioconvert.h" #include "event.h" #include "eventbase.h" #include "waveevent.h" #include "midievent.h" +//#include "globals.h" + +// Added by Tim. p3.3.20 +#define USE_SAMPLERATE //--------------------------------------------------------- // Event @@ -63,7 +68,17 @@ Event Event::clone() return Event(ev->clone()); } +Event::Event() +{ + ev = 0; + _sfCurFrame = 0; + _audConv = 0; +} + Event::Event(EventType t) { + _sfCurFrame = 0; + _audConv = 0; + if (t == Wave) ev = new WaveEventBase(t); else @@ -71,13 +86,32 @@ Event::Event(EventType t) { ++(ev->refCount); } Event::Event(const Event& e) { + _sfCurFrame = 0; + _audConv = 0; + ev = e.ev; - if (ev) - ++(ev->refCount); - } + if(ev) + ++(ev->refCount); + + #ifdef USE_SAMPLERATE + //_audConv = AudioConverter::getAudioConverter(e._audConv); + if(e._audConv) + _audConv = e._audConv->reference(); + #endif + } Event::Event(EventBase* eb) { + _sfCurFrame = 0; + _audConv = 0; + ev = eb; ++(ev->refCount); + + #ifdef USE_SAMPLERATE + if(!ev->sndFile().isNull()) + //_audConv = AudioConverter::getAudioConverter(eb, SRC_SINC_MEDIUM_QUALITY); + //_audConv = new AudioConverter(ev->sndFile().channels(), SRC_SINC_MEDIUM_QUALITY); + _audConv = new SRCAudioConverter(ev->sndFile().channels(), SRC_SINC_MEDIUM_QUALITY); + #endif } Event::~Event() { @@ -85,6 +119,10 @@ Event::~Event() { delete ev; ev=0; } + + #ifdef USE_SAMPLERATE + AudioConverter::release(_audConv); + #endif } bool Event::empty() const { return ev == 0; } @@ -103,6 +141,7 @@ void Event::setType(EventType t) { } Event& Event::operator=(const Event& e) { + /* if (ev == e.ev) return *this; if (ev && --(ev->refCount) == 0) { @@ -113,7 +152,31 @@ Event& Event::operator=(const Event& e) { if (ev) ++(ev->refCount); return *this; + */ + + if (ev != e.ev) + { + if (ev && --(ev->refCount) == 0) { + delete ev; + ev = 0; + } + ev = e.ev; + if (ev) + ++(ev->refCount); + } + + #ifdef USE_SAMPLERATE + if (_audConv != e._audConv) + { + if(_audConv) + AudioConverter::release(_audConv); + //_audConv = AudioConverter::getAudioConverter(e._audConv); + _audConv = e._audConv->reference(); + } + #endif + return *this; } + bool Event::operator==(const Event& e) const { return ev == e.ev; } @@ -123,7 +186,35 @@ bool Event::selected() const { return ev->_selected; } void Event::setSelected(bool val) { ev->_selected = val; } void Event::move(int offset) { ev->move(offset); } -void Event::read(Xml& xml) { ev->read(xml); } +//void Event::read(Xml& xml) { ev->read(xml); } +void Event::read(Xml& xml) +{ + ev->read(xml); + + #ifdef USE_SAMPLERATE + if(!ev->sndFile().isNull()) + { + if(_audConv) + { + _audConv->setChannels(ev->sndFile().channels()); + } + else + { + //int srcerr; + //if(debugMsg) + // printf("Event::read Creating samplerate converter with %d channels\n", ev->sndFile().channels()); + //_src_state = src_new(SRC_SINC_MEDIUM_QUALITY, ev->sndFile().channels(), &srcerr); +// _audConv = new AudioConverter(ev->sndFile().channels(), SRC_SINC_MEDIUM_QUALITY); + _audConv = new SRCAudioConverter(ev->sndFile().channels(), SRC_SINC_MEDIUM_QUALITY); + //if(!_src_state) + //if(!_audConv) + // printf("Event::read Creation of samplerate converter with %d channels failed:%s\n", ev->sndFile().channels(), src_strerror(srcerr)); + } + } + #endif +} + + //void Event::write(int a, Xml& xml, const Pos& o) const { ev->write(a, xml, o); } void Event::write(int a, Xml& xml, const Pos& o, bool forceWavePaths) const { ev->write(a, xml, o, forceWavePaths); } void Event::dump(int n) const { ev->dump(n); } @@ -155,13 +246,32 @@ void Event::setName(const QString& s) { ev->setName(s); } int Event::spos() const { return ev->spos(); } void Event::setSpos(int s) { ev->setSpos(s); } SndFileR Event::sndFile() const { return ev->sndFile(); } -void Event::setSndFile(SndFileR& sf) { ev->setSndFile(sf); } + +//void Event::setSndFile(SndFileR& sf) { ev->setSndFile(sf); } +void Event::setSndFile(SndFileR& sf) +{ + ev->setSndFile(sf); + + #ifdef USE_SAMPLERATE + //if(_audConv) + if(_audConv && !sf.isNull()) + { + //_audConv->setSndFile(sf); + //if(sf.isNull()) + // AudioConverter::release(_audConv); + //else + _audConv->setChannels(sf.channels()); + } + #endif +} //void Event::read(unsigned offset, float** bpp, int channels, int nn, bool overwrite) void Event::readAudio(unsigned offset, float** bpp, int channels, int nn, bool doSeek, bool overwrite) { //ev->read(offset, bpp, channels, nn, overwrite); - ev->readAudio(offset, bpp, channels, nn, doSeek, overwrite); + //ev->readAudio(offset, bpp, channels, nn, doSeek, overwrite); + //_sfCurFrame = ev->readAudio(_src_state, _sfCurFrame, offset, bpp, channels, nn, doSeek, overwrite); + _sfCurFrame = ev->readAudio(_audConv, _sfCurFrame, offset, bpp, channels, nn, doSeek, overwrite); } void Event::setTick(unsigned val) { ev->setTick(val); } unsigned Event::tick() const { return ev->tick(); } diff --git a/muse/muse/event.h b/muse/muse/event.h index 1bbded12..2a034a36 100644 --- a/muse/muse/event.h +++ b/muse/muse/event.h @@ -11,6 +11,8 @@ #include <qstring.h> #include <map> +//#include <samplerate.h> +#include <sys/types.h> #include "wave.h" // wg. SndFile #include "pos.h" @@ -20,6 +22,7 @@ enum EventType { Note, Controller, Sysex, PAfter, CAfter, Meta, Wave }; class Xml; class EventBase; +class AudioConverter; //--------------------------------------------------------- // Event @@ -28,8 +31,12 @@ class EventBase; class Event { EventBase* ev; + off_t _sfCurFrame; + AudioConverter* _audConv; + public: - Event() { ev = 0; } + //Event() { ev = 0; } + Event(); Event(EventType t); Event(const Event& e); Event(EventBase* eb); @@ -79,6 +86,7 @@ class Event { void setName(const QString& s); int spos() const; void setSpos(int s); + //AudioConverter* audioConverter() { return _audConv;} SndFileR sndFile() const; virtual void setSndFile(SndFileR& sf); //virtual void read(unsigned offset, float** bpp, int channels, int nn, bool overwrite = true); diff --git a/muse/muse/eventbase.h b/muse/muse/eventbase.h index 9e29c81a..4a49b6f8 100644 --- a/muse/muse/eventbase.h +++ b/muse/muse/eventbase.h @@ -9,8 +9,13 @@ #ifndef __EVENTBASE_H__ #define __EVENTBASE_H__ +//#include <samplerate.h> +#include <sys/types.h> + #include "pos.h" +class AudioConverter; + //--------------------------------------------------------- // EventBase //--------------------------------------------------------- @@ -76,8 +81,13 @@ class EventBase : public PosLen { virtual SndFileR sndFile() const { return 0; } virtual void setSndFile(SndFileR&) { } virtual EventBase* clone() = 0; + //virtual void read(unsigned /*offset*/, float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool overwrite = true) {} - virtual void readAudio(unsigned /*offset*/, float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/) {} + //virtual void readAudio(unsigned /*offset*/, float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/) {} + //virtual off_t readAudio(SRC_STATE* /*src_state*/, off_t /*sfCurFrame*/, unsigned /*offset*/, + // float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/) { return 0; } + virtual off_t readAudio(AudioConverter* /*audConv*/, off_t /*sfCurFrame*/, unsigned /*offset*/, + float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/) { return 0; } }; #endif diff --git a/muse/muse/midiport.cpp b/muse/muse/midiport.cpp index 91444864..568fa68d 100644 --- a/muse/muse/midiport.cpp +++ b/muse/muse/midiport.cpp @@ -694,8 +694,8 @@ int MidiPort::hwCtrlState(int ch, int ctrl) const ch &= 0xff; iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) { - if (debugMsg) - printf("hwCtrlState: chan %d ctrl 0x%x not found\n", ch, ctrl); + //if (debugMsg) + // printf("hwCtrlState: chan %d ctrl 0x%x not found\n", ch, ctrl); return CTRL_VAL_UNKNOWN; } MidiCtrlValList* vl = cl->second; @@ -796,9 +796,9 @@ int MidiPort::getCtrl(int ch, int tick, int ctrl) const { iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) { - if (debugMsg) - printf("getCtrl: controller %d(0x%x) for channel %d not found size %zd\n", - ctrl, ctrl, ch, _controller->size()); + //if (debugMsg) + // printf("getCtrl: controller %d(0x%x) for channel %d not found size %zd\n", + // ctrl, ctrl, ch, _controller->size()); return CTRL_VAL_UNKNOWN; } return cl->second->value(tick); @@ -808,7 +808,9 @@ int MidiPort::getCtrl(int ch, int tick, int ctrl, Part* part) const { iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) { - if (debugMsg) + //if (debugMsg) + // printf("getCtrl: controller %d(0x%x) for channel %d not found size %zd\n", + // ctrl, ctrl, ch, _controller->size()); return CTRL_VAL_UNKNOWN; } return cl->second->value(tick, part); diff --git a/muse/muse/sync.cpp b/muse/muse/sync.cpp index 14c1f922..fe3dcfee 100644 --- a/muse/muse/sync.cpp +++ b/muse/muse/sync.cpp @@ -36,6 +36,7 @@ BValue extSyncFlag(0, "extSync"); // false - MASTER, true - SLAVE //bool acceptMTC = false; //bool acceptMC = true; //bool acceptMMC = true; +bool useJackTransport = true; static MTC mtcCurTime; static int mtcState; // 0-7 next expected quarter message diff --git a/muse/muse/sync.h b/muse/muse/sync.h index a2f4e1f9..f09d410c 100644 --- a/muse/muse/sync.h +++ b/muse/muse/sync.h @@ -108,6 +108,7 @@ extern BValue extSyncFlag; //extern bool acceptMC; //extern bool acceptMMC; extern int curMidiSyncInPort; +extern bool useJackTransport; #endif diff --git a/muse/muse/thread.cpp b/muse/muse/thread.cpp index 0576feda..444d5219 100644 --- a/muse/muse/thread.cpp +++ b/muse/muse/thread.cpp @@ -302,12 +302,10 @@ void Thread::loop() pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); - int policy; - /* + int policy = 0; if ((policy = sched_getscheduler (0)) < 0) { printf("Thread: Cannot get current client scheduler: %s\n", strerror(errno)); } - */ /* if (debugMsg) @@ -422,7 +420,6 @@ bool Thread::sendMsg1(const void* m, int n) void Thread::readMsg() { - ThreadMsg* p; if (read(toThreadFdr, &p, sizeof(p)) != sizeof(p)) { perror("Thread::readMessage(): read pipe failed"); diff --git a/muse/muse/wave.cpp b/muse/muse/wave.cpp index 0b52f743..ac26d5e9 100644 --- a/muse/muse/wave.cpp +++ b/muse/muse/wave.cpp @@ -29,9 +29,6 @@ //#define WAVE_DEBUG //#define WAVE_DEBUG_PRC -// Added by Tim. p3.3.18 -//#define USE_SAMPLERATE - const char* audioFilePattern[] = { "Wave/Binary (*.wav *.ogg *.bin)", "Wave (*.wav *.ogg)", @@ -56,8 +53,6 @@ SndFile::SndFile(const QString& name) sfUI = 0; csize = 0; cache = 0; - _src_state = 0; - _src_ratio = 1.0; openFlag = false; sndFiles.push_back(this); refCount=0; @@ -80,9 +75,6 @@ SndFile::~SndFile() delete[] cache; cache = 0; } - - if(_src_state) - src_delete(_src_state); } //--------------------------------------------------------- @@ -103,17 +95,6 @@ bool SndFile::openRead() if (sf == 0 || sfUI == 0) return true; - int srcerr; - if(_src_state) - src_delete(_src_state); - _src_state = src_new(SRC_SINC_MEDIUM_QUALITY, sfinfo.channels, &srcerr); - if(!_src_state) - printf("SndFile::openRead Creation of samplerate converter (channels:%d) failed: %s\n", sfinfo.channels, src_strerror(srcerr)); - //_src_ratio = (double)sampleRate / (double)sfinfo.samplerate; - //srcerr = src_set_ratio(_src_state, _src_ratio); - //if(srcerr != 0) - // printf("SndFile::openRead Setting of samplerate converter ratio failed: %s\n", src_strerror(srcerr)); - writeFlag = false; openFlag = true; QString cacheName = finfo->dirPath(true) + QString("/") + finfo->baseName(true) + QString(".wca"); @@ -366,17 +347,6 @@ bool SndFile::openWrite() QString cacheName = finfo->dirPath(true) + QString("/") + finfo->baseName(true) + QString(".wca"); readCache(cacheName, true); - - int srcerr; - if(_src_state) - src_delete(_src_state); - _src_state = src_new(SRC_SINC_MEDIUM_QUALITY, sfinfo.channels, &srcerr); - if(!_src_state) - printf("SndFile::openWrite Creation of samplerate converter (channels:%d) failed: %s\n", sfinfo.channels, src_strerror(srcerr)); - //_src_ratio = (double)sampleRate / (double)sfinfo.samplerate; - //srcerr = src_set_ratio(_src_state, _src_ratio); - //if(srcerr != 0) - // printf("SndFile::openRead Setting of samplerate converter ratio failed: %s\n", src_strerror(srcerr)); } return sf == 0; } @@ -468,17 +438,6 @@ void SndFile::setFormat(int fmt, int ch, int rate) sfinfo.format = fmt; sfinfo.seekable = true; sfinfo.frames = 0; - - int srcerr; - if(_src_state) - src_delete(_src_state); - _src_state = src_new(SRC_SINC_MEDIUM_QUALITY, sfinfo.channels, &srcerr); - if(!_src_state) - printf("SndFile::setFormat Creation of samplerate converter (channels:%d) failed: %s\n", sfinfo.channels, src_strerror(srcerr)); - //_src_ratio = (double)sampleRate / (double)sfinfo.samplerate; - //srcerr = src_set_ratio(_src_state, _src_ratio); - //if(srcerr != 0) - // printf("SndFile::openRead Setting of samplerate converter ratio failed: %s\n", src_strerror(srcerr)); } //--------------------------------------------------------- @@ -490,120 +449,7 @@ size_t SndFile::read(int srcChannels, float** dst, size_t n, bool overwrite) // Changed by Tim. p3.3.17 //float *buffer = new float[n * sfinfo.channels]; float buffer[n * sfinfo.channels]; - - size_t rn; - - // Do we need to resample? - // FIXME: Disabled resampling for now... -#ifdef USE_SAMPLERATE - if(sampleRate == sfinfo.samplerate) - { - rn = sf_readf_float(sf, buffer, n); - } - else - { - if(sfinfo.samplerate == 0) - { - if(debugMsg) - printf("SndFile::read Using SRC: Error: File samplerate is zero!\n"); - return 0; - } - - // Ratio is defined as output sample rate over input samplerate. - double srcratio = (double)sampleRate / (double)sfinfo.samplerate; - long outFrames = n; - //long outSize = n * sfinfo.channels; - - //long inSize = long(outSize * srcratio) + 1 // From MusE-2 file converter. - //long inSize = (long)floor(((double)outSize / srcratio)); // From simplesynth. - //long inFrames = (long)floor(((double)outFrames / srcratio)); // From simplesynth. - long inFrames = (long)ceil(((double)outFrames / srcratio)); // From simplesynth. - //long inFrames = (long)floor(double(outFrames * sfinfo.samplerate) / double(sampleRate)); // From simplesynth. - - // Extra input compensation - sometimes src requires more input frames than expected in order to - // always get a reliable number of used out frames ! - //inFrames = inFrames / (srcratio / 2.0); - long inComp = 10; - inFrames += inComp; - - long inSize = inFrames * sfinfo.channels; - - float inbuffer[inSize]; - - rn = sf_readf_float(sf, inbuffer, inFrames); - - // convert - SRC_DATA srcdata; - srcdata.data_in = inbuffer; - srcdata.data_out = buffer; - //srcdata.input_frames = inSize; - srcdata.input_frames = rn; - srcdata.output_frames = outFrames; - srcdata.end_of_input = ((long)rn != inFrames); - srcdata.src_ratio = srcratio; - - #ifdef WAVE_DEBUG_PRC - printf("SndFile::read SampleRate %s inFrames:%ld inSize:%ld outFrames:%ld outSize:%ld rn:%d", name().latin1(), inFrames, inSize, outFrames, n * sfinfo.channels, rn); - #endif - - int srcerr = src_process(_src_state, &srcdata); - if(srcerr != 0) - { - printf("\nSndFile::read SampleRate converter src_process failed: %s\n", src_strerror(srcerr)); - return 0; - } - - #ifdef WAVE_DEBUG_PRC - printf(" frames used in:%ld out:%ld\n", srcdata.input_frames_used, srcdata.output_frames_gen); - #endif - - // If the number of frames read by the soundfile equals the input frames, go back. - // Otherwise we have reached the end of the file, so going back is useless since - // there shouldn't be any further calls. (Definitely get buffer underruns if further calls!) - if((long)rn == inFrames) - { - // Go back by the amount of unused frames. - sf_count_t seekn = inFrames - srcdata.input_frames_used; - #ifdef WAVE_DEBUG_PRC - printf("SndFile::read seeking:%ld\n", seekn); - #endif - sf_seek(sf, -seekn, SEEK_CUR); - } - - if(debugMsg) - { - if(srcdata.output_frames_gen != outFrames) - printf("SndFile::read SampleRate %s output_frames_gen:%ld != outFrames:%ld outSize:%u inFrames:%ld srcdata.input_frames_used:%ld inSize:%ld rn:%d\n", name().latin1(), srcdata.output_frames_gen, outFrames, n * sfinfo.channels, inFrames, srcdata.input_frames_used, inSize, rn); - } - - if(inFrames != (long)rn) - { - // Back-convert. - long d = inFrames - (long)rn; - //rn = (double)d * srcratio + 1; - rn = (long)floor((double)d * srcratio); - } - else - if(srcdata.output_frames_gen < outFrames) - { - // SRC didn't give us the number of frames we requested. - // This can occasionally be radically different from the requested frames, or zero, - // even when ample excess input frames are supplied. - // We're not done converting yet - we haven't reached the end of the file. - // We must do something with the buffer. So let's zero whatever SRC didn't fill. - // FIXME: Instead of zeroing, try processing more input data until the out buffer is full. - long b = srcdata.output_frames_gen * sfinfo.channels; - long e = outFrames * sfinfo.channels; - for(long i = b; i < e; ++i) - buffer[i] = 0.0f; - rn = outFrames; - } - else - rn = srcdata.output_frames_gen; - } -#else - rn = sf_readf_float(sf, buffer, n); -#endif + size_t rn = sf_readf_float(sf, buffer, n); float* src = buffer; int dstChannels = sfinfo.channels; @@ -720,36 +566,7 @@ size_t SndFile::write(int srcChannels, float** src, size_t n) off_t SndFile::seek(off_t frames, int whence) { - // Changed by Tim. p3.3.17 - //return sf_seek(sf, frames, whence); - - off_t n = frames; - - // FIXME: Disabled resampling for now... -#ifdef USE_SAMPLERATE - if(sfinfo.samplerate != sampleRate) - { - double srcratio = (double)sfinfo.samplerate / (double)sampleRate; - //long inSize = long((double)frames * _src_ratio) + 1 // From MusE-2 file converter. - n = (off_t)floor(((double)frames * srcratio)); // From simplesynth. - - // Added by Tim. p3.3.17 - #ifdef WAVE_DEBUG - printf("SndFile::seek frames:%ld converted to frames:%ld whence:%d\n", frames, n, whence); - #endif - - n = sf_seek(sf, n, whence); - - // Reset the src converter. - int srcerr = src_reset(_src_state); - if(srcerr != 0) - printf("SndFile::seek Samplerate converter reset failed: %s\n", src_strerror(srcerr)); - } - else -#endif - n = sf_seek(sf, n, whence); - - return n; + return sf_seek(sf, frames, whence); } //--------------------------------------------------------- diff --git a/muse/muse/wave.h b/muse/muse/wave.h index d5c5eaea..5a7997fe 100644 --- a/muse/muse/wave.h +++ b/muse/muse/wave.h @@ -13,7 +13,6 @@ #include <list> #include <qfileinfo.h> #include <sndfile.h> -#include <samplerate.h> class Xml; @@ -53,9 +52,6 @@ class SndFile { SampleV** cache; int csize; //!< frames in cache - SRC_STATE* _src_state; - double _src_ratio; - void writeCache(const QString& path); bool openFlag; @@ -94,9 +90,9 @@ class SndFile { unsigned format() const; int sampleBits() const; void setFormat(int fmt, int ch, int rate); - double srcRatio() { return _src_ratio; } size_t read(int channel, float**, size_t, bool overwrite = true); + size_t readDirect(float* buf, size_t n) { return sf_readf_float(sf, buf, n); } size_t write(int channel, float**, size_t); off_t seek(off_t frames, int whence); @@ -151,6 +147,8 @@ class SndFileR { size_t read(int channel, float** f, size_t n, bool overwrite = true) { return sf->read(channel, f, n, overwrite); } + size_t readDirect(float* f, size_t n) { return sf->readDirect(f, n); } + size_t write(int channel, float** f, size_t n) { return sf->write(channel, f, n); } diff --git a/muse/muse/waveevent.cpp b/muse/muse/waveevent.cpp index 6bd7150e..dd18a2ff 100644 --- a/muse/muse/waveevent.cpp +++ b/muse/muse/waveevent.cpp @@ -6,16 +6,20 @@ // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) //========================================================= +#include "audioconvert.h" #include "globals.h" #include "event.h" #include "waveevent.h" #include "xml.h" #include "wave.h" #include <iostream> +#include <math.h> // Added by Tim. p3.3.18 -//#define WAVEEVENT_DEBUG //#define USE_SAMPLERATE +// +//#define WAVEEVENT_DEBUG +//#define WAVEEVENT_DEBUG_PRC //--------------------------------------------------------- // WaveEvent @@ -134,22 +138,294 @@ void WaveEventBase::write(int level, Xml& xml, const Pos& offset, bool forcePath } //void WaveEventBase::read(unsigned offset, float** buffer, int channel, int n, bool overwrite) -void WaveEventBase::readAudio(unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) - { +//void WaveEventBase::readAudio(unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) +//off_t WaveEventBase::readAudio(SRC_STATE* src_state, off_t sfCurFrame, unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) +off_t WaveEventBase::readAudio(AudioConverter* audConv, off_t sfCurFrame, unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) +{ + // Added by Tim. p3.3.17 + #ifdef WAVEEVENT_DEBUG_PRC + printf("WaveEventBase::readAudio audConv:%p sfCurFrame:%ld offset:%u channel:%d n:%d\n", audConv, sfCurFrame, offset, channel, n); + #endif + + // Changed by Tim. p3.3.18 + #ifdef USE_SAMPLERATE + + // If we have a valid audio converter then use it to do the processing. Otherwise just a normal seek + read. + if(audConv) + //sfCurFrame = audConv->process(f, sfCurFrame, offset + _spos, buffer, channel, n, doSeek, overwrite); + sfCurFrame = audConv->readAudio(f, sfCurFrame, offset, buffer, channel, n, doSeek, overwrite); + else + { + if(!f.isNull()) + { + sfCurFrame = f.seek(offset + _spos, 0); + sfCurFrame += f.read(channel, buffer, n, overwrite); + } + } + return sfCurFrame; + + /* + unsigned fsrate = f.samplerate(); + int fchan = f.channels(); + off_t frame = offset + _spos; + //bool resample = src_state && ((unsigned)sampleRate != fsrate); + bool resample = audConv && audConv->isValid() && ((unsigned)sampleRate != fsrate); + + // Is a 'transport' seek requested? (Not to be requested with every read! Should only be for 'first read' seeks, or positional 'transport' seeks.) + // Due to the support of sound file references in MusE, seek must ALWAYS be done before read, as before, + // except now we alter the seek position if sample rate conversion is being used and remember the seek positions. + if(doSeek) + { + if(!resample) + { + // Sample rates are the same. Just a regular seek, no conversion. + sfCurFrame = f.seek(frame, 0); + } + else + { + // Sample rates are different. Seek to a calculated 'sample rate ratio factored' position. + + double srcratio = (double)fsrate / (double)sampleRate; + //long inSize = long((double)frames * _src_ratio) + 1 // From MusE-2 file converter. + off_t newfr = (off_t)floor(((double)frame * srcratio)); // From simplesynth. + + //_sfCurFrame = sf_seek(sf, newfr, 0); + sfCurFrame = f.seek(newfr, 0); + // Added by Tim. p3.3.17 - #ifdef WAVEEVENT_DEBUG - printf("WaveEventBase::readAudio offset:%u channel:%d n:%d\n", offset, channel, n); + #ifdef WAVEEVENT_DEBUG_PRC + printf("WaveEventBase::readAudio Seek frame:%ld converted to frame:%ld _sfCurFrame:%ld\n", frame, newfr, sfCurFrame); #endif - if (f.isNull()) - return; + // Reset the src converter. It's current state is meaningless now. + //int srcerr = src_reset(src_state); + int srcerr = audConv->reset(); + if(srcerr != 0) + printf("WaveEventBase::readAudio Converter reset failed: %s\n", src_strerror(srcerr)); + } + } + else + { + // No seek requested. Are the rates the same? + if(!resample) + // Sample rates are the same. Just a regular seek, no conversion. + sfCurFrame = f.seek(frame, 0); + else + { + // Added by Tim. p3.3.17 + #ifdef WAVEEVENT_DEBUG_PRC + printf("WaveEventBase::readAudio No 'transport' seek, rates different. Seeking to _sfCurFrame:%ld\n", sfCurFrame); + #endif - // Changed by Tim. p3.3.18 - // FIXME: Removed until resampling is enabled. - #ifdef USE_SAMPLERATE - if(doSeek) + // Sample rates are different. We can't just tell seek to go to an absolute calculated position, + // since the last position can vary - it might not be what the calculated position is. + // We must use the last position left by SRC conversion, ie. let the file position progress on its own. + sfCurFrame = f.seek(sfCurFrame, 0); + } + } + + // Do we not need to resample? + if(!resample) + { + return sfCurFrame + f.read(channel, buffer, n, overwrite); + } + + size_t rn; + + if((sampleRate == 0) || (fsrate == 0)) + { + if(debugMsg) + printf("WaveEventBase::readAudio Using SRC: Error: sampleRate or file samplerate is zero!\n"); + return sfCurFrame; + } + + // Ratio is defined as output sample rate over input samplerate. + double srcratio = (double)sampleRate / (double)fsrate; + long outFrames = n; + //long outSize = outFrames * channel; + long outSize = outFrames * fchan; + + //long inSize = long(outSize * srcratio) + 1 // From MusE-2 file converter. + //long inSize = (long)floor(((double)outSize / srcratio)); // From simplesynth. + //long inFrames = (long)floor(((double)outFrames / srcratio)); // From simplesynth. + long inFrames = (long)ceil(((double)outFrames / srcratio)); // From simplesynth. + //long inFrames = (long)floor(double(outFrames * sfinfo.samplerate) / double(sampleRate)); // From simplesynth. + + // Extra input compensation - sometimes src requires more input frames than expected in order to + // always get a reliable number of used out frames ! + //inFrames = inFrames / (srcratio / 2.0); + long inComp = 10; + inFrames += inComp; + + long inSize = inFrames * fchan; + //long inSize = inFrames * channel; + + float inbuffer[inSize]; + float outbuffer[outSize]; + + //float* poutbuf; + + // If the number of file channels is the same as the process channels AND we want overwrite, we can get away with direct copying. + //if(overwrite && channel == fchan) + // Point the out buffer directly at the return buffers. + // poutbuf = buffer; + //else + // Point the out buffer at our local buffers. + // poutbuf = &outbuffer[0]; + + // Converter channels are fixed at creation time! Can't change them on the fly. Can't use 'channel' paramter. + //rn = f.read(inbuffer, inFrames); + rn = f.readDirect(inbuffer, inFrames); + + // convert + SRC_DATA srcdata; + srcdata.data_in = inbuffer; + srcdata.data_out = outbuffer; + //srcdata.data_out = poutbuf; + //srcdata.input_frames = inSize; + srcdata.input_frames = rn; + srcdata.output_frames = outFrames; + srcdata.end_of_input = ((long)rn != inFrames); + srcdata.src_ratio = srcratio; + + #ifdef WAVEEVENT_DEBUG_PRC + printf("WaveEventBase::readAudio %s processing converter... inFrames:%ld inSize:%ld outFrames:%ld outSize:%ld rn:%d", + f.name().latin1(), inFrames, inSize, outFrames, outSize, rn); + #endif + + //int srcerr = src_process(src_state, &srcdata); + int srcerr = audConv->process(&srcdata); + if(srcerr != 0) + { + printf("\nWaveEventBase::readAudio SampleRate converter process failed: %s\n", src_strerror(srcerr)); + return sfCurFrame += rn; + } + + #ifdef WAVEEVENT_DEBUG_PRC + printf(" frames used in:%ld out:%ld\n", srcdata.input_frames_used, srcdata.output_frames_gen); + #endif + + // If the number of frames read by the soundfile equals the input frames, go back. + // Otherwise we have reached the end of the file, so going back is useless since + // there shouldn't be any further calls. (Definitely get buffer underruns if further calls!) + if((long)rn == inFrames) + { + // Go back by the amount of unused frames. + sf_count_t seekn = inFrames - srcdata.input_frames_used; + if(seekn != 0) + { + #ifdef WAVEEVENT_DEBUG_PRC + printf("WaveEventBase::readAudio Seek-back by:%d\n", seekn); #endif - f.seek(offset + _spos, 0); - - f.read(channel, buffer, n, overwrite); + sfCurFrame = f.seek(-seekn, SEEK_CUR); + } + else + sfCurFrame += rn; + } + else + sfCurFrame += rn; + + if(debugMsg) + { + if(srcdata.output_frames_gen != outFrames) + printf("WaveEventBase::readAudio %s output_frames_gen:%ld != outFrames:%ld outSize:%ld inFrames:%ld srcdata.input_frames_used:%ld inSize:%ld rn:%d\n", + f.name().latin1(), srcdata.output_frames_gen, outFrames, outSize, inFrames, srcdata.input_frames_used, inSize, rn); + } + + if(inFrames != (long)rn) + { + if(debugMsg) + printf("WaveEventBase::readAudio %s rn:%zd != inFrames:%ld output_frames_gen:%ld outFrames:%ld outSize:%ld srcdata.input_frames_used:%ld inSize:%ld\n", + f.name().latin1(), rn, inFrames, srcdata.output_frames_gen, outFrames, outSize, srcdata.input_frames_used, inSize); + + // We've reached the end of the file. Convert the number of frames read. + //rn = (double)rn * srcratio + 1; + rn = (long)floor((double)rn * srcratio); + if(rn > (size_t)outFrames) + rn = outFrames; + } + else + if(srcdata.output_frames_gen != outFrames) + { + // SRC didn't give us the number of frames we requested. + // This can occasionally be radically different from the requested frames, or zero, + // even when ample excess input frames are supplied. + // We're not done converting yet - we haven't reached the end of the file. + // We must do something with the buffer. So let's zero whatever SRC didn't fill. + // FIXME: Instead of zeroing, try processing more input data until the out buffer is full. + long b = srcdata.output_frames_gen * channel; + long e = outFrames * channel; + for(long i = b; i < e; ++i) + outbuffer[i] = 0.0f; + //poutbuf[i] = 0.0f; + rn = outFrames; + } + else + rn = outFrames; + + float* poutbuf = &outbuffer[0]; + if(fchan == channel) + { + if(overwrite) + for (size_t i = 0; i < rn; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) = *poutbuf++; + } + else + for(size_t i = 0; i < rn; ++i) + { + for(int ch = 0; ch < channel; ++ch) + *(buffer[ch] + i) += *poutbuf++; } + } + else if((fchan == 2) && (channel == 1)) + { + // stereo to mono + if(overwrite) + for(size_t i = 0; i < rn; ++i) + *(buffer[0] + i) = poutbuf[i + i] + poutbuf[i + i + 1]; + else + for(size_t i = 0; i < rn; ++i) + *(buffer[0] + i) += poutbuf[i + i] + poutbuf[i + i + 1]; + } + else if((fchan == 1) && (channel == 2)) + { + // mono to stereo + if(overwrite) + for(size_t i = 0; i < rn; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) = data; + *(buffer[1]+i) = data; + } + else + for(size_t i = 0; i < rn; ++i) + { + float data = *poutbuf++; + *(buffer[0]+i) += data; + *(buffer[1]+i) += data; + } + } + else + { + if(debugMsg) + printf("WaveEventBase::readAudio Channel mismatch: source chans:%d -> dst chans:%d\n", fchan, channel); + } + + return sfCurFrame; + */ + + + #else + if(f.isNull()) + //return; + return sfCurFrame; + + sfCurFrame = f.seek(offset + _spos, 0); + sfCurFrame += f.read(channel, buffer, n, overwrite); + return sfCurFrame; + #endif + +} + diff --git a/muse/muse/waveevent.h b/muse/muse/waveevent.h index c5ca5315..926e9c64 100644 --- a/muse/muse/waveevent.h +++ b/muse/muse/waveevent.h @@ -9,8 +9,13 @@ #ifndef __WAVE_EVENT_H__ #define __WAVE_EVENT_H__ +//#include <samplerate.h> +#include <sys/types.h> + #include "eventbase.h" +class AudioConverter; + //--------------------------------------------------------- // WaveEvent //--------------------------------------------------------- @@ -43,7 +48,12 @@ class WaveEventBase : public EventBase { // Changed by Tim. p3.3.17 //virtual void read(unsigned offset, float** bpp, int channels, int nn, bool overwrite = true); - virtual void readAudio(unsigned /*offset*/, float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/); + //virtual void readAudio(unsigned /*offset*/, float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/); + //virtual off_t readAudio(SRC_STATE* /*src_state*/, off_t /*sfCurFrame*/, unsigned /*offset*/, + // float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/); + virtual off_t readAudio(AudioConverter* /*audConv*/, off_t /*sfCurFrame*/, unsigned /*offset*/, + float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/); }; + #endif diff --git a/muse/muse/widgets/citem.cpp b/muse/muse/widgets/citem.cpp index a4cef8b7..2974196d 100644 --- a/muse/muse/widgets/citem.cpp +++ b/muse/muse/widgets/citem.cpp @@ -25,7 +25,9 @@ CItem::CItem(const QPoint&p, const QRect& r) _isMoving = false; } -CItem::CItem(Event e, Part* p) +// Changed by Tim. p3.3.20 +//CItem::CItem(Event e, Part* p) +CItem::CItem(const Event& e, Part* p) { _event = e; _part = p; diff --git a/muse/muse/widgets/citem.h b/muse/muse/widgets/citem.h index 5d035110..c58b6bb1 100644 --- a/muse/muse/widgets/citem.h +++ b/muse/muse/widgets/citem.h @@ -36,7 +36,9 @@ class CItem { public: CItem(const QPoint& p, const QRect& r); CItem(); - CItem(Event e, Part* p); + // Changed by Tim. p3.3.20 + //CItem(Event e, Part* p); + CItem(const Event& e, Part* p); bool isMoving() const { return _isMoving; } void setMoving(bool f) { _isMoving = f; } diff --git a/muse/muse/widgets/midisync.ui b/muse/muse/widgets/midisync.ui index 3adc8e1d..2032e574 100644 --- a/muse/muse/widgets/midisync.ui +++ b/muse/muse/widgets/midisync.ui @@ -290,46 +290,67 @@ configuration dialog</comment> </vbox> </widget> <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>syncGen</cstring> + </property> + <property name="title"> + <string>Sync receiving and sending</string> + </property> + <grid> <property name="name"> - <cstring>syncGen</cstring> + <cstring>unnamed</cstring> </property> - <property name="title"> - <string>Sync receiving and sending</string> - </property> - <grid> - <property name="name"> - <cstring>unnamed</cstring> - </property> - <widget class="QListView" row="1" column="0"> - <property name="name"> - <cstring>devicesListView</cstring> - </property> - </widget> - <widget class="QCheckBox" row="0" column="0"> - <property name="name"> - <cstring>extSyncCheckbox</cstring> - </property> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="text"> - <string>Slave to external sync</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - <property name="toolTip" stdset="0"> - <string>Allow Muse to be controlled by external midi</string> - </property> - <property name="whatsThis" stdset="0"> - <string>When in slave mode, tempo is - controlled externally, as well - as the transport (play, stop etc.) + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>useJackTransportCheckbox</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Use Jack transport</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Send and receive Jack transport</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Send and receive Jack transport information, + including stop, start and position.</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>extSyncCheckbox</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Slave to external sync</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Control MusE timing by external midi clock or MTC sync</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When in slave mode, tempo is + controlled externally. +MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC).</string> - </property> - </widget> - </grid> + </property> + </widget> + <widget class="QListView" row="2" column="0"> + <property name="name"> + <cstring>devicesListView</cstring> + </property> + </widget> + </grid> </widget> </grid> </widget> diff --git a/muse/muse/widgets/midisyncimpl.cpp b/muse/muse/widgets/midisyncimpl.cpp index 6fae59b7..d38068fc 100644 --- a/muse/muse/widgets/midisyncimpl.cpp +++ b/muse/muse/widgets/midisyncimpl.cpp @@ -22,6 +22,7 @@ //#include <qwhatsthis.h> #include <qmessagebox.h> +#include "app.h" #include "song.h" #include "midiport.h" #include "midiseq.h" @@ -292,6 +293,8 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent, const char* name) //connect(syncMode, SIGNAL(clicked(int)), SLOT(syncChanged(int))); connect(extSyncCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); + connect(mtcSyncType, SIGNAL(activated(int)), SLOT(syncChanged())); + connect(useJackTransportCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); connect(&extSyncFlag, SIGNAL(valueChanged(bool)), SLOT(extSyncChanged(bool))); @@ -328,7 +331,12 @@ void MidiSyncConfig::songChanged(int flags) //for(int i = 0; i < MIDI_PORTS; ++i) // tmpMidiSyncPorts[i] = midiSyncPorts[i]; + extSyncCheckbox->blockSignals(true); + useJackTransportCheckbox->blockSignals(true); extSyncCheckbox->setChecked(extSyncFlag.value()); + useJackTransportCheckbox->setChecked(useJackTransport); + useJackTransportCheckbox->blockSignals(false); + extSyncCheckbox->blockSignals(false); mtcSyncType->setCurrentItem(mtcType); @@ -455,6 +463,8 @@ void MidiSyncConfig::extSyncChanged(bool v) { extSyncCheckbox->blockSignals(true); extSyncCheckbox->setChecked(v); +// if(v) +// song->setMasterFlag(false); extSyncCheckbox->blockSignals(false); } @@ -541,7 +551,12 @@ void MidiSyncConfig::apply() mtcType = mtcSyncType->currentItem(); //extSyncFlag.setValue(syncMode->id(syncMode->selected())); + //extSyncFlag.blockSignals(true); extSyncFlag.setValue(extSyncCheckbox->isChecked()); +// if(extSyncFlag.value()) +// song->setMasterFlag(false); + //extSyncFlag.blockSignals(false); + useJackTransport = useJackTransportCheckbox->isChecked(); mtcOffset.setH(mtcOffH->value()); mtcOffset.setM(mtcOffM->value()); @@ -568,6 +583,8 @@ void MidiSyncConfig::apply() lvi = (MidiSyncLViewItem*)lvi->nextSibling(); } + //muse->changeConfig(true); // save settings + _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); |