コード例 #1
0
ファイル: ofApp.cpp プロジェクト: danzeeeman/ofxXwax
void ofApp::audioOut(float* output, int bufferSize, int nChannels) {
	float sum = 0;
	for (int i = 0; i < bufferSize; i++){
		output[i * nChannels] = floatBuffer[bufferPosition * nChannels];
		output[i * nChannels + 1] = floatBuffer[bufferPosition * nChannels + 1];
		
		curBuffer[i * nChannels] = output[i * nChannels] * (1<<15);
		curBuffer[i * nChannels + 1] = output[i * nChannels + 1] * (1<<15);
	
		if(i % 2 == 0) { // drop 96khz to 48khz
			bufferPosition++;
			if(bufferPosition == bufferFrames) {
				bufferPosition = 0;
				relativePosition = 0;
				exportXml();
			}
		}
	}
	
	timecoder_submit(&timecoder, &curBuffer[0], bufferSize);
	relativeTtm[ttmPosition] = getRelative();
	absoluteTtm[ttmPosition] = getAbsolute();
	if(exporting) {
		wholeBuffer.push_back(absoluteTtm[ttmPosition] );
	}
	pitchTtm[ttmPosition] = getPitch();
	ttmPosition++;
	if(ttmPosition == relativeTtm.size()) {
		ttmPosition = 0;
	}
}
コード例 #2
0
ファイル: alsa.c プロジェクト: achterin/xwax-
static int capture(struct device_t *dv)
{
    int r;
    struct alsa_t *alsa = (struct alsa_t*)dv->local;

    r = snd_pcm_readi(alsa->capture.pcm, alsa->capture.buf,
                      alsa->capture.period);
    if (r < 0)
        return r;
    
    if (r < alsa->capture.period) {
        fprintf(stderr, "alsa: capture underrun %d/%ld.\n",
                r, alsa->capture.period);
    }
    
    if (dv->timecoder)
        timecoder_submit(dv->timecoder, alsa->capture.buf, r);

    return 0;
}
コード例 #3
0
ファイル: jack.c プロジェクト: doublerebel/s-xwax
static void process_deck(struct device_t *dv, jack_nframes_t nframes)
{
    int n;
    jack_default_audio_sample_t *in[DEVICE_CHANNELS], *out[DEVICE_CHANNELS];
    jack_nframes_t remain;
    struct jack_t *jack = (struct jack_t*)dv->local;

    for (n = 0; n < DEVICE_CHANNELS; n++) {
        in[n] = jack_port_get_buffer(jack->input_port[n], nframes);
        assert(in[n] != NULL);
        out[n] = jack_port_get_buffer(jack->output_port[n], nframes);
        assert(out[n] != NULL);
    }

    /* For large values of nframes, communicate with the timecoder and
     * player in smaller blocks */

    remain = nframes;
    while (remain > 0) {
        signed short buf[MAX_BLOCK * DEVICE_CHANNELS];
        jack_nframes_t block;

        if (remain < MAX_BLOCK)
            block = remain;
        else
            block = MAX_BLOCK;

        /* Timecode input */

        interleave(buf, in, block);
        if (dv->timecoder)
            timecoder_submit(dv->timecoder, buf, block);

        /* Audio output -- handle in the same loop for finer granularity */

        player_collect(dv->player, buf, block, rate);
        uninterleave(out, buf, block);

	remain -= block;
    }
}
コード例 #4
0
ファイル: ofxXwax.cpp プロジェクト: scratchml/ofxXwax
void ofxXwax::update(float* input) {
    // convert from -1 to 1 to a 16-byte signed short integer
    for (int i = 0; i < bufferSize * nChannels; i++) {
        shortBuffer[i] = input[i] * (1<<15);
    }

    timecoder_submit(&timecoder, &shortBuffer[0], bufferSize);

    float when;
    float curPosition = timecoder_get_position(&timecoder, &when);

    pitch = timecoder_get_pitch(&timecoder);
    velocity = (msPerSecond * bufferSize / sampleRate) * pitch;
    relativePosition += velocity;

    if(curPosition == invalidPosition) {
        absoluteValid = false;
        absolutePosition += velocity;
    } else {
        absoluteValid = true;
        absolutePosition = curPosition;
    }
}
コード例 #5
0
void VinylControlXwax::analyzeSamples(CSAMPLE* pSamples, size_t nFrames) {
    ScopedTimer t("VinylControlXwax::analyzeSamples");
    CSAMPLE gain = m_pVinylControlInputGain->get();
    const int kChannels = 2;

    // We only support amplifying with the VC pre-amp.
    if (gain < 1.0f) {
        gain = 1.0f;
    }

    size_t samplesSize = nFrames * kChannels;

    if (samplesSize > m_workBufferSize) {
        delete [] m_pWorkBuffer;
        m_pWorkBuffer = new short[samplesSize];
        m_workBufferSize = samplesSize;
    }

    // Convert CSAMPLE samples to shorts, preventing overflow.
    for (int i = 0; i < static_cast<int>(samplesSize); ++i) {
        CSAMPLE sample = pSamples[i] * gain * SAMPLE_MAX;

        if (sample > SAMPLE_MAX) {
            m_pWorkBuffer[i] = SAMPLE_MAX;
        } else if (sample < SAMPLE_MIN) {
            m_pWorkBuffer[i] = SAMPLE_MIN;
        } else {
            m_pWorkBuffer[i] = static_cast<short>(sample);
        }
    }

    // Submit the samples to the xwax timecode processor. The size argument is
    // in stereo frames.
    timecoder_submit(&timecoder, m_pWorkBuffer, nFrames);

    bool bHaveSignal = fabs(pSamples[0]) + fabs(pSamples[1]) > kMinSignal;
    //qDebug() << "signal?" << bHaveSignal;

    //TODO: Move all these config object get*() calls to an "updatePrefs()" function,
    //        and make that get called when any options get changed in the preferences dialog, rather than
    //        polling everytime we get a buffer.


    // Check if vinyl control is enabled...
    m_bIsEnabled = enabled == NULL ? false : checkEnabled(m_bIsEnabled, enabled->get());

    if(bHaveSignal) {
        // Always analyze the input samples
        m_iPosition = timecoder_get_position(&timecoder, NULL);
        //Notify the UI if the timecode quality is good
        establishQuality(m_iPosition != -1);
    }

    //are we even playing and enabled at all?
    if (!m_bIsEnabled)
        return;

    double dVinylPitch = timecoder_get_pitch(&timecoder);

    // Has a new track been loaded? Currently we use track duration which is
    // integer seconds in the song. However, for calculations we need the
    // higher-accuracy duration found by dividing the track samples by the
    // samplerate.
    // TODO(XXX): we should really sync on all track changes
    // TODO(rryan): Should we calculate the true duration to check if it
    // changed?  It's just an extra division by trackSampleRate.
    double duration_inaccurate = duration->get();
    if (duration_inaccurate != m_dOldDurationInaccurate) {
        m_bForceResync = true;
        m_bTrackSelectMode = false; //just in case
        m_dOldDurationInaccurate = duration_inaccurate;
        m_dOldDuration = trackSamples->get() / 2 / trackSampleRate->get();

        // we were at record end, so turn it off and restore mode
        if(m_bAtRecordEnd) {
            disableRecordEndMode();
            if (m_iOldVCMode == MIXXX_VCMODE_CONSTANT)
                m_iVCMode = MIXXX_VCMODE_RELATIVE;
            else
                m_iVCMode = m_iOldVCMode;
        }
    }

    // make sure m_dVinylPosition only has good values
    if (m_iPosition != -1) {
        m_dVinylPosition = static_cast<double>(m_iPosition) / 1000.0 - m_iLeadInTime;
    }

    // Initialize drift control to zero in case we don't get any position data
    // to calculate it with.
    double dDriftControl = 0.0;

    // Get the playback position in the file in seconds.
    double filePosition = playPos->get() * m_dOldDuration;

    int reportedMode = mode->get();
    bool reportedPlayButton = playButton->get();

    if (m_iVCMode != reportedMode) {
        //if we are playing, don't allow change
        //to absolute mode (would cause sudden track skip)
        if (reportedPlayButton && reportedMode == MIXXX_VCMODE_ABSOLUTE) {
            m_iVCMode = MIXXX_VCMODE_RELATIVE;
            mode->slotSet((double)m_iVCMode);
        } else {
            // go ahead and switch
            m_iVCMode = reportedMode;
            if (reportedMode == MIXXX_VCMODE_ABSOLUTE) {
                m_bForceResync = true;
            }
        }

        //if we are out of error mode...
        if (vinylStatus->get() == VINYL_STATUS_ERROR &&
                m_iVCMode == MIXXX_VCMODE_RELATIVE) {
            vinylStatus->slotSet(VINYL_STATUS_OK);
        }
    }

    //if looping has been enabled, don't allow absolute mode
    if (loopEnabled->get() && m_iVCMode == MIXXX_VCMODE_ABSOLUTE) {
        m_iVCMode = MIXXX_VCMODE_RELATIVE;
        mode->slotSet((double)m_iVCMode);
    }

    // Don't allow cueing mode to be enabled in absolute mode.
    if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE &&
            cueing->get() != MIXXX_RELATIVE_CUE_OFF) {
        cueing->set(MIXXX_RELATIVE_CUE_OFF);
    }

    //are we newly playing near the end of the record?  (in absolute mode, this happens
    //when the filepos is past safe (more accurate),
    //but it can also happen in relative mode if the vinylpos is nearing the end
    //If so, change to constant mode so DJ can move the needle safely

    if (!m_bAtRecordEnd && reportedPlayButton) {
        if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE &&
                (filePosition + m_iLeadInTime) * 1000.0 > m_uiSafeZone &&
                !m_bForceResync) {
            // corner case: we are waiting for resync so don't enable just yet
            enableRecordEndMode();
        } else if (m_iVCMode != MIXXX_VCMODE_ABSOLUTE &&
                       m_iPosition != -1 &&
                       m_iPosition > static_cast<int>(m_uiSafeZone)) {
            enableRecordEndMode();
        }
    }

    if (m_bAtRecordEnd) {
        //if m_bAtRecordEnd was true, maybe it no longer applies:

        if (!reportedPlayButton) {
            //if we turned off play button, also disable
            disableRecordEndMode();
        } else if (m_iPosition != -1 &&
                   m_iPosition <= static_cast<int>(m_uiSafeZone) &&
                   m_dVinylPosition > 0 &&
                   checkSteadyPitch(dVinylPitch, filePosition) > 0.5) {
            //if good position, and safe, and not in leadin, and steady,
            //disable
            disableRecordEndMode();
        }

        if (m_bAtRecordEnd) {
            //ok, it's still valid, blink
            if ((reportedPlayButton && (int)(filePosition * 2.0) % 2) ||
                (!reportedPlayButton && (int)(m_iPosition / 500.0) % 2))
                vinylStatus->slotSet(VINYL_STATUS_WARNING);
            else
                vinylStatus->slotSet(VINYL_STATUS_DISABLED);
        }
    }

    //check here for position > safe, and if no record end mode,
    //then trigger track selection mode.  just pass position to it
    //and ignore pitch

    if (!m_bAtRecordEnd) {
        if (m_iPosition != -1 && m_iPosition > static_cast<int>(m_uiSafeZone)) {
            //only enable if pitch is steady, though.  Heavy scratching can
            //produce crazy results and trigger this mode
            if (m_bTrackSelectMode || checkSteadyPitch(dVinylPitch, filePosition) > 0.1) {
                //until I can figure out how to detect "track 2" on serato CD,
                //don't try track selection
                if (!m_bCDControl) {
                    if (!m_bTrackSelectMode) {
                        qDebug() << "position greater than safe, select mode" << m_iPosition << m_uiSafeZone;
                        m_bTrackSelectMode = true;
                        togglePlayButton(false);
                        resetSteadyPitch(0.0, 0.0);
                        controlScratch->slotSet(0.0);
                    }
                    doTrackSelection(true, dVinylPitch, m_iPosition);
                }

                //hm I wonder if track will keep playing while this happens?
                //not sure what we want to do here...  probably enforce
                //stopped deck.

                //but if constant mode...  nah, force stop.
                return;
            }
            //if it's not steady yet we process as normal
        } else {
            //so we're not unsafe.... but
            //if no position, but we were in select mode, do select mode
            if (m_iPosition == -1 && m_bTrackSelectMode) {
                //qDebug() << "no position, but were in select mode";
                doTrackSelection(false, dVinylPitch, m_iPosition);

                //again, force stop?
                return;
            } else if (m_bTrackSelectMode) {
                //qDebug() << "discontinuing select mode, selecting track";
                if (m_pControlTrackLoader == NULL)
                    m_pControlTrackLoader = new ControlObjectThread(m_group,"LoadSelectedTrack");

                if (!m_pControlTrackLoader) {
                    qDebug() << "ERROR: couldn't get track loading object?";
                } else {
                    m_pControlTrackLoader->slotSet(1.0);
                    m_pControlTrackLoader->slotSet(0.0); //I think I have to do this...
                }
                //if position is known and safe then no track select mode
                m_bTrackSelectMode = false;
            }
        }
    }

    if (m_iVCMode == MIXXX_VCMODE_CONSTANT) {
        //when we enabled constant mode we set the rate slider
        //now we just either set scratch val to 0 (stops playback)
        //or 1 (plays back at that rate)

        double newScratch = reportedPlayButton ? rateDir->get() *
                (rateSlider->get() * rateRange->get()) + 1.0 : 0.0;
        controlScratch->slotSet(newScratch);

        //is there any reason we'd need to do anything else?
        return;
    }

    //CONSTANT MODE NO LONGER APPLIES...

    // When there's a timecode signal available
    // This is set when we analyze samples (no need for lock I think)
    if(bHaveSignal) {
        //POSITION: MAYBE  PITCH: YES

        //We have pitch, but not position.  so okay signal but not great (scratching / cueing?)
        //qDebug() << "Pitch" << dVinylPitch;

        if (m_iPosition != -1) {
            //POSITION: YES  PITCH: YES
            //add a value to the pitch ring (for averaging / smoothing the pitch)
            //qDebug() << fabs(((m_dVinylPosition - m_dVinylPositionOld) * (dVinylPitch / fabs(dVinylPitch))));

            //save the absolute amount of drift for when we need to estimate vinyl position
            m_dDriftAmt = m_dVinylPosition - filePosition;

            //qDebug() << "drift" << m_dDriftAmt;

            if (m_bForceResync) {
                //if forceresync was set but we're no longer absolute,
                //it no longer applies
                //if we're in relative mode then we'll do a sync
                //because it might select a cue
                if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE ||
                        (m_iVCMode == MIXXX_VCMODE_RELATIVE && cueing->get())) {
                    syncPosition();
                    resetSteadyPitch(dVinylPitch, m_dVinylPosition);
                }
                m_bForceResync = false;
            } else if (fabs(m_dVinylPosition - filePosition) > 0.1 &&
                       m_dVinylPosition < -2.0) {
                //At first I thought it was a bug to resync to leadin in relative mode,
                //but after using it that way it's actually pretty convenient.
                //qDebug() << "Vinyl leadin";
                syncPosition();
                resetSteadyPitch(dVinylPitch, m_dVinylPosition);
                if (uiUpdateTime(filePosition))
                    rateSlider->slotSet(rateDir->get() * (fabs(dVinylPitch) - 1.0) / rateRange->get());
            } else if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE &&
                       (fabs(m_dVinylPosition - m_dVinylPositionOld) >= 5.0)) {
                //If the position from the timecode is more than a few seconds off, resync the position.
                //qDebug() << "resync position (>15.0 sec)";
                //qDebug() << m_dVinylPosition << m_dVinylPositionOld << m_dVinylPosition - m_dVinylPositionOld;
                syncPosition();
                resetSteadyPitch(dVinylPitch, m_dVinylPosition);
            } else if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE && m_bCDControl &&
                       fabs(m_dVinylPosition - m_dVinylPositionOld) >= 0.1) {
                //qDebug() << "CDJ resync position (>0.1 sec)";
                syncPosition();
                resetSteadyPitch(dVinylPitch, m_dVinylPosition);
            } else if (playPos->get() >= 1.0 && dVinylPitch > 0) {
                //end of track, force stop
                togglePlayButton(false);
                resetSteadyPitch(0.0, 0.0);
                controlScratch->slotSet(0.0);
                m_iPitchRingPos = 0;
                m_iPitchRingFilled = 0;
                return;
            } else {
                togglePlayButton(checkSteadyPitch(dVinylPitch, filePosition) > 0.5);
            }

            // Calculate how much the vinyl's position has drifted from it's timecode and compensate for it.
            // (This is caused by the manufacturing process of the vinyl.)
            if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE &&
                    fabs(m_dDriftAmt) > 0.1 && fabs(m_dDriftAmt) < 5.0) {
                dDriftControl = m_dDriftAmt * .01;
            } else {
                dDriftControl = 0.0;
            }

            m_dVinylPositionOld = m_dVinylPosition;
        } else {
            //POSITION: NO  PITCH: YES
            //if we don't have valid position, we're not playing so reset time to current
            //estimate vinyl position

            if (playPos->get() >= 1.0 && dVinylPitch > 0) {
                //end of track, force stop
                togglePlayButton(false);
                resetSteadyPitch(0.0, 0.0);
                controlScratch->slotSet(0.0);
                m_iPitchRingPos = 0;
                m_iPitchRingFilled = 0;
                return;
            }

            if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE &&
                fabs(dVinylPitch) < 0.05 &&
                fabs(m_dDriftAmt) >= 0.3) {
                //qDebug() << "slow, out of sync, syncing position";
                syncPosition();
            }

            m_dVinylPositionOld = filePosition + m_dDriftAmt;

            if (dVinylPitch > 0.2) {
                togglePlayButton(checkSteadyPitch(dVinylPitch, filePosition) > 0.5);
            }
        }

        //playbutton status may have changed
        reportedPlayButton = playButton->get();

        if (reportedPlayButton) {
            // Only add to the ring if pitch is stable
            m_pPitchRing[m_iPitchRingPos] = dVinylPitch;
            if (m_iPitchRingFilled < m_iPitchRingSize) {
                m_iPitchRingFilled++;
            }
            m_iPitchRingPos = (m_iPitchRingPos + 1) % m_iPitchRingSize;
        } else {
            // Reset ring if pitch isn't steady
            m_iPitchRingPos = 0;
            m_iPitchRingFilled = 0;
        }

        //only smooth when we have good position (no smoothing for scratching)
        double averagePitch = 0.0;
        if (m_iPosition != -1 && reportedPlayButton) {
            for (int i = 0; i < m_iPitchRingFilled; ++i) {
                averagePitch += m_pPitchRing[i];
            }
            averagePitch /= m_iPitchRingFilled;
            // Round out some of the noise
            averagePitch = round(averagePitch * 10000.0);
            averagePitch /= 10000.0;
        } else {
            averagePitch = dVinylPitch;
        }

        controlScratch->slotSet(averagePitch + dDriftControl);
        if (m_iPosition != -1 && reportedPlayButton && uiUpdateTime(filePosition)) {
            double true_pitch = averagePitch + dDriftControl;
            double pitch_difference = true_pitch - m_dDisplayPitch;

            // The true pitch can show a misleading amount of variance --
            // differences of .1% or less can show up as 1 or 2 bpm changes.
            // Therefore we react slowly to bpm changes to show a more steady
            // number to the user.
            if (fabs(pitch_difference) > 0.5) {
                // For large changes in pitch (start/stop, usually), immediately
                // update the display.
                m_dDisplayPitch = true_pitch;
            } else if (fabs(pitch_difference) > 0.005) {
                // For medium changes in pitch, take 4 callback loops to
                // converge on the correct amount.
                m_dDisplayPitch += pitch_difference * .25;
            } else {
                // For extremely small changes, converge very slowly.
                m_dDisplayPitch += pitch_difference * .01;
            }
            rateSlider->slotSet(rateDir->get() * (m_dDisplayPitch - 1.0) / rateRange->get());
            m_dUiUpdateTime = filePosition;
        }

        m_dOldFilePos = filePosition;
    } else {
        // No pitch data available (the needle is up/stopped.... or *really*
        // crappy signal)

        //POSITION: NO  PITCH: NO
        //if it's been a long time, we're stopped.
        //if it hasn't been long,
        //let the track play a wee bit more before deciding we've stopped

        rateSlider->slotSet(0.0);

        if (m_iVCMode == MIXXX_VCMODE_ABSOLUTE &&
                fabs(m_dVinylPosition - filePosition) >= 0.1) {
            //qDebug() << "stopped, out of sync, syncing position";
            syncPosition();
        }

        if(fabs(filePosition - m_dOldFilePos) >= 0.1 ||
               filePosition == m_dOldFilePos) {
            //We are not playing any more
            togglePlayButton(false);
            resetSteadyPitch(0.0, 0.0);
            controlScratch->slotSet(0.0);
            //resetSteadyPitch(dVinylPitch, filePosition);
            // Notify the UI that the timecode quality is garbage/missing.
            m_fTimecodeQuality = 0.0f;
            m_iPitchRingPos = 0;
            m_iPitchRingFilled = 0;
            m_iQualPos = 0;
            m_iQualFilled = 0;
            m_bForceResync = true;
            vinylStatus->slotSet(VINYL_STATUS_OK);
        }
    }
}
コード例 #6
0
ファイル: device.c プロジェクト: ewanuno/xwax
void device_submit(struct device *dv, signed short *pcm, size_t n)
{
    assert(dv->timecoder != NULL);
    timecoder_submit(dv->timecoder, pcm, n);
}