Merge lp:~ywwg/mixxx/features_xwax2 into lp:~mixxxdevelopers/mixxx/trunk
- features_xwax2
- Merge into trunk
Status: | Merged | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 2753 | ||||||||||||||||||||
Proposed branch: | lp:~ywwg/mixxx/features_xwax2 | ||||||||||||||||||||
Merge into: | lp:~mixxxdevelopers/mixxx/trunk | ||||||||||||||||||||
Diff against target: |
7961 lines (+3993/-1888) 59 files modified
mixxx/build/depends.py (+2/-0) mixxx/build/features.py (+4/-1) mixxx/build/qtcreator/mixxx.pro (+1/-0) mixxx/lib/xwax/lut.c (+110/-0) mixxx/lib/xwax/lut.cpp (+110/-0) mixxx/lib/xwax/lut.h (+42/-0) mixxx/lib/xwax/pitch.h (+71/-0) mixxx/lib/xwax/timecoder.c (+381/-449) mixxx/lib/xwax/timecoder.h (+80/-42) mixxx/lib/xwax/timecoder_win32.cpp (+346/-411) mixxx/res/skins/Outline1024x600-Netbook/skin.xml (+77/-0) mixxx/src/cachingreader.cpp (+25/-3) mixxx/src/controlobject.cpp (+5/-2) mixxx/src/controlpushbutton.cpp (+25/-44) mixxx/src/controlpushbutton.h (+2/-0) mixxx/src/controlvaluedelegate.cpp (+3/-0) mixxx/src/dlgpreferences.cpp (+19/-1) mixxx/src/dlgpreferences.h (+12/-10) mixxx/src/dlgprefnovinyl.cpp (+32/-0) mixxx/src/dlgprefnovinyl.h (+39/-0) mixxx/src/dlgprefnovinyldlg.ui (+475/-0) mixxx/src/dlgprefsound.cpp (+5/-2) mixxx/src/dlgprefvinyl.cpp (+77/-120) mixxx/src/dlgprefvinyl.h (+3/-9) mixxx/src/dlgprefvinyldlg.ui (+130/-28) mixxx/src/engine/bpmcontrol.cpp (+1/-1) mixxx/src/engine/enginebuffer.cpp (+177/-46) mixxx/src/engine/enginebuffer.h (+24/-5) mixxx/src/engine/enginebufferscalelinear.cpp (+224/-125) mixxx/src/engine/enginebufferscalelinear.h (+13/-3) mixxx/src/engine/enginedeck.cpp (+2/-1) mixxx/src/engine/ratecontrol.cpp (+20/-1) mixxx/src/engine/ratecontrol.h (+14/-8) mixxx/src/engine/readaheadmanager.cpp (+12/-10) mixxx/src/mathstuff.cpp (+10/-0) mixxx/src/mathstuff.h (+9/-0) mixxx/src/midi/midiscriptengine.cpp (+3/-3) mixxx/src/mixxx.cpp (+143/-38) mixxx/src/mixxx.h (+7/-1) mixxx/src/sounddeviceportaudio.cpp (+2/-2) mixxx/src/soundmanager.cpp (+56/-27) mixxx/src/soundmanager.h (+6/-1) mixxx/src/soundmanagerutil.cpp (+15/-0) mixxx/src/soundmanagerutil.h (+1/-0) mixxx/src/vinylcontrol.cpp (+41/-24) mixxx/src/vinylcontrol.h (+68/-46) mixxx/src/vinylcontrolproxy.cpp (+28/-2) mixxx/src/vinylcontrolproxy.h (+14/-11) mixxx/src/vinylcontrolsignalwidget.cpp (+115/-150) mixxx/src/vinylcontrolsignalwidget.h (+21/-16) mixxx/src/vinylcontrolxwax.cpp (+701/-159) mixxx/src/vinylcontrolxwax.h (+72/-31) mixxx/src/waveform/waveformrendersignal.cpp (+1/-3) mixxx/src/widget/wnumberpos.cpp (+10/-4) mixxx/src/widget/wnumberrate.cpp (+0/-1) mixxx/src/widget/wnumberrate.h (+1/-0) mixxx/src/widget/woverview.cpp (+4/-3) mixxx/src/widget/wstatuslight.cpp (+95/-37) mixxx/src/widget/wstatuslight.h (+7/-7) |
||||||||||||||||||||
To merge this branch: | bzr merge lp:~ywwg/mixxx/features_xwax2 | ||||||||||||||||||||
Related bugs: |
|
||||||||||||||||||||
Related blueprints: |
Vinyl Control Improvements
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Good | Approve | ||
RJ Skerry-Ryan | Approve | ||
Albert Santoni | Needs Fixing | ||
Review via email: mp+37798@code.launchpad.net |
Commit message
Description of the change
This branch revamps vinyl control. It:
* updates xwax to the latest version
* makes changes to the engine code to support prerolling of tracks (positions < 0)
* tweaks some of the UI widgets (multistate buttons, multistate lights)
* rewrites the vinyl control code
* rewrites the linear scalar to work better
RJ Skerry-Ryan (rryan) wrote : | # |
RAFFI TEA (raffitea) wrote : | # |
Does it also compile on Windows?
Owen Williams (ywwg) wrote : | # |
> Does it also compile on Windows?
I think the new version of xwax needs to be checked. In the existing trunk, there's a _win32 file that was created, but I don't know how. It might need to be recreated for the new version. You could always try compiling it and see what happens :)
Albert Santoni (gamegod) wrote : | # |
There's a bunch of C99 stuff in the original timecoder.c that I fixed
to compile with MSVC on Windows (no C99). Mainly, I remember it was
declaration of the timecoder struct. It should be mergeable by eye,
and I'll do it when I get around to building the branch on Windows if
someone else doesn't beat me to it.
On Wed, Oct 13, 2010 at 5:33 AM, Owen Williams <email address hidden> wrote:
>> Does it also compile on Windows?
> I think the new version of xwax needs to be checked. In the existing trunk, there's a _win32 file that was created, but I don't know how. It might need to be recreated for the new version. You could always try compiling it and see what happens :)
> --
> https:/
> Your team Mixxx Development Team is requested to review the proposed merge of lp:~ywwg/mixxx/features_xwax2 into lp:mixxx.
>
Phillip Whelan (pwhelan) wrote : | # |
There are also some other issues that I know your code has or had:
* Enabling Vinyl Control on one deck sometimes locks up the other
* Old Skins or skins that are not updated to account for your widget
changes crash mixxx.
I'll check out your latest version of the branch to see if these are fixed.
- 2474. By Owen Williams <owen@ywwg>
-
* merge from trunk and fix conflicts
- 2475. By Owen Williams <owen@ywwg>
-
* we don't need to worry about device problems here (I think?)
- 2476. By Owen Williams <owen@ywwg>
-
* merge from trunk -- ugh, tough merge
- 2477. By Owen Williams <owen@ywwg>
-
* fix startup crash. Don't try to initialize cotnrolobject values right after creating the threads
- 2478. By Owen Williams <owen@ywwg>
-
* fix pushbuttons to work with multistate pushbutton widgets
* fix pushbuttons so that toggles work with midi controllers that do toggling (nanokontrol)
* fix vinyl code so that it's not specific to my nanokontrol setup
* simplify mode-switching code: don't allow switch to ABS mode if playing and needleskip prevention is on - 2479. By Owen Williams <owen@ywwg>
-
* merge with trunk, fix conflicts
- 2480. By Owen Williams <owen@ywwg>
-
* merge from lp:mixxx
- 2481. By Owen Williams <owen@ywwg>
-
* fix vinyl control checkboxes not working
- 2482. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2483. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
* adapted to new pref dialog design
* adapted vinyl code to work with new n-deck design. - 2484. By Owen Williams <email address hidden>
-
* remove my personal library unscanner hack
- 2485. By Owen Williams <email address hidden>
-
* merge with crazy fast-moving lp:mixxx
Albert Santoni (gamegod) wrote : | # |
Notes from an email conversation between Owen and I:
- Keyboard controls are a bit busted due to modifications made to controlpushbutt
- Must change the vinyl control code to be able to deal with a single deck set on any of the inputs, or figure out just how this should work in general. The solution must be generalizable to N-decks of vinyl control input, so we probably don't want to hardcode "Deck 1" as a magic deck.
- Should also figure out oddball cases like what happens if you have two decks of input and 3 players in Mixxx. How does the routing work?
- 2486. By Owen Williams <email address hidden>
-
* redo vinyl input mapping to support arbitrary numbers of decks
* to enable "single deck mode" set both decks to the same set of inputs - 2487. By Owen Williams <email address hidden>
-
* fix constant mode playback speed
- 2488. By Owen Williams <email address hidden>
-
* fix incorrect comments
* fix enable/disable of vinyl mode to be smooth and glitch-free - 2489. By Owen Williams <email address hidden>
-
* fix wstatuslights
- 2490. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
Albert Santoni (gamegod) wrote : | # |
- The guy who made the Trancer skin originally had another skin which
was completely ripped off from Traktor. In addition, the Trancer skin
has been removed from KDE-Look, so I'd prefer to err on the side of
caution and not include the Trancer skin. It's probably ripped off
from somewhere, I just don't know where yet.
- I had previously patched timecoder.c to make it store the lookup
table in the timecoder struct. This is absolutely necessary otherwise
you end up with a race condition because the code isn't thread safe and you will get crashes and all sorts of weird problems.
(eg. compare the new timecoder_
- Can the "enable vinyl control" menu items be moved into a submenu off the Options menu like :
Options
Vinyl Control -> Enable Deck 1
- Launchpad is wrong, the diff is incomplete (why??) so this is not a full review yet.
- VinylModeSwitch.js - What does this do?
- I can't get taglib built on Windows with MSVC so I haven't had a chance to try to get the new xwax code building on Windows.
- 2491. By Owen Williams <email address hidden>
-
* remove unneeded javascript file
Owen Williams (ywwg) wrote : | # |
> - Launchpad is wrong, the diff is incomplete (why??) so this is not a full review yet.
what do you mean the diff is incomplete?
>
> - VinylModeSwitch.js - What does this do?
that was a workaround I was using for a while. It's deprecated so I'll
remove it.
> - I had previously patched timecoder.c to make it store the lookup
>
> table in the timecoder struct. This is absolutely necessary otherwise
>
> you end up with a race condition because the code isn't thread safe and you will get crashes and all sorts of weird problems.
>
> (eg. compare the new timecoder_
>
I don't think this needs to be done again. Looking in the newer xwax, it already is storing the def in the timecoder, and the def stores the lookup table in itself. It looks to me like it's doing what you describe. (That and it doesn't crash or give me weird problems)
- 2492. By Owen Williams <email address hidden>
-
* move vinyl control options to submenu
- 2493. By Owen Williams <email address hidden>
-
* when turning off vinyl control, set play button based on current speed
- 2494. By Owen Williams <email address hidden>
-
* removed infringing skin
* modified outlinenetbook with basic vinyl indicators and buttons
Albert Santoni (gamegod) wrote : | # |
> > - Launchpad is wrong, the diff is incomplete (why??) so this is not a full
> review yet.
>
> what do you mean the diff is incomplete?
>
The diff that Launchpad generated doesn't seem to have all the changes that you made in your branch, for whatever reason (eg. it was missing the vinylcontrolxwa
> >
> > - VinylModeSwitch.js - What does this do?
>
> that was a workaround I was using for a while. It's deprecated so I'll
> remove it.
Ok, thanks!
>
>
> > - I had previously patched timecoder.c to make it store the lookup
> >
> > table in the timecoder struct. This is absolutely necessary otherwise
> >
> > you end up with a race condition because the code isn't thread safe and you
> will get crashes and all sorts of weird problems.
> >
> > (eg. compare the new timecoder_
> >
>
> I don't think this needs to be done again. Looking in the newer xwax, it
> already is storing the def in the timecoder, and the def stores the lookup
> table in itself. It looks to me like it's doing what you describe. (That and
> it doesn't crash or give me weird problems)
The problem is that the timecode_def array is a global variable in timecoder.c. When we build the lookup table in both our VinylControlXwax threads, they can be concurrently writing to the same memory, in that timecode_def array. As well, when we free the lookup table, we're double-freeing the same memory. It's a subtle problem but it will cause crashes. You probably won't see it most of the time because it's a race condition.
The code has diverged mainly with the addition of this lut struct to timecode_def_t. Not sure if it's better to go back and merge our old timecoder.c with the latest one or whether I should try to redo all the changes from scratch to make it re-entrant again.
Albert Santoni (gamegod) wrote : | # |
After staring at the xwax code for a while, I think it's going to be quite a bit of work to get the changes right, and in the end it would probably result in another forked piece of code (hard to maintain).
I think what I'm going to do is modify vinylcontrolxwa
I'll see how this goes...
Albert Santoni (gamegod) wrote : | # |
void EngineBufferSca
- Why did you add the isNan checks here? Were you seeing NaNs appearing in this code? (If so, it might have been a symptom of a bigger problem...)
Sean M. Pappalardo (pegasus-renegadetech) wrote : | # |
xwax v0.8 is to be released very soon: http://
Is it worth updating to that version before merging this?
Owen Williams (ywwg) wrote : | # |
Yeah it's worth trying to merge that in. It looks like the API hasn't changed a lot so it shouldn't be that hard.
Albert Santoni (gamegod) wrote : | # |
In my features_
- keyboard and MIDI buttons on my controllers (basically reverted the behaviour in ControlPushButton)
- a crash in VinylControlXwax due to the code stepping out of bounds in the dPitch ringbuffer:
ringPos++ returns "ringPos" then increments the value. ++ringPos would have done what you wanted, but I've just the ringPos++ before the if statement so it's less confusing for the next person who looks at this code.
I also fixed a bug in trunk where channel pairs with non-even base numbers could be selected (like channels 2-3 instead of 3-4), which was preventing me from using my second input pair.
What else do we need to fix?
- 2498. By Owen Williams <email address hidden>
-
* merge from lp:~mixxxdevelopers/mixxx/features_xwax2_albert:
* Fixed out-of-bounds array access in VinylControlXwax ringbuffer.
* Fixed MIDI buttons and keyboard buttons for most controllers.
* Renamed xwax files to .cpp to make Visual Studio compile them as C++. (This is easier than re-implementing missing C99 stuf$
* Instead of re-implementing re-entrancy modifications in xwax's timecoder, I wrapped the relevent code in a static mutex, wh$
* New xwax vinyl control code compiles on Windows now* also update xwax to 0.8
* fix compilation under linux (as opposed to windows) - 2499. By Owen Williams <email address hidden>
-
* restore my the "safe" values I found work better
- 2500. By Owen Williams <email address hidden>
-
* don't resync if near start of track (prevents annoying jumping)
- 2501. By Owen Williams <email address hidden>
-
* aw hell, typo
- 2502. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
William Good (bkgood) wrote : | # |
Owen- it looks like you got this in your latest merge, but your SoundManager code may need updated. I changed SoundDevice to not error when it gets an AudioInput whose channels overlap with another Input. This way, a user can send audio input from DevX Channels 1-2 to vinyl control 1 and 2 and then the VC code can decide which deck to apply that vinyl control signal to (I'm guessing that's what you're doing?). This should make your life in SoundManager a little easier.
Owen Williams (ywwg) wrote : | # |
Actually... I was kind of relying on that error :). I have to build a map of the vinyl inputs, and if I get no error, then I know it's a new set of inputs. If I get an error, then I know it's a duplicate set of inputs and I have to find the previous inputs it corresponds to.
I think I can rewrite the test pretty easily, though, and this will be not as hacky.
- 2503. By Owen Williams <email address hidden>
-
* duplicate_inputs is no longer used at all, so remove it completely
RAFFI TEA (raffitea) wrote : | # |
Owen, I'd like to test on Windows but I can't check out your branch with 'bzr branch ...'
There's a file called 'lut.cpp' in lib/xwax which is a symbolic link. BZR on Windows does not support symbolic links. Please resolve that problem!
Thanks,
Tobias
Owen Williams (ywwg) wrote : | # |
ok fixed. It's just a duplicate of lut.c. I thought perhaps bzr would be smart enough to compensate for that on windows
With QT 4.7.1 (current version in Arch Linux repositories), I experience segfaults whenever I try to change something very quickly. Examples include moving a knob quickly (both through a MIDI controller and with a mouse), scratching (i.e. moving the waveform quickly), and anything else that modifies the GUI rapidly.
Here's a backtrace:
http://
It looks like the problem is with QPixmap::isNull().
The second issue that I found was that after the segfault occurs, vinyl control is broken when I start up Mixxx again. To fix it, I need to go into the audio i/o preferences, change some value and change it back again (to enable the "Apply" button) and then click "Apply." This will allow the vinyl controller to work again (turning vinyl control off and back on again without doing this doesn't have any effect).
The third issue was that the gain bars in the vinyl control setup page don't seem to be showing any activity (they just show the grid background).
Lastly, as I understand it, scratch mode has been disabled. So, the option for it should be removed from the vinyl control options in the preferences.
I completely forgot another bug that I experienced. When I have
"Pitch/Rate slider direction" set to "Down increases speed" in the
Interface tab of the preferences, the vinyl control doesn't respect
that. That is, when I slow the platter down, the waveform stretches the
wrong way and the BPM on the track indicator increases.
> With QT 4.7.1 (current version in Arch Linux repositories), I experience
> segfaults whenever I try to change something very quickly. Examples include
> moving a knob quickly (both through a MIDI controller and with a mouse),
> scratching (i.e. moving the waveform quickly), and anything else that modifies
> the GUI rapidly.
>
> Here's a backtrace:
> http://
>
> It looks like the problem is with QPixmap::isNull().
>
> The second issue that I found was that after the segfault occurs, vinyl
> control is broken when I start up Mixxx again. To fix it, I need to go into
> the audio i/o preferences, change some value and change it back again (to
> enable the "Apply" button) and then click "Apply." This will allow the vinyl
> controller to work again (turning vinyl control off and back on again without
> doing this doesn't have any effect).
>
> The third issue was that the gain bars in the vinyl control setup page don't
> seem to be showing any activity (they just show the grid background).
>
> Lastly, as I understand it, scratch mode has been disabled. So, the option
> for it should be removed from the vinyl control options in the preferences.
Albert Santoni (gamegod) wrote : | # |
Hey Owen,
Thanks for fixing stuff so quickly. I just ran valgrind on your branch, and this was the result after ~30 seconds:
http://
I ran valgrind for several hours on trunk earlier today and didn't see the QWidget painting warnings nor the EBSL errors.
I did see the inflateReset2 warnings in trunk though.
I was using the outlineNetbook skin during these tests. This might suggest there's a problem in WStatusLight... ? (as well as EBSL in your branch, trunk seemed fine)
Thanks!
Albert
- 2510. By Owen Williams <email address hidden>
-
* initialization bug
Owen Williams (ywwg) wrote : | # |
I just pushed a possible fix for this bug. There was a for-loop:
for (int i=0; i<m_iNoPos; ++i)
Which I think means that it starts at 1, not 0? I don't really know how to read valgrind so I'm not sure if that was the problem.
I was still getting segfaults with revision 2510, but I think I see the problem.
When moving knobs really fast (in any skin), you eventually arrive to this backtrace:
http://
(Updated because I was an idiot and forgot to turn off symbol stripping)
After doing some poking in wstatuslight.cpp, I found that at one point, m_iPos was 127, but m_iNoPos was only 1. Therefore, we are trying to access a bad place of memory in line 117. I'm not sure what's causing m_iPos to become 127 -- I don't know enough about the code.
However, when I changed line 113 from:
if (m_pPixmapSLs[
to
if (m_iPos < m_iNoPos && m_pPixmapSLs[
I couldn't reproduce the segfaults any more.
This probably isn't the "correct" fix (although I couldn't see any visible result of the change other than it not crashing), because something is likely causing m_iPos to be set incorrectly.
Owen Williams (ywwg) wrote : | # |
You rock, nice debugging. I tweaked the code so that the improper values are just thrown out completely so m_iPos doesn't get set to an invalid value.
I'll see if I can figure out why that wrong value is being set, but it might be something simple like some lights consider "127" to be on instead of "1". Perhaps that's a special case.
No problem, thanks for the nice new features! I'll also continue testing and debugging whenever I get a chance. Otherwise, your branch seems to be working fine now, except for the volume gain on the vinyl control preferences panel. That might also be my window manager causing problems, so you'll probably need someone else to test that.
> You rock, nice debugging. I tweaked the code so that the improper values are
> just thrown out completely so m_iPos doesn't get set to an invalid value.
>
> I'll see if I can figure out why that wrong value is being set, but it might
> be something simple like some lights consider "127" to be on instead of "1".
> Perhaps that's a special case.
Owen Williams (ywwg) wrote : | # |
Ah, I got it. The PeakIndicator used in the LateNight theme (which was crashing) is a statuslight, and that sets the value to 1, or in midi, 127. The old code set the light to off when the value was 0 and on when it was anything else.
So I changed the code so that for two-state lights, the old way applies: 0=off, nonzero=on. For multistate status lights (like mine), the values have to be exact.
This should solve the crashes (I hope).
- 2513. By Owen Williams <email address hidden>
-
* fix (finally?) status light crashes and non-lighting boolean lights
Albert Santoni (gamegod) wrote : | # |
Thanks Owen! I will try to run Valgrind again on your branch to confirm as
soon as I have a chance.
On 2010-11-17 1:56 PM, "Owen Williams" <email address hidden> wrote:
> Ah, I got it. The PeakIndicator used in the LateNight theme (which was
crashing) is a statuslight, and that sets the value to 1, or in midi, 127.
The old code set the light to off when the value was 0 and on when it was
anything else.
>
> So I changed the code so that for two-state lights, the old way applies:
0=off, nonzero=on. For multistate status lights (like mine), the values have
to be exact.
>
> This should solve the crashes (I hope).
> --
> https:/
> You are reviewing the proposed merge of lp:~ywwg/mixxx/features_xwax2 into
lp:mixxx.
Oops, it looks like features_xwax2, when merged with the last revision of trunk that didn't have conflicts (i.e. probably not a problem), doesn't control the second deck with the second vinyl input!
I just got my second audio card in today so that I could finally allocate one turntable to the first deck and the other to the second deck. However, both decks follow one turntable, no matter what I set the second deck's input to.
It sounds like just a simple typo or something. I'll try to look through the code to figure out what it is, but Owen probably has a better idea.
- 2514. By Owen Williams <email address hidden>
-
* merge with lp:mixxx -r 2552
- 2515. By Owen Williams <email address hidden>
-
* merge with lp:mixxx -r 2557
- 2516. By Owen Williams <email address hidden>
-
* merge with lp:mixxx -r 2559
- 2517. By Owen Williams <email address hidden>
-
* don't print to cout
- 2518. By Owen Williams <email address hidden>
-
* use the control object value for samplerate instead of config value. this solves wrong samplerate bugs (because
the config file can be out of date when using JACK) - 2519. By Owen Williams <email address hidden>
-
* make sure vinyl input hashes are truly unique
Sorry, I have another bug to report. Looks like waveform/BPM scaling features have disappeared? Possibly a conflict resolution problem?
- 2520. By Owen Williams <email address hidden>
-
* confirm device samplerate matches configured samplerate (can be wrong with JACK)
and reopen devices and vinyl controllers if there's a discrepency - 2521. By Owen Williams <email address hidden>
-
* fix not respecting slider direction when switching to constant mode
- 2522. By Owen Williams <email address hidden>
-
* sigh, yet another stab at getting samplerates to all agree
- 2523. By Owen Williams <email address hidden>
-
* only analyze samples for the input device we were asked to analyze
- 2524. By Owen Williams <email address hidden>
-
* step 1 of fixing linear scaler
- 2525. By Owen Williams <email address hidden>
-
* step two of rewriting the scaler. sounds better maybe?
- 2526. By Owen Williams <email address hidden>
-
* audio work step three, pre-cleanup
- 2527. By Owen Williams <email address hidden>
-
* finally done(?) fixing the scaler
- 2528. By Owen Williams <email address hidden>
-
* more soul-crushing work on the scaler
* still bad clicks and pops when rapidly changing speed - 2529. By Owen Williams <email address hidden>
-
* another scaler tweak
- 2530. By Owen Williams <email address hidden>
-
* I'll live with it. scaler work
- 2531. By Owen Williams <email address hidden>
-
* clean up (scaler)
- 2532. By Owen Williams <email address hidden>
-
* tweak ramps
- 2533. By Owen Williams <email address hidden>
-
* fixed it. really. the scaler works.
- 2534. By Owen Williams <email address hidden>
-
* fix occasional incorrect bufferings, and leave in all my debug crap (commented out)
* attempted to create multi-buffer ramp-in, failed miserably, have no idea why - 2535. By Owen Williams <email address hidden>
-
* merge with lp:mixxx 2580
- 2536. By Owen Williams <email address hidden>
-
* undo my JACK samplerate fixes to prepare for alternative trunk fix
- 2537. By Owen Williams <email address hidden>
-
* merge lp:mixxx 2581
- 2538. By Owen Williams <email address hidden>
-
* fix end-of-track playbutton flashing
- 2539. By Owen Williams <email address hidden>
-
* fix end-of-track behavior
- 2540. By Owen Williams <email address hidden>
-
* try using two samples for rampout
* tried holding last sample, not setting to zero -- meh - 2541. By Owen Williams <email address hidden>
-
* check for end-of-track after we resync in case we're no longer
at end of track - 2542. By Owen Williams <email address hidden>
-
* more tweaks to scaler because I just can't stop myself
- 2543. By Owen Williams <email address hidden>
-
* based on profiling data, flip loop in pushBuffer -- massive CPU use reduction!
- 2544. By Owen Williams <email address hidden>
-
* hrm, I thought that wouldn't happen, but no worries, it'll just loop again
Owen Williams (ywwg) wrote : | # |
yeah yeah... trunk moves fast. Should be ok now.
- 2547. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2548. By Owen Williams <email address hidden>
-
* make doubly sure that vinylcontrol is off if there's no vinyl input
- 2549. By Owen Williams <email address hidden>
-
* split the scale function into two, avoiding an awful
hack involving dangerously reassigning the buffer member. - 2550. By Owen Williams <email address hidden>
-
* fix bug in last revision (was double-updating a member var)
- 2551. By Owen Williams <email address hidden>
-
* try to prevent spurious needleskip detections
- 2552. By Owen Williams <email address hidden>
-
* that eliminated needleskip detection completely, revert
I can't say that this is xwax2's (revision 2546) fault yet and this is going to be a really hard one to debug, but twice now, I've noticed after a few hours of mix[xx]ing, the second deck suddenly loses its vinyl control and just plays on its own. I have to go into the sound preferences and re-apply the audio devices in order to refresh things and make the vinyl control work again. I have no clue how to reproduce this problem and it's going to be difficult to find the root cause of it, but I'll spend January debugging as much as possible.
Has anyone else noticed this? Has anyone else been debugging this branch?
Albert Santoni (gamegod) wrote : | # |
This branch still crashes on startup while generating the timecode lookup tables on Windows, using the nightly I posted to /mess. Looks like I probably didn't fix the thread safety problem properly. (A valgrind run on Linux should immediately point out the problem though.)
I found a feature-bug kind of thing.
When the needle hits the first "warning" groove (which then transitions into the set of grooves for the last minute of the vinyl), I get the following debug message:
Debug: [VinylControlXwax 1]: record end, setting constant mode
(Here's what I mean by the location:)
http://
That's a useful feature, but the problem happens after this is triggered. Vinyl control no longer works until I go to preferences, change some audio hardware setting and then change it back (so that the Apply button is enabled) and then click Apply.
I think that what should happen is that the Vinyl Control should be disabled (i.e. in Options -> Vinyl Control, it should be unchecked for that channel). Currently, it remains checked even when switching to constant mode.
Also, it would be nice if this was user-configurable. There's one more minute left in the track after that hits -- if I forget to set the needle back, another minute is usually enough to end the track.
Owen Williams (ywwg) wrote : | # |
>
> Debug: [VinylControlXwax 1]: record end, setting constant mode
Your skin may be missing the new vinyl GUI elements. Vinyl control has three modes, which are togglable at any time* with midi or by the GUI: ABSolute, RELative, and CONSTant. Absolute mode follows the needle wherever it goes, relative keeps the playhead in the same place but takes the pitch from the vinyl, and constant just keeps playing at the last rate.
When the needle reaches the end of the record, the vinyl control switches to constant and there's an indicator light that should blink orange. At this point, you can move the needle back a few minutes and then change the playback mode to relative -- this way you can keep controlling the speed of playback.
But, the one bug report I would take from this is that when loading a new track, vinyl control should switch out of CONST and back to either ABS or REL... not sure how best to pick which
(* it is not possible to switch to ABS mode if you're already playing the record -- this prevents accidental sudden jumps. To get to ABS mode, stop the record and then switch the mode)
- 2555. By Owen Williams <email address hidden>
-
* when coming out of record-end mode, make sure we're not in constant mode
Thanks for your response, Owen. That makes sense -- I'll wait until the skin gets updated.
One fun little bug, probably the result of a bad merge or something:
Line 218 of src/library/
The rating and key columns are missing there.
- 2556. By Owen Williams <email address hidden>
-
* fixed missing parameters to db call (thanks JWC)
Owen Williams (ywwg) wrote : | # |
Ah, now I see the problem, my custom skin got blown away in a merge. I restored the skin.xml now, so try out Outline1024x600
- 2557. By Owen Williams <email address hidden>
-
* restore custom skin
Ah, with the custom skin, I can easily see what's going on.
However, while disableRecordEn
Debug: [VinylControlXwax 1]: record end, setting constant mode
Debug: [VinylControlXwax 1]: disableRecordEn
Debug: [VinylControlXwax 1]: Switched back, iVCMode= 1 , iOldMode= 1
So, that should mean that for the next track I load (or if I set my needle back and try moving backwards from the end of the current track), I should be in relative mode again. However, it seems to stay in constant mode! I'm guessing that iVCMode gets overwritten again after disableRecordEn
- 2558. By Owen Williams <email address hidden>
-
* stupid bugfix: actually set the mode when disabling record end mode
- 2559. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2560. By Owen Williams <email address hidden>
-
* fix turning on vinyl control would incorrectly pop out of CONST mode if it was set
- 2561. By Owen Williams <email address hidden>
-
* try a new needleskip algorithm
* record-end mode should be used even if needleskip is off - 2562. By Owen Williams <email address hidden>
-
* make sure vinyl control gets the correct sample rate
RAFFI TEA (raffitea) wrote : | # |
Great work Owen. Works great on OS X.
Some comments:
* It would be nice to add an "emergency mode". Switch to constant mode if time-code signal becomes bad.
* I like the BPM scaling but it changes too much if the platter is at 0% pitch. We should display the average BPM value over some period of time, let's say 500 ms.
RAFFI TEA (raffitea) wrote : | # |
Another potential bug:
If I start in absolute mode and use loops, your code seems to switch to relative mode internally. But this is not reflected on the GUI.
I would have expected some strange behavior if loops are used in absolute mode.
- 2563. By Owen Williams <email address hidden>
-
* if looping, don't allow absolute mode control
Owen Williams (ywwg) wrote : | # |
@RAFFI_TEA, thanks for the notes. I've fixed the looping bug, so that should be all set now.
The "emergency mode" you describe is already pretty much implemented. Serato vinyl has two pieces of information: pitch and absolute timecode. Mixxx already knows how to deal with situations where we have pitch information but not timecode. As for situations where the pitch data is not audible, it's not really possible to distinguish between "bad signal" and "I lifted the needle." I have some little tricks, like a .1 second delay before I stop playback, and needle-skip detection, but that's about all I can do.
Under what situations are you seeing too much bpm scaling? I have a lot of code to try to make it reasonable already, and last time we talked about this we decided it takes a little getting used to but is actually pretty good.
RAFFI TEA (raffitea) wrote : | # |
Hey Owen,
I am struggling a lot with dust in my attic floor -- don't ask me why :-)
As long as I have clean needles the time-code is interpreted fine. But when times goes by the signal becomes rather bad. The pitch jumps up and down.
In Mixxx's preferences, there is a possibility to check the time-code signal. But users won't have preferences opened at all times. Is it possible check the signal quality?
Or in other words, xwax but also commercial product provide a kind of time-code panel (a.k.a. scratch panel in Traktor). There a kind of "circle" if the time-code signal is good. It becomes a kind of dotted ellipse or "egg" if the signal quality decreases. But just before the time-code becomes too bad, an emergency mode (const mode) activates. (Scratch panel: http://
<<<< Under what situations are you seeing too much bpm scaling?
<<<< I have a lot of code to try to make it reasonable already
<<<< and last time we talked about this we decided it takes a
<<<< little gettingused to but is actually pretty good.
At standard pitch, the BPM values alternate between +- 0.4 BPM (assume 128 BPM). I don't hear any noticeable pitch movements through my headphones. I really like the feature but I think BPM values of +- 0.4 are outliers and beginners may be confused to learn beat matching (also they should learn it by ear). Therefore I think some average BPM computation is plausible. (But maybe my turntables need overhaul)
Owen Williams (ywwg) wrote : | # |
> BPM values of +- 0.4
These aren't outliers, you're just seeing how much wow/flutter your turntable actually has (I'm already smoothing the values anyway).
Enough people have mentioned this issue that it's probably worth addressing. I've added QTimers in the display widgets that update the BPM displays every 1/4th second. I'd like to strike a balance between updating too fast and too slow.
- 2564. By Owen Williams <email address hidden>
-
* only update bpm / rate display every 1/4 second
RAFFI TEA (raffitea) wrote : | # |
Thanks, Owen. The BPM display is easier to read now :-)
- 2565. By Owen Williams <email address hidden>
-
* use a better method for setting constant mode during needle skip
- 2566. By Owen Williams <email address hidden>
-
* make sure to set position no matter what the new scaler is
- 2567. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2568. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2569. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2570. By Owen Williams <email address hidden>
-
* make sure table is sorted on startup
- 2571. By Owen Williams <email address hidden>
-
* fix sneaky little overflow bug
- 2572. By Owen Williams <email address hidden>
-
* introduce new vinyl signal quality widget:
** ring-shaped plot thanks to xwax's built-in functionality
** color representing % of samples returning valid positions: red (0) to green (all)
** a sweep marker, showing direction of vinyl rotation (for left-right switcheroo problems)
** fancy green grid, meaning nothing at all
* note, check XXX: comment about endianness... is this a problem anywhere? (colors will be wrong)
* thanks to Albert for giving me a head-start on this - 2573. By Owen Williams <email address hidden>
-
* redo needleskip checking *again*. I think it's pretty good now but needs testing
- 2574. By Owen Williams <email address hidden>
-
* substantial merge from lp:mixxx
- 2575. By Owen Williams <email address hidden>
-
* signal widget: if no timer, just draw the bg
- 2576. By Owen Williams <email address hidden>
-
* oops
- 2577. By Owen Williams <email address hidden>
-
* make steadypitch a little more forgiving again
- 2578. By Owen Williams <email address hidden>
-
* only show grid lines if there's a vinyl input that could be receiving a signal
- 2579. By Owen Williams <email address hidden>
-
* fix incorrect midi programming for needleskip mode
- 2580. By Owen Williams <email address hidden>
-
* merge with lp:mixxx
- 2581. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2582. By Owen Williams <email address hidden>
-
* remap playposition from -.14 to 1.14 for nicer midi value but still preroll (and eventual postroll)
- 2583. By Owen Williams <email address hidden>
-
* support 45RPM vinyl control
* support different vinyl types on each deck
* (note, these changes are not well-tested) - 2584. By Owen Williams <email address hidden>
-
* fix a bug in xwax (a first!) where position wasn't adjusted for 45rpm speeds
- 2585. By Owen Williams <email address hidden>
-
* neaten up the vinyl dialog, and add an apply button which redoes the sound setup so user can see changes in the vinyl signal graphs
- 2586. By Owen Williams <email address hidden>
-
* add a special version of the vinyl configuration window that is all grayed out for people who don't have vinyl support
* TODO: create a special page on mixxx explaining the app store GPL bullshit concisely, with a big huge link to the
download page - 2587. By Owen Williams <email address hidden>
-
* fix crash in midiscript: check for NULL before trying to access object
- 2588. By Owen Williams <email address hidden>
-
* fix skin
- 2589. By Owen Williams <email address hidden>
-
* don't need this
- 2590. By Owen Williams <email address hidden>
-
* prevent crashes when no decks have started up
- 2591. By Owen Williams <email address hidden>
-
* NEEDS TESTING: first pass at implementing relative mode cue point selection.
- 2592. By Owen Williams <email address hidden>
-
* don't seek to inactive hotcues
- 2593. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2594. By Owen Williams <email address hidden>
-
* merge from lp:mixxx (blows away scratchlib among other things)
- 2595. By Owen Williams <email address hidden>
-
* EXPERIMENTAL: initial needle cueing code
* redid an if statement which caused huge changes in tabbing - 2596. By Owen Williams <email address hidden>
-
* added yet another pushbutton, for enabling vinyl cueing mode
* make CDJ resync mode really only active with CDJs - 2597. By Owen Williams <email address hidden>
-
* finish needle cueing mode: now has three possible settings: off, cue only to Cue point, and cue to nearest hotcue
- 2598. By Owen Williams <email address hidden>
-
* giant merge from lp:mixxx
- 2599. By Owen Williams <email address hidden>
-
* merge from lp:mixxx, hope my linear scale code still works
- 2600. By Owen Williams <email address hidden>
-
* whoops
- 2601. By Owen Williams <email address hidden>
-
* remove redundant line of extra code that wasn't needed because it was redundant
- 2602. By Owen Williams <email address hidden>
-
* fix a bunch of tabbing issues to make the eventual merge cleaner
- 2603. By Owen Williams <email address hidden>
-
* asantoni's fixes for building on windows
Albert Santoni (gamegod) wrote : | # |
Hey Owen,
The only remaining thing we might want to nitpick in this branch is the changes you made to CachingReader. If CachingReader is supposed to be reading from a file, perhaps it would make more sense for the "if pre-roll then read silence" logic to be outside/before CachingReader instead.
I'm OK if we just leave a note saying maybe there's a better spot for this. I don't think RAMAN nor the scaling classes are the right place for it, so that basically leaves EngineBuffer.
Owen Williams (ywwg) wrote : | # |
I'll add the comment for now. I'm not sure how I would move that code out of the reader, because enginebuffer is pretty far removed from the file itself and would have to know exactly how many samples of the buffer should be silence and how many are audio data. This number would depend on: sample rate, playback rate, and if the DJ is reversing direction (scratching the first beat). I'm afraid it would be ugly and bug-prone.
RAFFI TEA (raffitea) wrote : | # |
Hey Owen,
tested relative needle cueing. Works great. I really like it. I have never used Scratch Live but now I can imagine how it feels :-) Good work.
Please note that I have not tested the HOT CUE JUMPING!!! This will follow soon.
RAFFI TEA (raffitea) wrote : | # |
Just testing the 45RPM seed. Works as expected. No problems during beat-matching.
I've seen a fix for Windows in the revision notes. Can I used your branch on Windows now? No crash during time-code (LUT) initialization?
RAFFI TEA (raffitea) wrote : | # |
> Hey Owen,
>
> tested relative needle cueing. Works great. I really like it. I have never
> used Scratch Live but now I can imagine how it feels :-) Good work.
>
> Please note that I have not tested the HOT CUE JUMPING!!! This will follow
> soon.
Hot cue jump = your thirdly implemented option --> jump to the nearest hot cue --> see r2597
Albert Santoni (gamegod) wrote : | # |
On Fri, Mar 18, 2011 at 1:10 PM, RAFFI TEA <email address hidden> wrote:
> Just testing the 45RPM seed. Works as expected. No problems during beat-matching.
>
> I've seen a fix for Windows in the revision notes. Can I used your branch on Windows now? No crash during time-code (LUT) initialization?
Correct, this branch works fine for me on Windows now. :)
- 2606. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2607. By Owen Williams <email address hidden>
-
* don't use keyboard events when class methods will do (solves compiz suckage lp:738881)
- 2608. By Owen Williams <email address hidden>
-
* finish implementation of vinyl-controlled track selection
* new method: use control track to select track, then move needle back to regular area and new track will play. Selecting a new track manually will override track selection - 2609. By Owen Williams <email address hidden>
-
* unneeded commented code
RAFFI TEA (raffitea) wrote : | # |
> > BPM values of +- 0.4
>
> These aren't outliers, you're just seeing how much wow/flutter your turntable
> actually has (I'm already smoothing the values anyway).
>
> Enough people have mentioned this issue that it's probably worth addressing.
> I've added QTimers in the display widgets that update the BPM displays every
> 1/4th second. I'd like to strike a balance between updating too fast and too
> slow.
Hey Owen,
I am really thankful that BPM updates every 1/4th second. The BPM display is much better to read but it is still not perfectly to read for me. I really recommend to build the average BPM for every time interval. Traktor uses a similar approach which I think is pretty useful to find the perfect pitch position faster.
If you think it is not usefully maybe you can direct me to the code so I can implement for my own :-)
Again, thanks for your awesome work!
Tobias
RJ Skerry-Ryan (rryan) wrote : | # |
Yea, I was looking at that QTimer suspiciously as I was doing the review :)
I think that instead of doing the updates in the UI (if 4 decks are going,
thats 4x250ms timers = 16Hz GUI updates even if no deck has a loaded track,
we should probably be issuing updates from within the engine. This can be
done in src/engine/
window. There is already an engine update interval (e.g. how fast the
position marker on the waveform overview is updated) that we should
piggyback off of.
The added benefit here is that the a MIDI script that updates a BPM LCD on a
MIDI controller (e.g. Stanton SCS1 series) will also update at the same
interval and with the same value.
the file_bpm control is generally the detected BPM of the file and 'bpm'
control is for the engine's current dynamic rate. They are the same
currently, WNumberBpm scales the 'bpm' control by the current rate (there's
an open bug to change that too) but I think they should be changed so that
the 'bpm' control updates in intervals that are the average BPM for the
window in which the rate was calculated.
On Mon, Mar 21, 2011 at 11:03 AM, RAFFI TEA <email address hidden>wrote:
> > > BPM values of +- 0.4
> >
> > These aren't outliers, you're just seeing how much wow/flutter your
> turntable
> > actually has (I'm already smoothing the values anyway).
> >
> > Enough people have mentioned this issue that it's probably worth
> addressing.
> > I've added QTimers in the display widgets that update the BPM displays
> every
> > 1/4th second. I'd like to strike a balance between updating too fast and
> too
> > slow.
>
> Hey Owen,
>
> I am really thankful that BPM updates every 1/4th second. The BPM display
> is much better to read but it is still not perfectly to read for me. I
> really recommend to build the average BPM for every time interval. Traktor
> uses a similar approach which I think is pretty useful to find the perfect
> pitch position faster.
>
> If you think it is not usefully maybe you can direct me to the code so I
> can implement for my own :-)
>
> Again, thanks for your awesome work!
>
> Tobias
> --
> https:/
> Your team Mixxx Development Team is subscribed to branch lp:mixxx.
>
RJ Skerry-Ryan (rryan) wrote : | # |
(the open bug I referenced is here :
https:/
On Mon, Mar 21, 2011 at 11:27 AM, RJ Ryan <email address hidden> wrote:
> Yea, I was looking at that QTimer suspiciously as I was doing the review :)
>
> I think that instead of doing the updates in the UI (if 4 decks are going,
> thats 4x250ms timers = 16Hz GUI updates even if no deck has a loaded track,
> we should probably be issuing updates from within the engine. This can be
> done in src/engine/
> window. There is already an engine update interval (e.g. how fast the
> position marker on the waveform overview is updated) that we should
> piggyback off of.
>
> The added benefit here is that the a MIDI script that updates a BPM LCD on
> a
> MIDI controller (e.g. Stanton SCS1 series) will also update at the same
> interval and with the same value.
>
> the file_bpm control is generally the detected BPM of the file and 'bpm'
> control is for the engine's current dynamic rate. They are the same
> currently, WNumberBpm scales the 'bpm' control by the current rate (there's
> an open bug to change that too) but I think they should be changed so that
> the 'bpm' control updates in intervals that are the average BPM for the
> window in which the rate was calculated.
>
>
> On Mon, Mar 21, 2011 at 11:03 AM, RAFFI TEA <<email address hidden>
> >wrote:
>
> > > > BPM values of +- 0.4
> > >
> > > These aren't outliers, you're just seeing how much wow/flutter your
> > turntable
> > > actually has (I'm already smoothing the values anyway).
> > >
> > > Enough people have mentioned this issue that it's probably worth
> > addressing.
> > > I've added QTimers in the display widgets that update the BPM displays
> > every
> > > 1/4th second. I'd like to strike a balance between updating too fast
> and
> > too
> > > slow.
> >
> > Hey Owen,
> >
> > I am really thankful that BPM updates every 1/4th second. The BPM display
> > is much better to read but it is still not perfectly to read for me. I
> > really recommend to build the average BPM for every time interval.
> Traktor
> > uses a similar approach which I think is pretty useful to find the
> perfect
> > pitch position faster.
> >
> > If you think it is not usefully maybe you can direct me to the code so I
> > can implement for my own :-)
> >
> > Again, thanks for your awesome work!
> >
> > Tobias
> > --
> > https:/
> > Your team Mixxx Development Team is subscribed to branch lp:mixxx.
> >
>
> --
> https:/
> Your team Mixxx Development Team is subscribed to branch lp:mixxx.
>
- 2610. By Owen Williams <email address hidden>
-
* tweaks to control track selection to make it better
- 2611. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2612. By Owen Williams <email address hidden>
-
* merge cleanup
- 2613. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2614. By Owen Williams <email address hidden>
-
* fix linear scaler pops and clicks once and for all
* disable vinyl emu in enginechannel because it hurts more than it helps - 2615. By Owen Williams <email address hidden>
-
* comment debug statement
- 2616. By Owen Williams <email address hidden>
-
* no really, linear scaler is probably better than serato's now
- 2617. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
RAFFI TEA (raffitea) wrote : | # |
Tested with Traktor vinyls. Works great! Don't feel any differences compared to Serato.
I also used Serato and Traktor vinyls simultaneously on my turntables. Works as expected.
Why don't we support Traktor CDs? --Xwax limitation?
Found a bug! I think it's the result of a problem with the merging and all the recent changes.
If you press a hotcue several times quickly while vinyl control is playing the record, the "play" button will remain in the "playing" state indefinitely (really, I tried everything to get it to shut off), meaning that you can't load a new track, so you have to restart Mixxx.
RAFFI TEA (raffitea) wrote : | # |
> Found a bug! I think it's the result of a problem with the merging and all
> the recent changes.
>
> If you press a hotcue several times quickly while vinyl control is playing the
> record, the "play" button will remain in the "playing" state indefinitely
> (really, I tried everything to get it to shut off), meaning that you can't
> load a new track, so you have to restart Mixxx.
Found another minor bug. Try to set a loop and look at the play button while the loop is active. The play button goes off and on ("blinks") but the music just plays fine.
- 2621. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2622. By Owen Williams <email address hidden>
-
* fix play button turning off while looping
* clean up some unused variables - 2623. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
- 2624. By Owen Williams <email address hidden>
-
* updated xwax safe values for traktor (thanks Tobias!)
RJ Skerry-Ryan (rryan) wrote : | # |
Hey Owen,
I read over your changes and I have some comments and things that need fixing. I covered things that weren't src/vinyl* classes. I'm going to leave those to Albert. This branch is a huge amount of work so thanks so much for your dedication to making Mixxx vinyl control rock!
First off, I'm going to be picky about control naming :) It's
important because by publishing the controls, we're going to be
stuck with the names for years to come, so I'd like them to be
descriptive and uniform. Also, MIDI scripters and GUI skinners
are going to have to deal w/ them, so it's important that they
be descriptive and easy to use.
* Naming of control values should be lowercase separated by
underscores on word boundaries (most controls are formatted
this way). Optionally prefix it with a general identifier like
'vinylcontrol' or 'vc' if the controls appear in a group not
specific to the feature like [VinylControl].
* For controls in [ChannelX]
-- vinylcontrol_
-- vinylcontrol_mode
-- vinylcontrol_cueing
-- vinylcontrol_
-- vinylcontrol_
* For controls in the group [VinylControl]
-- lead_in_time
-- mode
-- needle_
-- gain
Hopefully this isn't a huge deal and you can just find-replace all the instances. Let me know if I'm being a huge pain in the ass.
controlpushbutt
-------
* In ControlPushButt
set to 0 in ControlObject constructor, redundant
* I think a CPB w/ multiple states should be a "toggle" button
with multiple states. Non-toggle should remain a momentary 0 or
1 button. Does that screw anything up? Just move the check in
setvalueFromMidi from the !m_bIsToggleButton block to the
m_bIsToggleButton block. I think all you should have to do is
setToggle(true) for the vinyl cue and mode controls in
RateControl.
mathstuff.h
-----------
* Super-yes on adding the isnan stuff to this file. Though why is
is the non-Windows #include of math.h commented?
mixxx.cpp
---------
* This config initialization / saving code, along with all the
code in SoundManager, should be migrated to a
VinylControlM
post-merge though.
widget/
widget/
-------
* I'm pretty shaky on these changes (the range ajustments). I
think it might help me if I explain the entire path the
playposition control travels from CO to the widget:
* playposition is a ControlPotmeter and it has a range of -.14 to
1.14. Let's consider the case that playposition is 0 (a track
has just been loaded)
* When playposition is set to a value, an update for its proxies is queued.
* On a CO::sync(), this update is propagated to the proxies via
COT::setExtern.
* All GUI widgets mapped to the skin have a
ControlObject
COTW, COTW::setExtern is called. This posts a Qt event to the
COTM (base class) with the value from
CO::getValueT
- 2625. By Owen Williams <email address hidden>
-
* change ALL vinyl control object names
* requires skin and config file updates - 2626. By Owen Williams <email address hidden>
-
* typo in control object name
- 2627. By Owen Williams <email address hidden>
-
* make multistate pushbottons toggling
- 2628. By Owen Williams <email address hidden>
-
* revert numberrate, don't do throttling here
- 2629. By Owen Williams <email address hidden>
-
* fix typo
- 2630. By Owen Williams <email address hidden>
-
* get rid of special buffers for scratching, just use the buffer we have
- 2631. By Owen Williams <email address hidden>
-
* little tiny code-neatening
- 2632. By Owen Williams <email address hidden>
-
* fix some memory leaks in status light code
- 2633. By Owen Williams <email address hidden>
-
* merge from lp:mixxx
*sigh* This is an interesting one.
Start playing a track at any tempo. Then, spin the vinyl back and release it to let it play. For about a quarter of a second, it'll play correctly at the original tempo, and then right afterwards, the pitch will dip very heavily (>10%) and return back to normal about 50% later.
You can also achieve this effect by scratching back and forth, but the _speed_ at which you scratch actually makes a difference. For example, if you scratch back and forth at roughly 33rpm, the pitch dip won't exist! If you scratch slowly though, the amount that the pitch dips seems to be proportional to the speed at which you scroll.
Again, the weird thing is that the record starts playing normally for about a quarter of a second and _then_ the pitch dips and returns back to normal.
Yes, I verified that this is not in fact my turntables.
RAFFI TEA (raffitea) wrote : | # |
JWC,
sounds like we have similar problems, but the only difference is, that I experience the problem with Traktor Scratch, too. Whenever I release the vinyl on my (crappy?) "Reloop RR 4000 m3d" the BPM value increases up to 5% and more. After a few seconds the BPM values narrows down to the base value. This is the reason why I always use "key lock" to prevent changes in the pitch.
Please note that I used clean needles and Serato/Traktor records for the test.
I guess that my turntables are crappy. Would be interested how a Tecnics behave... because the behavior of my turntables is not normal. All professional DJs I know don't have such problems.
I forgot to mention that I am using CV02 vinyls that are pretty clean and two needles that are fine. My turntables aren't great: Stanton STR8-60 and T.80. But looking at the samples on return from the backspin or whatever, they look okay. I'm thinking it's an error in the algorithm. Somehow, information recovery doesn't seem work quite correctly. Specifically, I'm extremely interested in figuring out why after a backspin, the sound returns normally for a quarter of a second before the dip in pitch.
I'll record some examples in a few hours for the purposes of demonstration.
Possible reason (haven't tested to confirm yet):
Last night I was playing with my main sound card as output and my usb sound card as input. Normally, I use my USB card as both input and output. For my laptop, though, the USB bus doesn't seem to have enough bandwidth to do this.
So, radiomark told me on IRC that the samples weren't syncing up because of clock differences:
> radiomark | If you imagine, it has to synchronise playback (which runs
> | from the soundcard's outbound clock) with the relative and
> | absolute vinyl data (which runs on the clock of the spinnin
> | vinyl)
I'm investigating this a bit. Maybe you already implemented it and it's something else... I'll take a look.
RJ Skerry-Ryan (rryan) wrote : | # |
OK looks like all my complaints are now fixed :) (minus VinylControlControl in EngineBuffer -- I'll remove that myself post merge) it's now up to albert to OK the vinylcontrol* classes and then we can merge this sucker.
Just to get it in writing somewhere, ywwg and I talked about how I asked him to use WDisplay instead of WStatusLight, but it turns out WDisplay is geared towards being a knob but with no input. The control connected to it is scaled to 0.0 through 128.0 based on its range. This isn't suitable for indicator controls that represent fixed status values.
After Owen's changes, WStatusLight will be to WPushButton what WDisplay is to WKnob.
Albert Santoni (gamegod) wrote : | # |
I also approve. The vinyl control logic is fairly straightforward, it
just needs loads of testing now to make sure it's tight. Some final
comments:
- I would squash those qDebugs in vinylcontrolxwa
- Maybe factor some of those constants out too.
- We should make sure a tester verifies that vinyl control still works
with a 96000 Hz samplerate.
Otherwise, great stuff! Launchpad's code review interface is broken
right now so I can't approve it properly, but RJ, feel free to merge
into trunk whenever.
Thanks Owen!
Albert
On Fri, Apr 8, 2011 at 11:21 PM, RJ Ryan <email address hidden> wrote:
> Review: Approve
> OK looks like all my complaints are now fixed :) (minus VinylControlControl in EngineBuffer -- I'll remove that myself post merge) it's now up to albert to OK the vinylcontrol* classes and then we can merge this sucker.
>
> Just to get it in writing somewhere, ywwg and I talked about how I asked him to use WDisplay instead of WStatusLight, but it turns out WDisplay is geared towards being a knob but with no input. The control connected to it is scaled to 0.0 through 128.0 based on its range. This isn't suitable for indicator controls that represent fixed status values.
>
> After Owen's changes, WStatusLight will be to WPushButton what WDisplay is to WKnob.
> --
> https:/
> You are reviewing the proposed merge of lp:~ywwg/mixxx/features_xwax2 into lp:mixxx.
>
William Good (bkgood) wrote : | # |
Can we hold off on merging this for a bit longer? I'm working though getting the code to conform (at least mostly) with style guidelines, mostly tabs to spaces and deleting characters matching \b+$ (so whitespace at end of lines), but perhaps more importantly some unbraced if's (which can create real confusion later).
- 2639. By Owen Williams <email address hidden>
-
* merge from lp:~mixxxdevelopers/mixxx/features_xwax2_bill
- 2640. By Owen Williams <email address hidden>
-
* merge from lp:~mixxxdevelopers/mixxx/features_xwax2_bill (cleanup)
William Good (bkgood) wrote : | # |
heh, I lost it too :) It looks like you got all my changes, so I'm happy. Merge away!
Preview Diff
1 | === modified file 'mixxx/build/depends.py' |
2 | --- mixxx/build/depends.py 2011-04-13 02:58:05 +0000 |
3 | +++ mixxx/build/depends.py 2011-04-16 23:24:33 +0000 |
4 | @@ -336,6 +336,7 @@ |
5 | "dlgprefcontrols.cpp", |
6 | "dlgprefbpm.cpp", |
7 | "dlgprefreplaygain.cpp", |
8 | + "dlgprefnovinyl.cpp", |
9 | "dlgbpmscheme.cpp", |
10 | "dlgabout.cpp", |
11 | "dlgprefeq.cpp", |
12 | @@ -581,6 +582,7 @@ |
13 | build.env.Uic4('dlgbpmschemedlg.ui') |
14 | # build.env.Uic4('dlgbpmtapdlg.ui') |
15 | build.env.Uic4('dlgprefvinyldlg.ui') |
16 | + build.env.Uic4('dlgprefnovinyldlg.ui') |
17 | build.env.Uic4('dlgprefrecorddlg.ui') |
18 | build.env.Uic4('dlgaboutdlg.ui') |
19 | build.env.Uic4('dlgmidilearning.ui') |
20 | |
21 | === modified file 'mixxx/build/features.py' |
22 | --- mixxx/build/features.py 2011-04-04 05:46:49 +0000 |
23 | +++ mixxx/build/features.py 2011-04-16 23:24:33 +0000 |
24 | @@ -293,9 +293,12 @@ |
25 | 'dlgprefvinyl.cpp', |
26 | 'vinylcontrolsignalwidget.cpp'] |
27 | if build.platform_is_windows: |
28 | - sources.append("#lib/xwax/timecoder_win32.c") |
29 | + sources.append("#lib/xwax/timecoder_win32.cpp") |
30 | + sources.append("#lib/xwax/lut.cpp") |
31 | else: |
32 | sources.append("#lib/xwax/timecoder.c") |
33 | + sources.append("#lib/xwax/lut.c") |
34 | + |
35 | return sources |
36 | |
37 | class Tonal(Feature): |
38 | |
39 | === modified file 'mixxx/build/qtcreator/mixxx.pro' |
40 | --- mixxx/build/qtcreator/mixxx.pro 2010-11-11 19:10:52 +0000 |
41 | +++ mixxx/build/qtcreator/mixxx.pro 2011-04-16 23:24:33 +0000 |
42 | @@ -110,6 +110,7 @@ |
43 | $$UI_DIR/ui_dlgprefrecorddlg.h \ |
44 | $$UI_DIR/ui_dlgprefsounddlg.h \ |
45 | $$UI_DIR/ui_dlgprefvinyldlg.h \ |
46 | + $$UI_DIR/ui_dlgprefnovinyldlg.h \ |
47 | $$UI_DIR/ui_dlgprefnomididlg.h |
48 | |
49 | INCLUDEPATH += src \ |
50 | |
51 | === added file 'mixxx/lib/xwax/lut.c' |
52 | --- mixxx/lib/xwax/lut.c 1970-01-01 00:00:00 +0000 |
53 | +++ mixxx/lib/xwax/lut.c 2011-04-16 23:24:33 +0000 |
54 | @@ -0,0 +1,110 @@ |
55 | +/* |
56 | + * Copyright (C) 2010 Mark Hills <mark@pogo.org.uk> |
57 | + * |
58 | + * This program is free software; you can redistribute it and/or |
59 | + * modify it under the terms of the GNU General Public License |
60 | + * version 2, as published by the Free Software Foundation. |
61 | + * |
62 | + * This program is distributed in the hope that it will be useful, but |
63 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
64 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
65 | + * General Public License version 2 for more details. |
66 | + * |
67 | + * You should have received a copy of the GNU General Public License |
68 | + * version 2 along with this program; if not, write to the Free |
69 | + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
70 | + * MA 02110-1301, USA. |
71 | + * |
72 | + */ |
73 | + |
74 | +#include <stdio.h> |
75 | +#include <stdlib.h> |
76 | + |
77 | +#include "lut.h" |
78 | + |
79 | +/* The number of bits to form the hash, which governs the overall size |
80 | + * of the hash lookup table, and hence the amount of chaining */ |
81 | + |
82 | +#define HASH_BITS 16 |
83 | + |
84 | +#define HASH(timecode) ((timecode) & ((1 << HASH_BITS) - 1)) |
85 | +#define NO_SLOT ((unsigned)-1) |
86 | + |
87 | + |
88 | +/* Initialise an empty hash lookup table to store the given number |
89 | + * of timecode -> position lookups */ |
90 | + |
91 | +int lut_init(struct lut_t *lut, int nslots) |
92 | +{ |
93 | + int n, hashes; |
94 | + size_t bytes; |
95 | + |
96 | + hashes = 1 << HASH_BITS; |
97 | + bytes = sizeof(struct slot_t) * nslots + sizeof(slot_no_t) * hashes; |
98 | + |
99 | + fprintf(stderr, "Lookup table has %d hashes to %d slots" |
100 | + " (%d slots per hash, %zuKb)\n", |
101 | + hashes, nslots, nslots / hashes, bytes / 1024); |
102 | + |
103 | + lut->slot = (struct slot_t*)malloc(sizeof(struct slot_t) * nslots); |
104 | + if (lut->slot == NULL) { |
105 | + perror("malloc"); |
106 | + return -1; |
107 | + } |
108 | + |
109 | + lut->table = (slot_no_t*)malloc(sizeof(slot_no_t) * hashes); |
110 | + if (lut->table == NULL) { |
111 | + perror("malloc"); |
112 | + return -1; |
113 | + } |
114 | + |
115 | + for (n = 0; n < hashes; n++) |
116 | + lut->table[n] = NO_SLOT; |
117 | + |
118 | + lut->avail = 0; |
119 | + |
120 | + return 0; |
121 | +} |
122 | + |
123 | + |
124 | +void lut_clear(struct lut_t *lut) |
125 | +{ |
126 | + free(lut->table); |
127 | +} |
128 | + |
129 | + |
130 | +void lut_push(struct lut_t *lut, unsigned int timecode) |
131 | +{ |
132 | + unsigned int hash; |
133 | + slot_no_t slot_no; |
134 | + struct slot_t *slot; |
135 | + |
136 | + slot_no = lut->avail++; /* take the next available slot */ |
137 | + |
138 | + slot = &lut->slot[slot_no]; |
139 | + slot->timecode = timecode; |
140 | + |
141 | + hash = HASH(timecode); |
142 | + slot->next = lut->table[hash]; |
143 | + lut->table[hash] = slot_no; |
144 | +} |
145 | + |
146 | + |
147 | +unsigned int lut_lookup(struct lut_t *lut, unsigned int timecode) |
148 | +{ |
149 | + unsigned int hash; |
150 | + slot_no_t slot_no; |
151 | + struct slot_t *slot; |
152 | + |
153 | + hash = HASH(timecode); |
154 | + slot_no = lut->table[hash]; |
155 | + |
156 | + while (slot_no != NO_SLOT) { |
157 | + slot = &lut->slot[slot_no]; |
158 | + if (slot->timecode == timecode) |
159 | + return slot_no; |
160 | + slot_no = slot->next; |
161 | + } |
162 | + |
163 | + return (unsigned)-1; |
164 | +} |
165 | |
166 | === added file 'mixxx/lib/xwax/lut.cpp' |
167 | --- mixxx/lib/xwax/lut.cpp 1970-01-01 00:00:00 +0000 |
168 | +++ mixxx/lib/xwax/lut.cpp 2011-04-16 23:24:33 +0000 |
169 | @@ -0,0 +1,110 @@ |
170 | +/* |
171 | + * Copyright (C) 2010 Mark Hills <mark@pogo.org.uk> |
172 | + * |
173 | + * This program is free software; you can redistribute it and/or |
174 | + * modify it under the terms of the GNU General Public License |
175 | + * version 2, as published by the Free Software Foundation. |
176 | + * |
177 | + * This program is distributed in the hope that it will be useful, but |
178 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
179 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
180 | + * General Public License version 2 for more details. |
181 | + * |
182 | + * You should have received a copy of the GNU General Public License |
183 | + * version 2 along with this program; if not, write to the Free |
184 | + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
185 | + * MA 02110-1301, USA. |
186 | + * |
187 | + */ |
188 | + |
189 | +#include <stdio.h> |
190 | +#include <stdlib.h> |
191 | + |
192 | +#include "lut.h" |
193 | + |
194 | +/* The number of bits to form the hash, which governs the overall size |
195 | + * of the hash lookup table, and hence the amount of chaining */ |
196 | + |
197 | +#define HASH_BITS 16 |
198 | + |
199 | +#define HASH(timecode) ((timecode) & ((1 << HASH_BITS) - 1)) |
200 | +#define NO_SLOT ((unsigned)-1) |
201 | + |
202 | + |
203 | +/* Initialise an empty hash lookup table to store the given number |
204 | + * of timecode -> position lookups */ |
205 | + |
206 | +int lut_init(struct lut_t *lut, int nslots) |
207 | +{ |
208 | + int n, hashes; |
209 | + size_t bytes; |
210 | + |
211 | + hashes = 1 << HASH_BITS; |
212 | + bytes = sizeof(struct slot_t) * nslots + sizeof(slot_no_t) * hashes; |
213 | + |
214 | + fprintf(stderr, "Lookup table has %d hashes to %d slots" |
215 | + " (%d slots per hash, %zuKb)\n", |
216 | + hashes, nslots, nslots / hashes, bytes / 1024); |
217 | + |
218 | + lut->slot = (struct slot_t*)malloc(sizeof(struct slot_t) * nslots); |
219 | + if (lut->slot == NULL) { |
220 | + perror("malloc"); |
221 | + return -1; |
222 | + } |
223 | + |
224 | + lut->table = (slot_no_t*)malloc(sizeof(slot_no_t) * hashes); |
225 | + if (lut->table == NULL) { |
226 | + perror("malloc"); |
227 | + return -1; |
228 | + } |
229 | + |
230 | + for (n = 0; n < hashes; n++) |
231 | + lut->table[n] = NO_SLOT; |
232 | + |
233 | + lut->avail = 0; |
234 | + |
235 | + return 0; |
236 | +} |
237 | + |
238 | + |
239 | +void lut_clear(struct lut_t *lut) |
240 | +{ |
241 | + free(lut->table); |
242 | +} |
243 | + |
244 | + |
245 | +void lut_push(struct lut_t *lut, unsigned int timecode) |
246 | +{ |
247 | + unsigned int hash; |
248 | + slot_no_t slot_no; |
249 | + struct slot_t *slot; |
250 | + |
251 | + slot_no = lut->avail++; /* take the next available slot */ |
252 | + |
253 | + slot = &lut->slot[slot_no]; |
254 | + slot->timecode = timecode; |
255 | + |
256 | + hash = HASH(timecode); |
257 | + slot->next = lut->table[hash]; |
258 | + lut->table[hash] = slot_no; |
259 | +} |
260 | + |
261 | + |
262 | +unsigned int lut_lookup(struct lut_t *lut, unsigned int timecode) |
263 | +{ |
264 | + unsigned int hash; |
265 | + slot_no_t slot_no; |
266 | + struct slot_t *slot; |
267 | + |
268 | + hash = HASH(timecode); |
269 | + slot_no = lut->table[hash]; |
270 | + |
271 | + while (slot_no != NO_SLOT) { |
272 | + slot = &lut->slot[slot_no]; |
273 | + if (slot->timecode == timecode) |
274 | + return slot_no; |
275 | + slot_no = slot->next; |
276 | + } |
277 | + |
278 | + return (unsigned)-1; |
279 | +} |
280 | |
281 | === added file 'mixxx/lib/xwax/lut.h' |
282 | --- mixxx/lib/xwax/lut.h 1970-01-01 00:00:00 +0000 |
283 | +++ mixxx/lib/xwax/lut.h 2011-04-16 23:24:33 +0000 |
284 | @@ -0,0 +1,42 @@ |
285 | +/* |
286 | + * Copyright (C) 2009 Mark Hills <mark@pogo.org.uk> |
287 | + * |
288 | + * This program is free software; you can redistribute it and/or |
289 | + * modify it under the terms of the GNU General Public License |
290 | + * version 2, as published by the Free Software Foundation. |
291 | + * |
292 | + * This program is distributed in the hope that it will be useful, but |
293 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
294 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
295 | + * General Public License version 2 for more details. |
296 | + * |
297 | + * You should have received a copy of the GNU General Public License |
298 | + * version 2 along with this program; if not, write to the Free |
299 | + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
300 | + * MA 02110-1301, USA. |
301 | + * |
302 | + */ |
303 | + |
304 | +#ifndef LUT_H |
305 | +#define LUT_H |
306 | + |
307 | +typedef unsigned int slot_no_t; |
308 | + |
309 | +struct slot_t { |
310 | + unsigned int timecode; |
311 | + slot_no_t next; /* next slot with the same hash */ |
312 | +}; |
313 | + |
314 | +struct lut_t { |
315 | + struct slot_t *slot; |
316 | + slot_no_t *table, /* hash -> slot lookup */ |
317 | + avail; /* next available slot */ |
318 | +}; |
319 | + |
320 | +int lut_init(struct lut_t *lut, int nslots); |
321 | +void lut_clear(struct lut_t *lut); |
322 | + |
323 | +void lut_push(struct lut_t *lut, unsigned int timecode); |
324 | +unsigned int lut_lookup(struct lut_t *lut, unsigned int timecode); |
325 | + |
326 | +#endif |
327 | |
328 | === added file 'mixxx/lib/xwax/pitch.h' |
329 | --- mixxx/lib/xwax/pitch.h 1970-01-01 00:00:00 +0000 |
330 | +++ mixxx/lib/xwax/pitch.h 2011-04-16 23:24:33 +0000 |
331 | @@ -0,0 +1,71 @@ |
332 | +/* |
333 | + * Copyright (C) 2010 Mark Hills <mark@pogo.org.uk> |
334 | + * |
335 | + * This program is free software; you can redistribute it and/or |
336 | + * modify it under the terms of the GNU General Public License |
337 | + * version 2, as published by the Free Software Foundation. |
338 | + * |
339 | + * This program is distributed in the hope that it will be useful, but |
340 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
341 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
342 | + * General Public License version 2 for more details. |
343 | + * |
344 | + * You should have received a copy of the GNU General Public License |
345 | + * version 2 along with this program; if not, write to the Free |
346 | + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
347 | + * MA 02110-1301, USA. |
348 | + * |
349 | + */ |
350 | + |
351 | +#ifndef PITCH_H |
352 | +#define PITCH_H |
353 | + |
354 | +/* Values for the filter concluded experimentally */ |
355 | + |
356 | +#define ALPHA (1.0/512) |
357 | +#define BETA (ALPHA/1024) |
358 | + |
359 | +/* State of the pitch calculation filter */ |
360 | + |
361 | +struct pitch_t { |
362 | + float dt, x, v; |
363 | +}; |
364 | + |
365 | +/* Prepare the filter for observations every dt seconds */ |
366 | + |
367 | +static inline void pitch_init(struct pitch_t *p, float dt) |
368 | +{ |
369 | + p->dt = dt; |
370 | + p->x = 0.0; |
371 | + p->v = 0.0; |
372 | +} |
373 | + |
374 | +/* Input an observation to the filter; in the last dt seconds the |
375 | + * position has moved by dx. |
376 | + * |
377 | + * Because the vinyl uses timestamps, the values for dx are discrete |
378 | + * rather than smooth. */ |
379 | + |
380 | +static inline void pitch_dt_observation(struct pitch_t *p, float dx) |
381 | +{ |
382 | + float predicted_x, predicted_v, residual_x; |
383 | + |
384 | + predicted_x = p->x + p->v * p->dt; |
385 | + predicted_v = p->v; |
386 | + |
387 | + residual_x = dx - predicted_x; |
388 | + |
389 | + p->x = predicted_x + residual_x * ALPHA; |
390 | + p->v = predicted_v + residual_x * BETA / p->dt; |
391 | + |
392 | + p->x -= dx; /* relative to previous */ |
393 | +} |
394 | + |
395 | +/* Get the pitch after filtering */ |
396 | + |
397 | +static inline float pitch_current(struct pitch_t *p) |
398 | +{ |
399 | + return p->v; |
400 | +} |
401 | + |
402 | +#endif |
403 | |
404 | === modified file 'mixxx/lib/xwax/timecoder.c' |
405 | --- mixxx/lib/xwax/timecoder.c 2011-04-08 06:04:27 +0000 |
406 | +++ mixxx/lib/xwax/timecoder.c 2011-04-16 23:24:33 +0000 |
407 | @@ -1,5 +1,5 @@ |
408 | /* |
409 | - * Copyright (C) 2008 Mark Hills <mark@pogo.org.uk> |
410 | + * Copyright (C) 2010 Mark Hills <mark@pogo.org.uk> |
411 | * |
412 | * This program is free software; you can redistribute it and/or |
413 | * modify it under the terms of the GNU General Public License |
414 | @@ -17,6 +17,7 @@ |
415 | * |
416 | */ |
417 | |
418 | +#include <assert.h> |
419 | #include <stdio.h> |
420 | #include <stdlib.h> |
421 | #include <string.h> |
422 | @@ -25,10 +26,8 @@ |
423 | #include "timecoder.h" |
424 | |
425 | #define ZERO_THRESHOLD 128 |
426 | -#define SIGNAL_THRESHOLD 256 |
427 | |
428 | -#define ZERO_AVG 1024 |
429 | -#define SIGNAL_AVG 256 |
430 | +#define ZERO_RC 0.001 /* time constant for zero/rumble filter */ |
431 | |
432 | #define REF_PEAKS_AVG 48 /* in wave cycles */ |
433 | |
434 | @@ -45,187 +44,201 @@ |
435 | |
436 | /* Timecode definitions */ |
437 | |
438 | - |
439 | -#define POLARITY_NEGATIVE 0 |
440 | -#define POLARITY_POSITIVE 1 |
441 | - |
442 | - |
443 | -struct timecode_def_t timecode_def[] = { |
444 | - { |
445 | - name: "serato_2a", |
446 | - desc: "Serato 2nd Ed., side A", |
447 | - resolution: 1000, |
448 | - polarity: POLARITY_POSITIVE, |
449 | - bits: 20, |
450 | - seed: 0x59017, |
451 | - tap: {2, 5, 6, 7, 8, 13, 14, 16, 17}, |
452 | - ntaps: 9, |
453 | - length: 712000, |
454 | - safe: 707000, |
455 | - lookup: NULL |
456 | - }, |
457 | - { |
458 | - name: "serato_2b", |
459 | - desc: "Serato 2nd Ed., side B", |
460 | - seed: 0x8f3c6, |
461 | - resolution: 1000, |
462 | - polarity: POLARITY_POSITIVE, |
463 | - bits: 20, |
464 | - tap: {3, 4, 6, 7, 12, 13, 14, 15, 18}, /* reverse of side A */ |
465 | - ntaps: 9, |
466 | - length: 922000, |
467 | - safe: 917000, |
468 | - lookup: NULL |
469 | - }, |
470 | - { |
471 | - name: "serato_cd", |
472 | - desc: "Serato CD", |
473 | - resolution: 1000, |
474 | - polarity: POLARITY_POSITIVE, |
475 | - bits: 20, |
476 | - seed: 0xd8b40, |
477 | - tap: {2, 4, 6, 8, 10, 11, 14, 16, 17}, |
478 | - ntaps: 9, |
479 | - length: 910000, |
480 | - safe: 900000, |
481 | - lookup: NULL |
482 | - }, |
483 | - { |
484 | - name: "traktor_a", |
485 | - desc: "Traktor Scratch, side A", |
486 | - resolution: 2000, |
487 | - polarity: POLARITY_POSITIVE, |
488 | - bits: 23, |
489 | - seed: 0x134503, |
490 | - tap: {6, 12, 18}, |
491 | - ntaps: 3, |
492 | - length: 1500000, |
493 | - safe: 1480000, |
494 | - lookup: NULL |
495 | - }, |
496 | - { |
497 | - name: "traktor_b", |
498 | - desc: "Traktor Scratch, side B", |
499 | - resolution: 2000, |
500 | - polarity: POLARITY_POSITIVE, |
501 | - bits: 23, |
502 | - seed: 0x32066c, |
503 | - tap: {6, 12, 18}, |
504 | - ntaps: 3, |
505 | - length: 2110000, |
506 | - safe: 2090000, |
507 | - lookup: NULL |
508 | - }, |
509 | - { |
510 | - name: NULL |
511 | +#define SWITCH_PHASE 0x1 /* tone phase difference of 270 (not 90) degrees */ |
512 | +#define SWITCH_PRIMARY 0x2 /* use left channel (not right) as primary */ |
513 | +#define SWITCH_POLARITY 0x4 /* read bit values in negative (not positive) */ |
514 | + |
515 | + |
516 | +static struct timecode_def_t timecode_def[] = { |
517 | + { |
518 | + .name = "serato_2a", |
519 | + .desc = "Serato 2nd Ed., side A", |
520 | + .resolution = 1000, |
521 | + .flags = 0, |
522 | + .bits = 20, |
523 | + .seed = 0x59017, |
524 | + .taps = 0x361e4, |
525 | + .length = 712000, |
526 | + .safe = 625000, |
527 | + .lookup = false |
528 | + }, |
529 | + { |
530 | + .name = "serato_2b", |
531 | + .desc = "Serato 2nd Ed., side B", |
532 | + .resolution = 1000, |
533 | + .flags = 0, |
534 | + .bits = 20, |
535 | + .seed = 0x8f3c6, |
536 | + .taps = 0x4f0d8, /* reverse of side A */ |
537 | + .length = 922000, |
538 | + .safe = 905000, |
539 | + .lookup = false |
540 | + }, |
541 | + { |
542 | + .name = "serato_cd", |
543 | + .desc = "Serato CD", |
544 | + .resolution = 1000, |
545 | + .flags = 0, |
546 | + .bits = 20, |
547 | + .seed = 0xd8b40, |
548 | + .taps = 0x34d54, |
549 | + .length = 950000, |
550 | + .safe = 940000, |
551 | + .lookup = false |
552 | + }, |
553 | + { |
554 | + .name = "traktor_a", |
555 | + .desc = "Traktor Scratch, side A", |
556 | + .resolution = 2000, |
557 | + .flags = SWITCH_PRIMARY | SWITCH_POLARITY | SWITCH_PHASE, |
558 | + .bits = 23, |
559 | + .seed = 0x134503, |
560 | + .taps = 0x041040, |
561 | + .length = 1500000, |
562 | + .safe = 1463000, |
563 | + .lookup = false |
564 | + }, |
565 | + { |
566 | + .name = "traktor_b", |
567 | + .desc = "Traktor Scratch, side B", |
568 | + .resolution = 2000, |
569 | + .flags = SWITCH_PRIMARY | SWITCH_POLARITY | SWITCH_PHASE, |
570 | + .bits = 23, |
571 | + .seed = 0x32066c, |
572 | + .taps = 0x041040, /* same as side A */ |
573 | + .length = 2110000, |
574 | + .safe = 2068000, |
575 | + .lookup = false |
576 | + }, |
577 | + { |
578 | + .name = "mixvibes_v2", |
579 | + .desc = "MixVibes V2", |
580 | + .resolution = 1300, |
581 | + .flags = SWITCH_PHASE, |
582 | + .bits = 20, |
583 | + .seed = 0x22c90, |
584 | + .taps = 0x00008, |
585 | + .length = 950000, |
586 | + .safe = 923000, |
587 | + .lookup = false |
588 | + }, |
589 | + { |
590 | + .name = "mixvibes_7inch", |
591 | + .desc = "MixVibes 7\"", |
592 | + .resolution = 1300, |
593 | + .flags = SWITCH_PHASE, |
594 | + .bits = 20, |
595 | + .seed = 0x22c90, |
596 | + .taps = 0x00008, |
597 | + .length = 312000, |
598 | + .safe = 310000, |
599 | + .lookup = false |
600 | + }, |
601 | + { |
602 | + .name = NULL |
603 | } |
604 | }; |
605 | |
606 | |
607 | -//struct timecode_def_t *def; |
608 | - |
609 | - |
610 | /* Linear Feeback Shift Register in the forward direction. New values |
611 | * are generated at the least-significant bit. */ |
612 | |
613 | -static inline int lfsr(unsigned int code, struct timecoder_t *timecoder) |
614 | -{ |
615 | - unsigned int r; |
616 | - char s, n; |
617 | - |
618 | - r = code & 1; |
619 | - |
620 | - for(n = 0; n < timecoder->tc_table->ntaps; n++) { |
621 | - s = *(timecoder->tc_table->tap + n); |
622 | - r += (code & (1 << s)) >> s; |
623 | - } |
624 | - |
625 | - return r & 0x1; |
626 | -} |
627 | - |
628 | - |
629 | -/* Linear Feeback Shift Register in the reverse direction. New values |
630 | - * are generated at the most-significant bit. */ |
631 | - |
632 | -static inline int lfsr_rev(unsigned int code, struct timecoder_t *timecoder) |
633 | -{ |
634 | - unsigned int r; |
635 | - char s, n; |
636 | - |
637 | - r = (code & (1 << (timecoder->tc_table->bits - 1))) >> (timecoder->tc_table->bits - 1); |
638 | - |
639 | - for(n = 0; n < timecoder->tc_table->ntaps; n++) { |
640 | - s = *(timecoder->tc_table->tap + n) - 1; |
641 | - r += (code & (1 << s)) >> s; |
642 | - } |
643 | - |
644 | - return r & 0x1; |
645 | -} |
646 | - |
647 | - |
648 | -/* Setup globally, for a chosen timecode definition */ |
649 | - |
650 | -int timecoder_build_lookup(char *timecode_name, struct timecoder_t *timecoder) { |
651 | - unsigned int n, current; |
652 | - |
653 | +static inline bits_t lfsr(bits_t code, bits_t taps) |
654 | +{ |
655 | + bits_t taken; |
656 | + int xrs; |
657 | + |
658 | + taken = code & taps; |
659 | + xrs = 0; |
660 | + while (taken != 0x0) { |
661 | + xrs += taken & 0x1; |
662 | + taken >>= 1; |
663 | + } |
664 | + |
665 | + return xrs & 0x1; |
666 | +} |
667 | + |
668 | + |
669 | +static inline bits_t fwd(bits_t current, struct timecode_def_t *def) |
670 | +{ |
671 | + bits_t l; |
672 | + |
673 | + /* New bits are added at the MSB; shift right by one */ |
674 | + |
675 | + l = lfsr(current, def->taps | 0x1); |
676 | + return (current >> 1) | (l << (def->bits - 1)); |
677 | +} |
678 | + |
679 | + |
680 | +static inline bits_t rev(bits_t current, struct timecode_def_t *def) |
681 | +{ |
682 | + bits_t l, mask; |
683 | + |
684 | + /* New bits are added at the LSB; shift left one and mask */ |
685 | + |
686 | + mask = (1 << def->bits) - 1; |
687 | + l = lfsr(current, (def->taps >> 1) | (0x1 << (def->bits - 1))); |
688 | + return ((current << 1) & mask) | l; |
689 | +} |
690 | + |
691 | + |
692 | +static struct timecode_def_t* find_definition(const char *name) |
693 | +{ |
694 | struct timecode_def_t *def; |
695 | + |
696 | def = &timecode_def[0]; |
697 | - |
698 | - while(def->name) { |
699 | - if(!strcmp(def->name, timecode_name)) |
700 | - break; |
701 | + while (def->name) { |
702 | + if (!strcmp(def->name, name)) |
703 | + return def; |
704 | def++; |
705 | } |
706 | - |
707 | - if(!def->name) { |
708 | - fprintf(stderr, "Timecode definition '%s' is not known.\n", |
709 | - timecode_name); |
710 | - return -1; |
711 | - } |
712 | - |
713 | - //Copy the lookup table stuff |
714 | - if (timecoder->tc_table == NULL) { |
715 | - timecoder->tc_table = malloc(sizeof(struct timecode_def_t)); |
716 | - } |
717 | - memcpy(timecoder->tc_table, def, sizeof(struct timecode_def_t)); |
718 | - |
719 | - fprintf(stderr, "Allocating %d slots (%zuKb) for %d bit timecode (%s)\n", |
720 | - 2 << timecoder->tc_table->bits, (2 << timecoder->tc_table->bits) * sizeof(unsigned int) / 1024, |
721 | - timecoder->tc_table->bits, timecoder->tc_table->desc); |
722 | - |
723 | - timecoder->tc_table->lookup = malloc((2 << timecoder->tc_table->bits) * sizeof(unsigned int)); |
724 | - if(!timecoder->tc_table->lookup) { |
725 | - perror("malloc"); |
726 | + return NULL; |
727 | +} |
728 | + |
729 | + |
730 | +/* Where necessary, build the lookup table required for this timecode */ |
731 | + |
732 | +static int build_lookup(struct timecode_def_t *def) |
733 | +{ |
734 | + unsigned int n; |
735 | + bits_t current, last; |
736 | + |
737 | + if (def->lookup) |
738 | return 0; |
739 | - } |
740 | - |
741 | - for(n = 0; n < ((unsigned int)2 << timecoder->tc_table->bits); n++) |
742 | - timecoder->tc_table->lookup[n] = -1; |
743 | - |
744 | - current = timecoder->tc_table->seed; |
745 | - |
746 | - for(n = 0; n < timecoder->tc_table->length; n++) { |
747 | - if(timecoder->tc_table->lookup[current] != -1) { |
748 | - fprintf(stderr, "Timecode has wrapped; finishing here.\n"); |
749 | - return -1; |
750 | - } |
751 | - |
752 | - timecoder->tc_table->lookup[current] = n; |
753 | - current = (current >> 1) + (lfsr(current, timecoder) << (timecoder->tc_table->bits - 1)); |
754 | - //printf("n=%d\n", n); |
755 | - } |
756 | + |
757 | + fprintf(stderr, "Building LUT for %d bit %dHz timecode (%s)\n", |
758 | + def->bits, def->resolution, def->desc); |
759 | + |
760 | + if (lut_init(&def->lut, def->length) == -1) |
761 | + return -1; |
762 | + |
763 | + current = def->seed; |
764 | + |
765 | + for (n = 0; n < def->length; n++) { |
766 | + /* timecode must not wrap */ |
767 | + assert(lut_lookup(&def->lut, current) == (unsigned)-1); |
768 | + lut_push(&def->lut, current); |
769 | + last = current; |
770 | + current = fwd(current, def); |
771 | + assert(rev(current, def) == last); |
772 | + } |
773 | + |
774 | + def->lookup = true; |
775 | |
776 | return 0; |
777 | } |
778 | |
779 | |
780 | -/* Free the timecoder lookup table when it is no longer needed */ |
781 | - |
782 | -void timecoder_free_lookup(struct timecoder_t* timecoder) { |
783 | - if (timecoder->tc_table->lookup) |
784 | - { |
785 | - free(timecoder->tc_table->lookup); |
786 | - timecoder->tc_table->lookup = NULL; |
787 | +/* Free the timecoder lookup tables when they are no longer needed */ |
788 | + |
789 | +void timecoder_free_lookup(void) { |
790 | + struct timecode_def_t *def; |
791 | + |
792 | + def = &timecode_def[0]; |
793 | + while (def->name) { |
794 | + if (def->lookup) |
795 | + lut_clear(&def->lut); |
796 | + def++; |
797 | } |
798 | } |
799 | |
800 | @@ -234,40 +247,43 @@ |
801 | { |
802 | ch->positive = 0; |
803 | ch->zero = 0; |
804 | - ch->crossing_ticker = 0; |
805 | } |
806 | |
807 | |
808 | -/* Initialise a timecode decoder */ |
809 | +/* Initialise a timecode decoder at the given reference speed */ |
810 | |
811 | -void timecoder_init(struct timecoder_t *tc) |
812 | +int timecoder_init(struct timecoder_t *tc, const char *def_name, double speed, |
813 | + unsigned int sample_rate) |
814 | { |
815 | - int c; |
816 | + /* A definition contains a lookup table which can be shared |
817 | + * across multiple timecoders */ |
818 | + |
819 | + tc->def = find_definition(def_name); |
820 | + if (tc->def == NULL) { |
821 | + fprintf(stderr, "Timecode definition '%s' is not known.\n", def_name); |
822 | + return -1; |
823 | + } |
824 | + if (build_lookup(tc->def) == -1) |
825 | + return -1; |
826 | + tc->speed = speed; |
827 | + |
828 | + tc->dt = 1.0 / sample_rate; |
829 | + tc->zero_alpha = tc->dt / (ZERO_RC + tc->dt); |
830 | |
831 | tc->forwards = 1; |
832 | - tc->rate = TIMECODER_RATE; |
833 | - |
834 | - tc->half_peak = 0; |
835 | - tc->wave_peak = 0; |
836 | - tc->ref_level = -1; |
837 | - tc->signal_level = 0; |
838 | - |
839 | - init_channel(&tc->mono); |
840 | - for(c = 0; c < TIMECODER_CHANNELS; c++) |
841 | - init_channel(&tc->channel[c]); |
842 | - |
843 | - tc->crossings = 0; |
844 | - tc->pitch_ticker = 0; |
845 | - |
846 | + init_channel(&tc->primary); |
847 | + init_channel(&tc->secondary); |
848 | + pitch_init(&tc->pitch, tc->dt); |
849 | + |
850 | + tc->ref_level = 32768.0; |
851 | tc->bitstream = 0; |
852 | tc->timecode = 0; |
853 | tc->valid_counter = 0; |
854 | tc->timecode_ticker = 0; |
855 | |
856 | tc->mon = NULL; |
857 | - tc->log_fd = -1; |
858 | - |
859 | - tc->tc_table = NULL; |
860 | + |
861 | + return 0; |
862 | } |
863 | |
864 | |
865 | @@ -283,13 +299,17 @@ |
866 | * display of the incoming audio. Initialise one for the given |
867 | * timecoder */ |
868 | |
869 | -void timecoder_monitor_init(struct timecoder_t *tc, int size, int scale) |
870 | +int timecoder_monitor_init(struct timecoder_t *tc, int size) |
871 | { |
872 | tc->mon_size = size; |
873 | - tc->mon_scale = scale; |
874 | tc->mon = malloc(SQ(tc->mon_size)); |
875 | + if (tc->mon == NULL) { |
876 | + perror("malloc"); |
877 | + return -1; |
878 | + } |
879 | memset(tc->mon, 0, SQ(tc->mon_size)); |
880 | tc->mon_counter = 0; |
881 | + return 0; |
882 | } |
883 | |
884 | |
885 | @@ -297,300 +317,212 @@ |
886 | |
887 | void timecoder_monitor_clear(struct timecoder_t *tc) |
888 | { |
889 | - if(tc->mon) { |
890 | + if (tc->mon) { |
891 | free(tc->mon); |
892 | tc->mon = NULL; |
893 | } |
894 | } |
895 | |
896 | |
897 | -static int detect_zero_crossing(struct timecoder_channel_t *ch, |
898 | - signed short v, int rate) |
899 | +static void detect_zero_crossing(struct timecoder_channel_t *ch, |
900 | + signed int v, float alpha) |
901 | { |
902 | - int swapped; |
903 | - |
904 | ch->crossing_ticker++; |
905 | |
906 | - swapped = 0; |
907 | - if(v >= ch->zero + ZERO_THRESHOLD && !ch->positive) { |
908 | - swapped = 1; |
909 | + ch->swapped = 0; |
910 | + if (v > ch->zero + ZERO_THRESHOLD && !ch->positive) { |
911 | + ch->swapped = 1; |
912 | ch->positive = 1; |
913 | ch->crossing_ticker = 0; |
914 | - } else if(v < ch->zero - ZERO_THRESHOLD && ch->positive) { |
915 | - swapped = 1; |
916 | + } else if (v < ch->zero - ZERO_THRESHOLD && ch->positive) { |
917 | + ch->swapped = 1; |
918 | ch->positive = 0; |
919 | ch->crossing_ticker = 0; |
920 | } |
921 | |
922 | - ch->zero += (v - ch->zero) * ZERO_AVG / rate; |
923 | + ch->zero += alpha * (v - ch->zero); |
924 | +} |
925 | + |
926 | + |
927 | +/* Plot the given sample value in the monitor (scope) */ |
928 | + |
929 | +static void update_monitor(struct timecoder_t *tc, signed int x, signed int y) |
930 | +{ |
931 | + int px, py, p; |
932 | + float v, w; |
933 | + |
934 | + if (!tc->mon) |
935 | + return; |
936 | + |
937 | + /* Decay the pixels already in the montior */ |
938 | + |
939 | + if (++tc->mon_counter % MONITOR_DECAY_EVERY == 0) { |
940 | + for (p = 0; p < SQ(tc->mon_size); p++) { |
941 | + if (tc->mon[p]) |
942 | + tc->mon[p] = tc->mon[p] * 7 / 8; |
943 | + } |
944 | + } |
945 | + |
946 | + v = (float)x / tc->ref_level / 2; |
947 | + w = (float)y / tc->ref_level / 2; |
948 | + |
949 | + px = tc->mon_size / 2 + (v * tc->mon_size / 2); |
950 | + py = tc->mon_size / 2 + (w * tc->mon_size / 2); |
951 | + |
952 | + /* Set the pixel value to white */ |
953 | + |
954 | + if (px > 0 && px < tc->mon_size && py > 0 && py < tc->mon_size) |
955 | + tc->mon[py * tc->mon_size + px] = 0xff; |
956 | +} |
957 | + |
958 | + |
959 | +/* Process a single bitstream reading */ |
960 | + |
961 | +static void process_bitstream(struct timecoder_t *tc, signed int m) |
962 | +{ |
963 | + bits_t b; |
964 | + |
965 | + b = m > tc->ref_level; |
966 | + |
967 | + /* Add it to the bitstream, and work out what we were expecting |
968 | + * (timecode). */ |
969 | + |
970 | + /* tc->bitstream is always in the order it is physically placed on |
971 | + * the vinyl, regardless of the direction. */ |
972 | + |
973 | + if (tc->forwards) { |
974 | + tc->timecode = fwd(tc->timecode, tc->def); |
975 | + tc->bitstream = (tc->bitstream >> 1) |
976 | + + (b << (tc->def->bits - 1)); |
977 | + |
978 | + } else { |
979 | + bits_t mask; |
980 | + |
981 | + mask = ((1 << tc->def->bits) - 1); |
982 | + tc->timecode = rev(tc->timecode, tc->def); |
983 | + tc->bitstream = ((tc->bitstream << 1) & mask) + b; |
984 | + } |
985 | + |
986 | + if (tc->timecode == tc->bitstream) |
987 | + tc->valid_counter++; |
988 | + else { |
989 | + tc->timecode = tc->bitstream; |
990 | + tc->valid_counter = 0; |
991 | + } |
992 | + |
993 | + /* Take note of the last time we read a valid timecode */ |
994 | |
995 | - return swapped; |
996 | + tc->timecode_ticker = 0; |
997 | + |
998 | + /* Adjust the reference level based on this new peak */ |
999 | + |
1000 | + tc->ref_level = (tc->ref_level * (REF_PEAKS_AVG - 1) + m) / REF_PEAKS_AVG; |
1001 | + |
1002 | +#ifdef DEBUG_BITSTREAM |
1003 | + fprintf(stderr, "%+6d zero, %+6d (ref %+6d)\t= %d%c (%5d)\n", |
1004 | + tc->primary.zero, |
1005 | + m, |
1006 | + tc->ref_level, |
1007 | + b, |
1008 | + tc->valid_counter == 0 ? 'x' : ' ', |
1009 | + tc->valid_counter); |
1010 | +#endif |
1011 | +} |
1012 | + |
1013 | + |
1014 | +/* Process a single sample from the incoming audio */ |
1015 | + |
1016 | +static void process_sample(struct timecoder_t *tc, |
1017 | + signed int primary, signed int secondary) |
1018 | +{ |
1019 | + signed int m; /* pcm sample, sum of two shorts */ |
1020 | + |
1021 | + detect_zero_crossing(&tc->primary, primary, tc->zero_alpha); |
1022 | + detect_zero_crossing(&tc->secondary, secondary, tc->zero_alpha); |
1023 | + |
1024 | + m = abs(primary - tc->primary.zero); |
1025 | + |
1026 | + /* If an axis has been crossed, use the direction of the crossing |
1027 | + * to work out the direction of the vinyl */ |
1028 | + |
1029 | + if (tc->primary.swapped) { |
1030 | + tc->forwards = (tc->primary.positive != tc->secondary.positive); |
1031 | + if (tc->def->flags & SWITCH_PHASE) |
1032 | + tc->forwards = !tc->forwards; |
1033 | + } if (tc->secondary.swapped) { |
1034 | + tc->forwards = (tc->primary.positive == tc->secondary.positive); |
1035 | + if (tc->def->flags & SWITCH_PHASE) |
1036 | + tc->forwards = !tc->forwards; |
1037 | + } |
1038 | + |
1039 | + /* If any axis has been crossed, register movement using the pitch |
1040 | + * counters */ |
1041 | + |
1042 | + if (!tc->primary.swapped && !tc->secondary.swapped) |
1043 | + pitch_dt_observation(&tc->pitch, 0.0); |
1044 | + else { |
1045 | + float dx; |
1046 | + |
1047 | + dx = 1.0 / tc->def->resolution / 4; |
1048 | + if (!tc->forwards) |
1049 | + dx = -dx; |
1050 | + pitch_dt_observation(&tc->pitch, dx); |
1051 | + } |
1052 | + |
1053 | + /* If we have crossed the primary channel in the right polarity, |
1054 | + * it's time to read off a timecode 0 or 1 value */ |
1055 | + |
1056 | + if (tc->secondary.swapped && |
1057 | + tc->primary.positive == ((tc->def->flags & SWITCH_POLARITY) == 0)) |
1058 | + { |
1059 | + process_bitstream(tc, m); |
1060 | + } |
1061 | + |
1062 | + tc->timecode_ticker++; |
1063 | } |
1064 | |
1065 | |
1066 | /* Submit and decode a block of PCM audio data to the timecoder */ |
1067 | |
1068 | -int timecoder_submit(struct timecoder_t *tc, const signed short *pcm, int samples) |
1069 | +void timecoder_submit(struct timecoder_t *tc, const signed short *pcm, size_t npcm) |
1070 | { |
1071 | - int b, l, /* bitstream and timecode bits */ |
1072 | - s, c, |
1073 | - x, y, p, /* monitor coordinates */ |
1074 | - v, |
1075 | - offset, |
1076 | - swapped, |
1077 | - monitor_centre; |
1078 | - signed short w; /* pcm sample values */ |
1079 | - unsigned int mask; |
1080 | - |
1081 | - b = 0; |
1082 | - l = 0; |
1083 | - |
1084 | - mask = ((1 << tc->tc_table->bits) - 1); |
1085 | - monitor_centre = tc->mon_size / 2; |
1086 | - |
1087 | - offset = 0; |
1088 | - |
1089 | - for(s = 0; s < samples; s++) { |
1090 | - |
1091 | - for(c = 0; c < TIMECODER_CHANNELS; c++) |
1092 | - detect_zero_crossing(&tc->channel[c], pcm[offset + c], tc->rate); |
1093 | - |
1094 | - /* Read from the mono channel */ |
1095 | - |
1096 | - v = pcm[offset] + pcm[offset + 1]; |
1097 | - swapped = detect_zero_crossing(&tc->mono, v, tc->rate); |
1098 | - |
1099 | - /* If a sign change in the (zero corrected) audio has |
1100 | - * happened, log the peak information */ |
1101 | - |
1102 | - if(swapped) { |
1103 | - |
1104 | - /* Work out whether half way through a cycle we are |
1105 | - * looking for the wave to be positive or negative */ |
1106 | - |
1107 | - if(tc->mono.positive == (tc->tc_table->polarity ^ tc->forwards)) { |
1108 | - |
1109 | - /* Entering the second half of a wave cycle */ |
1110 | - |
1111 | - tc->half_peak = tc->wave_peak; |
1112 | - |
1113 | - } else { |
1114 | - |
1115 | - /* Completed a full wave cycle, so time to analyse the |
1116 | - * level and work out whether it's a 1 or 0 */ |
1117 | - |
1118 | - b = tc->wave_peak + tc->half_peak > tc->ref_level; |
1119 | - |
1120 | - /* Log binary timecode */ |
1121 | - |
1122 | - if(tc->log_fd != -1) |
1123 | - write(tc->log_fd, b ? "1" : "0", 1); |
1124 | - |
1125 | - /* Add it to the bitstream, and work out what we were |
1126 | - * expecting (timecode). */ |
1127 | - |
1128 | - /* tc->bitstream is always in the order it is |
1129 | - * physically placed on the vinyl, regardless of the |
1130 | - * direction. */ |
1131 | - |
1132 | - if(tc->forwards) { |
1133 | - l = lfsr(tc->timecode, tc); |
1134 | - |
1135 | - tc->bitstream = (tc->bitstream >> 1) |
1136 | - + (b << (tc->tc_table->bits - 1)); |
1137 | - |
1138 | - tc->timecode = (tc->timecode >> 1) |
1139 | - + (l << (tc->tc_table->bits - 1)); |
1140 | - |
1141 | - } else { |
1142 | - l = lfsr_rev(tc->timecode, tc); |
1143 | - |
1144 | - tc->bitstream = ((tc->bitstream << 1) & mask) + b; |
1145 | - tc->timecode = ((tc->timecode << 1) & mask) + l; |
1146 | - } |
1147 | - |
1148 | - if(b == l) { |
1149 | - tc->valid_counter++; |
1150 | - } else { |
1151 | - tc->timecode = tc->bitstream; |
1152 | - tc->valid_counter = 0; |
1153 | - } |
1154 | - |
1155 | - /* Take note of the last time we read a valid timecode */ |
1156 | - |
1157 | - tc->timecode_ticker = 0; |
1158 | - |
1159 | - /* Adjust the reference level based on the peaks seen |
1160 | - * in this cycle */ |
1161 | - |
1162 | - if(tc->ref_level == -1) |
1163 | - tc->ref_level = tc->half_peak + tc->wave_peak; |
1164 | - else { |
1165 | - tc->ref_level = (tc->ref_level * (REF_PEAKS_AVG - 1) |
1166 | - + tc->half_peak + tc->wave_peak) |
1167 | - / REF_PEAKS_AVG; |
1168 | - } |
1169 | - |
1170 | - } |
1171 | - |
1172 | - /* Calculate the immediate direction from phase difference, |
1173 | - * based on the last channel to cross zero */ |
1174 | - |
1175 | - if(tc->channel[0].crossing_ticker > tc->channel[1].crossing_ticker) |
1176 | - tc->forwards = 1; |
1177 | - else |
1178 | - tc->forwards = 0; |
1179 | - |
1180 | - if(tc->forwards) |
1181 | - tc->crossings++; |
1182 | - else |
1183 | - tc->crossings--; |
1184 | - |
1185 | - tc->pitch_ticker += tc->crossing_ticker; |
1186 | - tc->crossing_ticker = 0; |
1187 | - tc->wave_peak = 0; |
1188 | - |
1189 | - } /* swapped */ |
1190 | - |
1191 | - tc->crossing_ticker++; |
1192 | - tc->timecode_ticker++; |
1193 | - |
1194 | - /* Find the zero-normalised sample of the peak value from |
1195 | - * the input */ |
1196 | - |
1197 | - w = abs(v - tc->mono.zero); |
1198 | - if(w > tc->wave_peak) |
1199 | - tc->wave_peak = w; |
1200 | - |
1201 | - /* Take a rolling average of zero and signal level */ |
1202 | - |
1203 | - tc->signal_level += (w - tc->signal_level) * SIGNAL_AVG / tc->rate; |
1204 | - |
1205 | - /* Update the monitor to add the incoming sample */ |
1206 | - |
1207 | - if(tc->mon) { |
1208 | - |
1209 | - /* Decay the pixels already in the montior */ |
1210 | - |
1211 | - if(++tc->mon_counter % MONITOR_DECAY_EVERY == 0) { |
1212 | - for(p = 0; p < SQ(tc->mon_size); p++) { |
1213 | - if(tc->mon[p]) |
1214 | - tc->mon[p] = tc->mon[p] * 7 / 8; |
1215 | - } |
1216 | - } |
1217 | - |
1218 | - v = pcm[offset]; /* first channel */ |
1219 | - w = pcm[offset + 1]; /* second channel */ |
1220 | - |
1221 | - x = monitor_centre + (v * tc->mon_size * tc->mon_scale / 32768); |
1222 | - y = monitor_centre + (w * tc->mon_size * tc->mon_scale / 32768); |
1223 | - |
1224 | - /* Set the pixel value to white */ |
1225 | - |
1226 | - if(x > 0 && x < tc->mon_size && y > 0 && y < tc->mon_size) |
1227 | - tc->mon[y * tc->mon_size + x] = 0xff; |
1228 | + while (npcm--) { |
1229 | + signed int primary, secondary; |
1230 | + |
1231 | + if (tc->def->flags & SWITCH_PRIMARY) { |
1232 | + primary = pcm[0]; |
1233 | + secondary = pcm[1]; |
1234 | + } else { |
1235 | + primary = pcm[1]; |
1236 | + secondary = pcm[0]; |
1237 | } |
1238 | - |
1239 | - offset += TIMECODER_CHANNELS; |
1240 | - |
1241 | - } /* for each sample */ |
1242 | - |
1243 | - /* Print debugging information */ |
1244 | - |
1245 | -#if 0 |
1246 | - fprintf(stderr, "%+6d +/%4d -/%4d (%4d,%4d)\t= %d (%d) %c %d" |
1247 | - "\t[crossings: %d %d]", |
1248 | - tc->mono.zero, |
1249 | - tc->half_peak, |
1250 | - tc->wave_peak, |
1251 | - tc->ref_level >> 1, |
1252 | - tc->signal_level, |
1253 | - b, l, b == l ? ' ' : 'x', |
1254 | - tc->valid_counter, |
1255 | - tc->crossings, |
1256 | - tc->pitch_ticker); |
1257 | - |
1258 | - if(tc->pitch_ticker) |
1259 | - fprintf(stderr, " = %d", tc->rate * tc->crossings / tc->pitch_ticker); |
1260 | - |
1261 | - fputc('\n', stderr); |
1262 | -#endif |
1263 | - |
1264 | - return 0; |
1265 | -} |
1266 | - |
1267 | - |
1268 | -/* Return the timecode pitch, based on cycles of the sine wave. This |
1269 | - * function can only be called by one context, at it resets the state |
1270 | - * of the counter in the timecoder. */ |
1271 | - |
1272 | -int timecoder_get_pitch(struct timecoder_t *tc, float *pitch) |
1273 | -{ |
1274 | - /* Let the caller know if there's no data to gather pitch from */ |
1275 | - |
1276 | - if(tc->crossings == 0) |
1277 | - return -1; |
1278 | - |
1279 | - /* Value of tc->crossings may be negative in reverse */ |
1280 | - |
1281 | - *pitch = tc->rate * (float)tc->crossings / tc->pitch_ticker |
1282 | - / (tc->tc_table->resolution * 2); |
1283 | - |
1284 | - tc->crossings = 0; |
1285 | - tc->pitch_ticker = 0; |
1286 | - |
1287 | - return 0; |
1288 | + |
1289 | + process_sample(tc, primary, secondary); |
1290 | + |
1291 | + update_monitor(tc, pcm[0], pcm[1]); |
1292 | + pcm += TIMECODER_CHANNELS; |
1293 | + } |
1294 | } |
1295 | |
1296 | |
1297 | /* Return the known position in the timecode, or -1 if not known. If |
1298 | * two few bits have been error-checked, then this also counts as |
1299 | - * invalid. If 'when' is given, return the time, in input samples since |
1300 | - * this value was read. */ |
1301 | + * invalid. If 'when' is given, return the time, in seconds since this |
1302 | + * value was read. */ |
1303 | |
1304 | -signed int timecoder_get_position(struct timecoder_t *tc, int *when) |
1305 | +signed int timecoder_get_position(struct timecoder_t *tc, float *when) |
1306 | { |
1307 | signed int r; |
1308 | |
1309 | - if(tc->valid_counter > VALID_BITS) { |
1310 | - r = tc->tc_table->lookup[tc->bitstream]; |
1311 | + if (tc->valid_counter > VALID_BITS) { |
1312 | + r = lut_lookup(&tc->def->lut, tc->bitstream) / tc->speed; |
1313 | |
1314 | - if(r >= 0) { |
1315 | - if(when) |
1316 | - *when = tc->timecode_ticker; |
1317 | + if (r >= 0) { |
1318 | + if (when) |
1319 | + *when = tc->timecode_ticker * tc->dt; |
1320 | return r; |
1321 | } |
1322 | } |
1323 | |
1324 | return -1; |
1325 | } |
1326 | - |
1327 | - |
1328 | -/* Return non-zero if there is any timecode signal available */ |
1329 | - |
1330 | -int timecoder_get_alive(struct timecoder_t *tc) |
1331 | -{ |
1332 | - if(tc->signal_level < SIGNAL_THRESHOLD) |
1333 | - return 0; |
1334 | - |
1335 | - return 1; |
1336 | -} |
1337 | - |
1338 | - |
1339 | -/* Return the last 'safe' timecode value on the record. Beyond this |
1340 | - * value, we probably want to ignore the timecode values, as we will |
1341 | - * hit the label of the record. */ |
1342 | - |
1343 | -unsigned int timecoder_get_safe(struct timecoder_t *tc) |
1344 | -{ |
1345 | - return tc->tc_table->safe; |
1346 | -} |
1347 | - |
1348 | - |
1349 | -/* Return the resolution of the timecode. This is the number of bits |
1350 | - * per second, which corresponds to the frequency of the sine wave */ |
1351 | - |
1352 | -int timecoder_get_resolution(struct timecoder_t *tc) |
1353 | -{ |
1354 | - return tc->tc_table->resolution; |
1355 | -} |
1356 | |
1357 | === modified file 'mixxx/lib/xwax/timecoder.h' |
1358 | --- mixxx/lib/xwax/timecoder.h 2011-04-08 06:04:27 +0000 |
1359 | +++ mixxx/lib/xwax/timecoder.h 2011-04-16 23:24:33 +0000 |
1360 | @@ -1,5 +1,5 @@ |
1361 | /* |
1362 | - * Copyright (C) 2008 Mark Hills <mark@pogo.org.uk> |
1363 | + * Copyright (C) 2010 Mark Hills <mark@pogo.org.uk> |
1364 | * |
1365 | * This program is free software; you can redistribute it and/or |
1366 | * modify it under the terms of the GNU General Public License |
1367 | @@ -20,80 +20,118 @@ |
1368 | #ifndef TIMECODER_H |
1369 | #define TIMECODER_H |
1370 | |
1371 | +#ifndef _MSC_VER |
1372 | +#include <stdbool.h> |
1373 | +#endif |
1374 | + |
1375 | +/* #include "device.h" */ |
1376 | +#include "lut.h" |
1377 | +#include "pitch.h" |
1378 | + |
1379 | #define TIMECODER_CHANNELS 2 |
1380 | -#define TIMECODER_RATE 44100 //Default rate - Albert |
1381 | - |
1382 | -#define MAX_BITS 32 /* bits in an int */ |
1383 | + |
1384 | + |
1385 | +typedef unsigned int bits_t; |
1386 | + |
1387 | |
1388 | struct timecode_def_t { |
1389 | char *name, *desc; |
1390 | int bits, /* number of bits in string */ |
1391 | resolution, /* wave cycles per second */ |
1392 | - tap[MAX_BITS], ntaps, /* LFSR taps */ |
1393 | - polarity; /* cycle begins POLARITY_POSITIVE or POLARITY_NEGATIVE */ |
1394 | - unsigned int seed, /* LFSR value at timecode zero */ |
1395 | - length, /* in cycles */ |
1396 | + flags; |
1397 | + bits_t seed, /* LFSR value at timecode zero */ |
1398 | + taps; /* central LFSR taps, excluding end taps */ |
1399 | + unsigned int length, /* in cycles */ |
1400 | safe; /* last 'safe' timecode number (for auto disconnect) */ |
1401 | - signed int *lookup; /* pointer to built lookup table */ |
1402 | + bool lookup; /* true if lut has been generated */ |
1403 | + struct lut_t lut; |
1404 | }; |
1405 | |
1406 | + |
1407 | struct timecoder_channel_t { |
1408 | - int positive; /* wave is in positive part of cycle */ |
1409 | + int positive, /* wave is in positive part of cycle */ |
1410 | + swapped; /* wave recently swapped polarity */ |
1411 | signed int zero; |
1412 | - int crossing_ticker; /* samples since we last crossed zero */ |
1413 | + unsigned int crossing_ticker; /* samples since we last crossed zero */ |
1414 | }; |
1415 | |
1416 | |
1417 | struct timecoder_t { |
1418 | - int forwards, rate; |
1419 | - |
1420 | - /* Signal levels */ |
1421 | - |
1422 | - signed int signal_level, half_peak, wave_peak, ref_level; |
1423 | - struct timecoder_channel_t mono, channel[TIMECODER_CHANNELS]; |
1424 | + struct timecode_def_t *def; |
1425 | + double speed; |
1426 | + |
1427 | + /* Precomputed values */ |
1428 | + |
1429 | + float dt, zero_alpha; |
1430 | |
1431 | /* Pitch information */ |
1432 | |
1433 | - int crossings, /* number of zero crossings */ |
1434 | - pitch_ticker, /* number of samples from which crossings counted */ |
1435 | - crossing_ticker; /* stored for incrementing pitch_ticker */ |
1436 | + int forwards; |
1437 | + struct timecoder_channel_t primary, secondary; |
1438 | + struct pitch_t pitch; |
1439 | |
1440 | /* Numerical timecode */ |
1441 | |
1442 | - unsigned int bitstream, /* actual bits from the record */ |
1443 | + signed int ref_level; |
1444 | + bits_t bitstream, /* actual bits from the record */ |
1445 | timecode; /* corrected timecode */ |
1446 | - int valid_counter, /* number of successful error checks */ |
1447 | + unsigned int valid_counter, /* number of successful error checks */ |
1448 | timecode_ticker; /* samples since valid timecode was read */ |
1449 | |
1450 | /* Feedback */ |
1451 | |
1452 | unsigned char *mon; /* x-y array */ |
1453 | - int mon_size, mon_counter, mon_scale, |
1454 | - log_fd; /* optional file descriptor to log to, or -1 for none */ |
1455 | - |
1456 | - struct timecode_def_t *tc_table; |
1457 | + int mon_size, mon_counter; |
1458 | }; |
1459 | |
1460 | |
1461 | -/* Building the lookup table is global. Need a good way to share |
1462 | - * lookup tables soon, so we can use a different timecode on |
1463 | - * each timecoder, and switch between them. */ |
1464 | - |
1465 | -int timecoder_build_lookup(char *timecode_name, struct timecoder_t *timecoder); |
1466 | -void timecoder_free_lookup(struct timecoder_t *timecoder); |
1467 | - |
1468 | -void timecoder_init(struct timecoder_t *tc); |
1469 | +void timecoder_free_lookup(void); |
1470 | + |
1471 | +int timecoder_init(struct timecoder_t *tc, const char *def_name, double speed, |
1472 | + unsigned int sample_rate); |
1473 | void timecoder_clear(struct timecoder_t *tc); |
1474 | |
1475 | -void timecoder_monitor_init(struct timecoder_t *tc, int size, int scale); |
1476 | +int timecoder_monitor_init(struct timecoder_t *tc, int size); |
1477 | void timecoder_monitor_clear(struct timecoder_t *tc); |
1478 | |
1479 | -int timecoder_submit(struct timecoder_t *tc, const signed short *aud, int samples); |
1480 | - |
1481 | -int timecoder_get_pitch(struct timecoder_t *tc, float *pitch); |
1482 | -signed int timecoder_get_position(struct timecoder_t *tc, int *when); |
1483 | -int timecoder_get_alive(struct timecoder_t *tc); |
1484 | -unsigned int timecoder_get_safe(struct timecoder_t *tc); |
1485 | -int timecoder_get_resolution(struct timecoder_t *tc); |
1486 | +void timecoder_submit(struct timecoder_t *tc, const signed short *pcm, size_t npcm); |
1487 | + |
1488 | +signed int timecoder_get_position(struct timecoder_t *tc, float *when); |
1489 | + |
1490 | + |
1491 | +/* Return the pitch relative to reference playback speed */ |
1492 | + |
1493 | +static inline float timecoder_get_pitch(struct timecoder_t *tc) |
1494 | +{ |
1495 | + return pitch_current(&tc->pitch) / tc->speed; |
1496 | +} |
1497 | + |
1498 | + |
1499 | +/* The last 'safe' timecode value on the record. Beyond this value, we |
1500 | + * probably want to ignore the timecode values, as we will hit the |
1501 | + * label of the record. */ |
1502 | + |
1503 | +static inline unsigned int timecoder_get_safe(struct timecoder_t *tc) |
1504 | +{ |
1505 | + return tc->def->safe; |
1506 | +} |
1507 | + |
1508 | + |
1509 | +/* The resolution of the timecode. This is the number of bits per |
1510 | + * second at reference playback speed */ |
1511 | + |
1512 | +static inline double timecoder_get_resolution(struct timecoder_t *tc) |
1513 | +{ |
1514 | + return tc->def->resolution * tc->speed; |
1515 | +} |
1516 | + |
1517 | + |
1518 | +/* The number of revolutions per second of the timecode vinyl, |
1519 | + * used only for visual display */ |
1520 | + |
1521 | +static inline double timecoder_revs_per_sec(struct timecoder_t *tc) |
1522 | +{ |
1523 | + return (33.0 + 1.0 / 3) * tc->speed / 60; |
1524 | +} |
1525 | |
1526 | #endif |
1527 | |
1528 | === renamed file 'mixxx/lib/xwax/timecoder_win32.c' => 'mixxx/lib/xwax/timecoder_win32.cpp' |
1529 | --- mixxx/lib/xwax/timecoder_win32.c 2009-08-06 06:36:18 +0000 |
1530 | +++ mixxx/lib/xwax/timecoder_win32.cpp 2011-04-16 23:24:33 +0000 |
1531 | @@ -1,5 +1,5 @@ |
1532 | /* |
1533 | - * Copyright (C) 2008 Mark Hills <mark@pogo.org.uk> |
1534 | + * Copyright (C) 2010 Mark Hills <mark@pogo.org.uk> |
1535 | * |
1536 | * This program is free software; you can redistribute it and/or |
1537 | * modify it under the terms of the GNU General Public License |
1538 | @@ -17,18 +17,19 @@ |
1539 | * |
1540 | */ |
1541 | |
1542 | +#include <assert.h> |
1543 | #include <stdio.h> |
1544 | #include <stdlib.h> |
1545 | #include <string.h> |
1546 | -//#include <unistd.h> |
1547 | +#ifndef _MSC_VER |
1548 | +#include <unistd.h> |
1549 | +#endif |
1550 | |
1551 | #include "timecoder.h" |
1552 | |
1553 | #define ZERO_THRESHOLD 128 |
1554 | -#define SIGNAL_THRESHOLD 256 |
1555 | |
1556 | -#define ZERO_AVG 1024 |
1557 | -#define SIGNAL_AVG 256 |
1558 | +#define ZERO_RC 0.001 /* time constant for zero/rumble filter */ |
1559 | |
1560 | #define REF_PEAKS_AVG 48 /* in wave cycles */ |
1561 | |
1562 | @@ -45,75 +46,95 @@ |
1563 | |
1564 | /* Timecode definitions */ |
1565 | |
1566 | - |
1567 | -#define POLARITY_NEGATIVE 0 |
1568 | -#define POLARITY_POSITIVE 1 |
1569 | - |
1570 | -struct timecode_def_t timecode_def[] = { |
1571 | +#define SWITCH_PHASE 0x1 /* tone phase difference of 270 (not 90) degrees */ |
1572 | +#define SWITCH_PRIMARY 0x2 /* use left channel (not right) as primary */ |
1573 | +#define SWITCH_POLARITY 0x4 /* read bit values in negative (not positive) */ |
1574 | + |
1575 | + |
1576 | +static struct timecode_def_t timecode_def[] = { |
1577 | { |
1578 | "serato_2a", |
1579 | "Serato 2nd Ed., side A", |
1580 | 20, |
1581 | - 1000, |
1582 | - {2, 5, 6, 7, 8, 13, 14, 16, 17}, |
1583 | - 9, |
1584 | - POLARITY_POSITIVE, |
1585 | + 1000, |
1586 | + 0, |
1587 | 0x59017, |
1588 | + 0x361e4, |
1589 | 712000, |
1590 | - 707000, |
1591 | - NULL |
1592 | + 625000, |
1593 | + false |
1594 | }, |
1595 | { |
1596 | "serato_2b", |
1597 | "Serato 2nd Ed., side B", |
1598 | 20, |
1599 | 1000, |
1600 | - {3, 4, 6, 7, 12, 13, 14, 15, 18}, /* reverse of side A */ |
1601 | - 9, |
1602 | - POLARITY_POSITIVE, |
1603 | + 0, |
1604 | 0x8f3c6, |
1605 | + 0x4f0d8, /* reverse of side A */ |
1606 | 922000, |
1607 | - 917000, |
1608 | - NULL |
1609 | + 905000, |
1610 | + false |
1611 | }, |
1612 | { |
1613 | "serato_cd", |
1614 | "Serato CD", |
1615 | 20, |
1616 | 1000, |
1617 | - {2, 4, 6, 8, 10, 11, 14, 16, 17}, |
1618 | - 9, |
1619 | - POLARITY_POSITIVE, |
1620 | + 0, |
1621 | 0xd8b40, |
1622 | - 910000, |
1623 | - 900000, |
1624 | - NULL |
1625 | + 0x34d54, |
1626 | + 950000, |
1627 | + 940000, |
1628 | + false |
1629 | }, |
1630 | { |
1631 | "traktor_a", |
1632 | "Traktor Scratch, side A", |
1633 | 23, |
1634 | 2000, |
1635 | - {6, 12, 18}, |
1636 | - 3, |
1637 | - POLARITY_NEGATIVE, |
1638 | + SWITCH_PRIMARY | SWITCH_POLARITY | SWITCH_PHASE, |
1639 | 0x134503, |
1640 | + 0x041040, |
1641 | 1500000, |
1642 | - 1480000, |
1643 | - NULL |
1644 | + 1463000, |
1645 | + false |
1646 | }, |
1647 | { |
1648 | "traktor_b", |
1649 | "Traktor Scratch, side B", |
1650 | 23, |
1651 | - 2000, |
1652 | - {6, 12, 18}, |
1653 | - 3, |
1654 | - POLARITY_NEGATIVE, |
1655 | + 2000, |
1656 | + SWITCH_PRIMARY | SWITCH_POLARITY | SWITCH_PHASE, |
1657 | 0x32066c, |
1658 | + 0x041040, /* same as side A */ |
1659 | 2110000, |
1660 | - 2090000, |
1661 | - NULL |
1662 | + 2068000, |
1663 | + false |
1664 | + }, |
1665 | + { |
1666 | + "mixvibes_v2", |
1667 | + "MixVibes V2", |
1668 | + 20, |
1669 | + 1300, |
1670 | + SWITCH_PHASE, |
1671 | + 0x22c90, |
1672 | + 0x00008, |
1673 | + 950000, |
1674 | + 923000, |
1675 | + false |
1676 | + }, |
1677 | + { |
1678 | + "mixvibes_7inch", |
1679 | + "MixVibes 7\"", |
1680 | + 20, |
1681 | + 1300, |
1682 | + SWITCH_PHASE, |
1683 | + 0x22c90, |
1684 | + 0x00008, |
1685 | + 312000, |
1686 | + 310000, |
1687 | + false |
1688 | }, |
1689 | { |
1690 | NULL |
1691 | @@ -121,110 +142,105 @@ |
1692 | }; |
1693 | |
1694 | |
1695 | -//struct timecode_def_t *def; |
1696 | - |
1697 | - |
1698 | /* Linear Feeback Shift Register in the forward direction. New values |
1699 | * are generated at the least-significant bit. */ |
1700 | |
1701 | -static int lfsr(unsigned int code, struct timecoder_t *timecoder) |
1702 | -{ |
1703 | - unsigned int r; |
1704 | - char s, n; |
1705 | - |
1706 | - r = code & 1; |
1707 | - |
1708 | - for(n = 0; n < timecoder->tc_table->ntaps; n++) { |
1709 | - s = *(timecoder->tc_table->tap + n); |
1710 | - r += (code & (1 << s)) >> s; |
1711 | - } |
1712 | - |
1713 | - return r & 0x1; |
1714 | -} |
1715 | - |
1716 | - |
1717 | -/* Linear Feeback Shift Register in the reverse direction. New values |
1718 | - * are generated at the most-significant bit. */ |
1719 | - |
1720 | -static /* inline */ int lfsr_rev(unsigned int code, struct timecoder_t *timecoder) // inline causes compile failure on MSVC++ 2005 EE |
1721 | -{ |
1722 | - unsigned int r; |
1723 | - char s, n; |
1724 | - |
1725 | - r = (code & (1 << (timecoder->tc_table->bits - 1))) >> (timecoder->tc_table->bits - 1); |
1726 | - |
1727 | - for(n = 0; n < timecoder->tc_table->ntaps; n++) { |
1728 | - s = *(timecoder->tc_table->tap + n) - 1; |
1729 | - r += (code & (1 << s)) >> s; |
1730 | - } |
1731 | - |
1732 | - return r & 0x1; |
1733 | -} |
1734 | - |
1735 | - |
1736 | -/* Setup globally, for a chosen timecode definition */ |
1737 | - |
1738 | -int timecoder_build_lookup(char *timecode_name, struct timecoder_t *timecoder) { |
1739 | - unsigned int n, current; |
1740 | - |
1741 | +static inline bits_t lfsr(bits_t code, bits_t taps) |
1742 | +{ |
1743 | + bits_t taken; |
1744 | + int xrs; |
1745 | + |
1746 | + taken = code & taps; |
1747 | + xrs = 0; |
1748 | + while (taken != 0x0) { |
1749 | + xrs += taken & 0x1; |
1750 | + taken >>= 1; |
1751 | + } |
1752 | + |
1753 | + return xrs & 0x1; |
1754 | +} |
1755 | + |
1756 | + |
1757 | +static inline bits_t fwd(bits_t current, struct timecode_def_t *def) |
1758 | +{ |
1759 | + bits_t l; |
1760 | + |
1761 | + /* New bits are added at the MSB; shift right by one */ |
1762 | + |
1763 | + l = lfsr(current, def->taps | 0x1); |
1764 | + return (current >> 1) | (l << (def->bits - 1)); |
1765 | +} |
1766 | + |
1767 | + |
1768 | +static inline bits_t rev(bits_t current, struct timecode_def_t *def) |
1769 | +{ |
1770 | + bits_t l, mask; |
1771 | + |
1772 | + /* New bits are added at the LSB; shift left one and mask */ |
1773 | + |
1774 | + mask = (1 << def->bits) - 1; |
1775 | + l = lfsr(current, (def->taps >> 1) | (0x1 << (def->bits - 1))); |
1776 | + return ((current << 1) & mask) | l; |
1777 | +} |
1778 | + |
1779 | + |
1780 | +static struct timecode_def_t* find_definition(const char *name) |
1781 | +{ |
1782 | struct timecode_def_t *def; |
1783 | + |
1784 | def = &timecode_def[0]; |
1785 | - |
1786 | - while(def->name) { |
1787 | - if(!strcmp(def->name, timecode_name)) |
1788 | - break; |
1789 | + while (def->name) { |
1790 | + if (!strcmp(def->name, name)) |
1791 | + return def; |
1792 | def++; |
1793 | } |
1794 | - |
1795 | - if(!def->name) { |
1796 | - fprintf(stderr, "Timecode definition '%s' is not known.\n", |
1797 | - timecode_name); |
1798 | - return -1; |
1799 | - } |
1800 | - |
1801 | - //Copy the lookup table stuff |
1802 | - if (timecoder->tc_table == NULL) { |
1803 | - timecoder->tc_table = malloc(sizeof(struct timecode_def_t)); |
1804 | - } |
1805 | - memcpy(timecoder->tc_table, def, sizeof(struct timecode_def_t)); |
1806 | - |
1807 | - //fprintf(stderr, "Allocating %d slots (%zuKb) for %d bit timecode (%s)\n", |
1808 | - // 2 << timecoder->tc_table->bits, (2 << timecoder->tc_table->bits) * sizeof(unsigned int) / 1024, |
1809 | - // timecoder->tc_table->bits, timecoder->tc_table->desc); |
1810 | - |
1811 | - timecoder->tc_table->lookup = malloc((2 << timecoder->tc_table->bits) * sizeof(unsigned int)); |
1812 | - if(!timecoder->tc_table->lookup) { |
1813 | - perror("malloc"); |
1814 | + return NULL; |
1815 | +} |
1816 | + |
1817 | + |
1818 | +/* Where necessary, build the lookup table required for this timecode */ |
1819 | + |
1820 | +static int build_lookup(struct timecode_def_t *def) |
1821 | +{ |
1822 | + unsigned int n; |
1823 | + bits_t current, last; |
1824 | + |
1825 | + if (def->lookup) |
1826 | return 0; |
1827 | - } |
1828 | - |
1829 | - for(n = 0; n < ((unsigned int)2 << timecoder->tc_table->bits); n++) |
1830 | - timecoder->tc_table->lookup[n] = -1; |
1831 | - |
1832 | - current = timecoder->tc_table->seed; |
1833 | - |
1834 | - for(n = 0; n < timecoder->tc_table->length; n++) { |
1835 | - if(timecoder->tc_table->lookup[current] != -1) { |
1836 | - //fprintf(stderr, "Timecode has wrapped; finishing here.\n"); |
1837 | - return -1; |
1838 | - } |
1839 | - |
1840 | - timecoder->tc_table->lookup[current] = n; |
1841 | - current = (current >> 1) + (lfsr(current, timecoder) << (timecoder->tc_table->bits - 1)); |
1842 | - //printf("n=%d\n", n); |
1843 | - } |
1844 | + |
1845 | + fprintf(stderr, "Building LUT for %d bit %dHz timecode (%s)\n", |
1846 | + def->bits, def->resolution, def->desc); |
1847 | + |
1848 | + if (lut_init(&def->lut, def->length) == -1) |
1849 | + return -1; |
1850 | + |
1851 | + current = def->seed; |
1852 | + |
1853 | + for (n = 0; n < def->length; n++) { |
1854 | + /* timecode must not wrap */ |
1855 | + assert(lut_lookup(&def->lut, current) == (unsigned)-1); |
1856 | + lut_push(&def->lut, current); |
1857 | + last = current; |
1858 | + current = fwd(current, def); |
1859 | + assert(rev(current, def) == last); |
1860 | + } |
1861 | + |
1862 | + def->lookup = true; |
1863 | |
1864 | return 0; |
1865 | } |
1866 | |
1867 | |
1868 | -/* Free the timecoder lookup table when it is no longer needed */ |
1869 | - |
1870 | -void timecoder_free_lookup(struct timecoder_t* timecoder) { |
1871 | - if (timecoder->tc_table->lookup) |
1872 | - { |
1873 | - free(timecoder->tc_table->lookup); |
1874 | - timecoder->tc_table->lookup = NULL; |
1875 | +/* Free the timecoder lookup tables when they are no longer needed */ |
1876 | + |
1877 | +void timecoder_free_lookup(void) { |
1878 | + struct timecode_def_t *def; |
1879 | + |
1880 | + def = &timecode_def[0]; |
1881 | + while (def->name) { |
1882 | + if (def->lookup) |
1883 | + lut_clear(&def->lut); |
1884 | + def++; |
1885 | } |
1886 | } |
1887 | |
1888 | @@ -233,40 +249,43 @@ |
1889 | { |
1890 | ch->positive = 0; |
1891 | ch->zero = 0; |
1892 | - ch->crossing_ticker = 0; |
1893 | } |
1894 | |
1895 | |
1896 | -/* Initialise a timecode decoder */ |
1897 | +/* Initialise a timecode decoder at the given reference speed */ |
1898 | |
1899 | -void timecoder_init(struct timecoder_t *tc) |
1900 | +int timecoder_init(struct timecoder_t *tc, const char *def_name, double speed, |
1901 | + unsigned int sample_rate) |
1902 | { |
1903 | - int c; |
1904 | + /* A definition contains a lookup table which can be shared |
1905 | + * across multiple timecoders */ |
1906 | + |
1907 | + tc->def = find_definition(def_name); |
1908 | + if (tc->def == NULL) { |
1909 | + fprintf(stderr, "Timecode definition '%s' is not known.\n", def_name); |
1910 | + return -1; |
1911 | + } |
1912 | + if (build_lookup(tc->def) == -1) |
1913 | + return -1; |
1914 | + tc->speed = speed; |
1915 | + |
1916 | + tc->dt = 1.0 / sample_rate; |
1917 | + tc->zero_alpha = tc->dt / (ZERO_RC + tc->dt); |
1918 | |
1919 | tc->forwards = 1; |
1920 | - tc->rate = TIMECODER_RATE; |
1921 | - |
1922 | - tc->half_peak = 0; |
1923 | - tc->wave_peak = 0; |
1924 | - tc->ref_level = -1; |
1925 | - tc->signal_level = 0; |
1926 | - |
1927 | - init_channel(&tc->mono); |
1928 | - for(c = 0; c < TIMECODER_CHANNELS; c++) |
1929 | - init_channel(&tc->channel[c]); |
1930 | - |
1931 | - tc->crossings = 0; |
1932 | - tc->pitch_ticker = 0; |
1933 | - |
1934 | + init_channel(&tc->primary); |
1935 | + init_channel(&tc->secondary); |
1936 | + pitch_init(&tc->pitch, tc->dt); |
1937 | + |
1938 | + tc->ref_level = 32768.0; |
1939 | tc->bitstream = 0; |
1940 | tc->timecode = 0; |
1941 | tc->valid_counter = 0; |
1942 | tc->timecode_ticker = 0; |
1943 | |
1944 | tc->mon = NULL; |
1945 | - tc->log_fd = -1; |
1946 | - |
1947 | - tc->tc_table = NULL; |
1948 | + |
1949 | + return 0; |
1950 | } |
1951 | |
1952 | |
1953 | @@ -282,13 +301,17 @@ |
1954 | * display of the incoming audio. Initialise one for the given |
1955 | * timecoder */ |
1956 | |
1957 | -void timecoder_monitor_init(struct timecoder_t *tc, int size, int scale) |
1958 | +int timecoder_monitor_init(struct timecoder_t *tc, int size) |
1959 | { |
1960 | tc->mon_size = size; |
1961 | - tc->mon_scale = scale; |
1962 | - tc->mon = malloc(SQ(tc->mon_size)); |
1963 | + tc->mon = (unsigned char*)malloc(SQ(tc->mon_size)); |
1964 | + if (tc->mon == NULL) { |
1965 | + perror("malloc"); |
1966 | + return -1; |
1967 | + } |
1968 | memset(tc->mon, 0, SQ(tc->mon_size)); |
1969 | tc->mon_counter = 0; |
1970 | + return 0; |
1971 | } |
1972 | |
1973 | |
1974 | @@ -296,300 +319,212 @@ |
1975 | |
1976 | void timecoder_monitor_clear(struct timecoder_t *tc) |
1977 | { |
1978 | - if(tc->mon) { |
1979 | + if (tc->mon) { |
1980 | free(tc->mon); |
1981 | tc->mon = NULL; |
1982 | } |
1983 | } |
1984 | |
1985 | |
1986 | -static int detect_zero_crossing(struct timecoder_channel_t *ch, |
1987 | - signed short v, int rate) |
1988 | +static void detect_zero_crossing(struct timecoder_channel_t *ch, |
1989 | + signed int v, float alpha) |
1990 | { |
1991 | - int swapped; |
1992 | - |
1993 | ch->crossing_ticker++; |
1994 | |
1995 | - swapped = 0; |
1996 | - if(v >= ch->zero + ZERO_THRESHOLD && !ch->positive) { |
1997 | - swapped = 1; |
1998 | + ch->swapped = 0; |
1999 | + if (v > ch->zero + ZERO_THRESHOLD && !ch->positive) { |
2000 | + ch->swapped = 1; |
2001 | ch->positive = 1; |
2002 | ch->crossing_ticker = 0; |
2003 | - } else if(v < ch->zero - ZERO_THRESHOLD && ch->positive) { |
2004 | - swapped = 1; |
2005 | + } else if (v < ch->zero - ZERO_THRESHOLD && ch->positive) { |
2006 | + ch->swapped = 1; |
2007 | ch->positive = 0; |
2008 | ch->crossing_ticker = 0; |
2009 | } |
2010 | |
2011 | - ch->zero += (v - ch->zero) * ZERO_AVG / rate; |
2012 | + ch->zero += alpha * (v - ch->zero); |
2013 | +} |
2014 | + |
2015 | + |
2016 | +/* Plot the given sample value in the monitor (scope) */ |
2017 | + |
2018 | +static void update_monitor(struct timecoder_t *tc, signed int x, signed int y) |
2019 | +{ |
2020 | + int px, py, p; |
2021 | + float v, w; |
2022 | + |
2023 | + if (!tc->mon) |
2024 | + return; |
2025 | + |
2026 | + /* Decay the pixels already in the montior */ |
2027 | + |
2028 | + if (++tc->mon_counter % MONITOR_DECAY_EVERY == 0) { |
2029 | + for (p = 0; p < SQ(tc->mon_size); p++) { |
2030 | + if (tc->mon[p]) |
2031 | + tc->mon[p] = tc->mon[p] * 7 / 8; |
2032 | + } |
2033 | + } |
2034 | + |
2035 | + v = (float)x / tc->ref_level / 2; |
2036 | + w = (float)y / tc->ref_level / 2; |
2037 | + |
2038 | + px = tc->mon_size / 2 + (v * tc->mon_size / 2); |
2039 | + py = tc->mon_size / 2 + (w * tc->mon_size / 2); |
2040 | + |
2041 | + /* Set the pixel value to white */ |
2042 | + |
2043 | + if (px > 0 && px < tc->mon_size && py > 0 && py < tc->mon_size) |
2044 | + tc->mon[py * tc->mon_size + px] = 0xff; |
2045 | +} |
2046 | + |
2047 | + |
2048 | +/* Process a single bitstream reading */ |
2049 | + |
2050 | +static void process_bitstream(struct timecoder_t *tc, signed int m) |
2051 | +{ |
2052 | + bits_t b; |
2053 | + |
2054 | + b = m > tc->ref_level; |
2055 | + |
2056 | + /* Add it to the bitstream, and work out what we were expecting |
2057 | + * (timecode). */ |
2058 | + |
2059 | + /* tc->bitstream is always in the order it is physically placed on |
2060 | + * the vinyl, regardless of the direction. */ |
2061 | + |
2062 | + if (tc->forwards) { |
2063 | + tc->timecode = fwd(tc->timecode, tc->def); |
2064 | + tc->bitstream = (tc->bitstream >> 1) |
2065 | + + (b << (tc->def->bits - 1)); |
2066 | + |
2067 | + } else { |
2068 | + bits_t mask; |
2069 | + |
2070 | + mask = ((1 << tc->def->bits) - 1); |
2071 | + tc->timecode = rev(tc->timecode, tc->def); |
2072 | + tc->bitstream = ((tc->bitstream << 1) & mask) + b; |
2073 | + } |
2074 | + |
2075 | + if (tc->timecode == tc->bitstream) |
2076 | + tc->valid_counter++; |
2077 | + else { |
2078 | + tc->timecode = tc->bitstream; |
2079 | + tc->valid_counter = 0; |
2080 | + } |
2081 | + |
2082 | + /* Take note of the last time we read a valid timecode */ |
2083 | |
2084 | - return swapped; |
2085 | + tc->timecode_ticker = 0; |
2086 | + |
2087 | + /* Adjust the reference level based on this new peak */ |
2088 | + |
2089 | + tc->ref_level = (tc->ref_level * (REF_PEAKS_AVG - 1) + m) / REF_PEAKS_AVG; |
2090 | + |
2091 | +#ifdef DEBUG_BITSTREAM |
2092 | + fprintf(stderr, "%+6d zero, %+6d (ref %+6d)\t= %d%c (%5d)\n", |
2093 | + tc->primary.zero, |
2094 | + m, |
2095 | + tc->ref_level, |
2096 | + b, |
2097 | + tc->valid_counter == 0 ? 'x' : ' ', |
2098 | + tc->valid_counter); |
2099 | +#endif |
2100 | +} |
2101 | + |
2102 | + |
2103 | +/* Process a single sample from the incoming audio */ |
2104 | + |
2105 | +static void process_sample(struct timecoder_t *tc, |
2106 | + signed int primary, signed int secondary) |
2107 | +{ |
2108 | + signed int m; /* pcm sample, sum of two shorts */ |
2109 | + |
2110 | + detect_zero_crossing(&tc->primary, primary, tc->zero_alpha); |
2111 | + detect_zero_crossing(&tc->secondary, secondary, tc->zero_alpha); |
2112 | + |
2113 | + m = abs(primary - tc->primary.zero); |
2114 | + |
2115 | + /* If an axis has been crossed, use the direction of the crossing |
2116 | + * to work out the direction of the vinyl */ |
2117 | + |
2118 | + if (tc->primary.swapped) { |
2119 | + tc->forwards = (tc->primary.positive != tc->secondary.positive); |
2120 | + if (tc->def->flags & SWITCH_PHASE) |
2121 | + tc->forwards = !tc->forwards; |
2122 | + } if (tc->secondary.swapped) { |
2123 | + tc->forwards = (tc->primary.positive == tc->secondary.positive); |
2124 | + if (tc->def->flags & SWITCH_PHASE) |
2125 | + tc->forwards = !tc->forwards; |
2126 | + } |
2127 | + |
2128 | + /* If any axis has been crossed, register movement using the pitch |
2129 | + * counters */ |
2130 | + |
2131 | + if (!tc->primary.swapped && !tc->secondary.swapped) |
2132 | + pitch_dt_observation(&tc->pitch, 0.0); |
2133 | + else { |
2134 | + float dx; |
2135 | + |
2136 | + dx = 1.0 / tc->def->resolution / 4; |
2137 | + if (!tc->forwards) |
2138 | + dx = -dx; |
2139 | + pitch_dt_observation(&tc->pitch, dx); |
2140 | + } |
2141 | + |
2142 | + /* If we have crossed the primary channel in the right polarity, |
2143 | + * it's time to read off a timecode 0 or 1 value */ |
2144 | + |
2145 | + if (tc->secondary.swapped && |
2146 | + tc->primary.positive == ((tc->def->flags & SWITCH_POLARITY) == 0)) |
2147 | + { |
2148 | + process_bitstream(tc, m); |
2149 | + } |
2150 | + |
2151 | + tc->timecode_ticker++; |
2152 | } |
2153 | |
2154 | |
2155 | /* Submit and decode a block of PCM audio data to the timecoder */ |
2156 | |
2157 | -int timecoder_submit(struct timecoder_t *tc, signed short *pcm, int samples) |
2158 | +void timecoder_submit(struct timecoder_t *tc, signed short *pcm, size_t npcm) |
2159 | { |
2160 | - int b, l, /* bitstream and timecode bits */ |
2161 | - s, c, |
2162 | - x, y, p, /* monitor coordinates */ |
2163 | - v, |
2164 | - offset, |
2165 | - swapped, |
2166 | - monitor_centre; |
2167 | - signed short w; /* pcm sample values */ |
2168 | - unsigned int mask; |
2169 | - |
2170 | - b = 0; |
2171 | - l = 0; |
2172 | - |
2173 | - mask = ((1 << tc->tc_table->bits) - 1); |
2174 | - monitor_centre = tc->mon_size / 2; |
2175 | - |
2176 | - offset = 0; |
2177 | - |
2178 | - for(s = 0; s < samples; s++) { |
2179 | - |
2180 | - for(c = 0; c < TIMECODER_CHANNELS; c++) |
2181 | - detect_zero_crossing(&tc->channel[c], pcm[offset + c], tc->rate); |
2182 | - |
2183 | - /* Read from the mono channel */ |
2184 | - |
2185 | - v = pcm[offset] + pcm[offset + 1]; |
2186 | - swapped = detect_zero_crossing(&tc->mono, v, tc->rate); |
2187 | - |
2188 | - /* If a sign change in the (zero corrected) audio has |
2189 | - * happened, log the peak information */ |
2190 | - |
2191 | - if(swapped) { |
2192 | - |
2193 | - /* Work out whether half way through a cycle we are |
2194 | - * looking for the wave to be positive or negative */ |
2195 | - |
2196 | - if(tc->mono.positive == (tc->tc_table->polarity ^ tc->forwards)) { |
2197 | - |
2198 | - /* Entering the second half of a wave cycle */ |
2199 | - |
2200 | - tc->half_peak = tc->wave_peak; |
2201 | - |
2202 | - } else { |
2203 | - |
2204 | - /* Completed a full wave cycle, so time to analyse the |
2205 | - * level and work out whether it's a 1 or 0 */ |
2206 | - |
2207 | - b = tc->wave_peak + tc->half_peak > tc->ref_level; |
2208 | - |
2209 | - /* Log binary timecode */ |
2210 | - |
2211 | - if(tc->log_fd != -1) |
2212 | - write(tc->log_fd, b ? "1" : "0", 1); |
2213 | - |
2214 | - /* Add it to the bitstream, and work out what we were |
2215 | - * expecting (timecode). */ |
2216 | - |
2217 | - /* tc->bitstream is always in the order it is |
2218 | - * physically placed on the vinyl, regardless of the |
2219 | - * direction. */ |
2220 | - |
2221 | - if(tc->forwards) { |
2222 | - l = lfsr(tc->timecode, tc); |
2223 | - |
2224 | - tc->bitstream = (tc->bitstream >> 1) |
2225 | - + (b << (tc->tc_table->bits - 1)); |
2226 | - |
2227 | - tc->timecode = (tc->timecode >> 1) |
2228 | - + (l << (tc->tc_table->bits - 1)); |
2229 | - |
2230 | - } else { |
2231 | - l = lfsr_rev(tc->timecode, tc); |
2232 | - |
2233 | - tc->bitstream = ((tc->bitstream << 1) & mask) + b; |
2234 | - tc->timecode = ((tc->timecode << 1) & mask) + l; |
2235 | - } |
2236 | - |
2237 | - if(b == l) { |
2238 | - tc->valid_counter++; |
2239 | - } else { |
2240 | - tc->timecode = tc->bitstream; |
2241 | - tc->valid_counter = 0; |
2242 | - } |
2243 | - |
2244 | - /* Take note of the last time we read a valid timecode */ |
2245 | - |
2246 | - tc->timecode_ticker = 0; |
2247 | - |
2248 | - /* Adjust the reference level based on the peaks seen |
2249 | - * in this cycle */ |
2250 | - |
2251 | - if(tc->ref_level == -1) |
2252 | - tc->ref_level = tc->half_peak + tc->wave_peak; |
2253 | - else { |
2254 | - tc->ref_level = (tc->ref_level * (REF_PEAKS_AVG - 1) |
2255 | - + tc->half_peak + tc->wave_peak) |
2256 | - / REF_PEAKS_AVG; |
2257 | - } |
2258 | - |
2259 | - } |
2260 | - |
2261 | - /* Calculate the immediate direction from phase difference, |
2262 | - * based on the last channel to cross zero */ |
2263 | - |
2264 | - if(tc->channel[0].crossing_ticker > tc->channel[1].crossing_ticker) |
2265 | - tc->forwards = 1; |
2266 | - else |
2267 | - tc->forwards = 0; |
2268 | - |
2269 | - if(tc->forwards) |
2270 | - tc->crossings++; |
2271 | - else |
2272 | - tc->crossings--; |
2273 | - |
2274 | - tc->pitch_ticker += tc->crossing_ticker; |
2275 | - tc->crossing_ticker = 0; |
2276 | - tc->wave_peak = 0; |
2277 | - |
2278 | - } /* swapped */ |
2279 | - |
2280 | - tc->crossing_ticker++; |
2281 | - tc->timecode_ticker++; |
2282 | - |
2283 | - /* Find the zero-normalised sample of the peak value from |
2284 | - * the input */ |
2285 | - |
2286 | - w = abs(v - tc->mono.zero); |
2287 | - if(w > tc->wave_peak) |
2288 | - tc->wave_peak = w; |
2289 | - |
2290 | - /* Take a rolling average of zero and signal level */ |
2291 | - |
2292 | - tc->signal_level += (w - tc->signal_level) * SIGNAL_AVG / tc->rate; |
2293 | - |
2294 | - /* Update the monitor to add the incoming sample */ |
2295 | - |
2296 | - if(tc->mon) { |
2297 | - |
2298 | - /* Decay the pixels already in the montior */ |
2299 | - |
2300 | - if(++tc->mon_counter % MONITOR_DECAY_EVERY == 0) { |
2301 | - for(p = 0; p < SQ(tc->mon_size); p++) { |
2302 | - if(tc->mon[p]) |
2303 | - tc->mon[p] = tc->mon[p] * 7 / 8; |
2304 | - } |
2305 | - } |
2306 | - |
2307 | - v = pcm[offset]; /* first channel */ |
2308 | - w = pcm[offset + 1]; /* second channel */ |
2309 | - |
2310 | - x = monitor_centre + (v * tc->mon_size * tc->mon_scale / 32768); |
2311 | - y = monitor_centre + (w * tc->mon_size * tc->mon_scale / 32768); |
2312 | - |
2313 | - /* Set the pixel value to white */ |
2314 | - |
2315 | - if(x > 0 && x < tc->mon_size && y > 0 && y < tc->mon_size) |
2316 | - tc->mon[y * tc->mon_size + x] = 0xff; |
2317 | + while (npcm--) { |
2318 | + signed int primary, secondary; |
2319 | + |
2320 | + if (tc->def->flags & SWITCH_PRIMARY) { |
2321 | + primary = pcm[0]; |
2322 | + secondary = pcm[1]; |
2323 | + } else { |
2324 | + primary = pcm[1]; |
2325 | + secondary = pcm[0]; |
2326 | } |
2327 | - |
2328 | - offset += TIMECODER_CHANNELS; |
2329 | - |
2330 | - } /* for each sample */ |
2331 | - |
2332 | - /* Print debugging information */ |
2333 | - |
2334 | -#if 0 |
2335 | - fprintf(stderr, "%+6d +/%4d -/%4d (%4d,%4d)\t= %d (%d) %c %d" |
2336 | - "\t[crossings: %d %d]", |
2337 | - tc->mono.zero, |
2338 | - tc->half_peak, |
2339 | - tc->wave_peak, |
2340 | - tc->ref_level >> 1, |
2341 | - tc->signal_level, |
2342 | - b, l, b == l ? ' ' : 'x', |
2343 | - tc->valid_counter, |
2344 | - tc->crossings, |
2345 | - tc->pitch_ticker); |
2346 | - |
2347 | - if(tc->pitch_ticker) |
2348 | - fprintf(stderr, " = %d", tc->rate * tc->crossings / tc->pitch_ticker); |
2349 | - |
2350 | - fputc('\n', stderr); |
2351 | -#endif |
2352 | - |
2353 | - return 0; |
2354 | -} |
2355 | - |
2356 | - |
2357 | -/* Return the timecode pitch, based on cycles of the sine wave. This |
2358 | - * function can only be called by one context, at it resets the state |
2359 | - * of the counter in the timecoder. */ |
2360 | - |
2361 | -int timecoder_get_pitch(struct timecoder_t *tc, float *pitch) |
2362 | -{ |
2363 | - /* Let the caller know if there's no data to gather pitch from */ |
2364 | - |
2365 | - if(tc->crossings == 0) |
2366 | - return -1; |
2367 | - |
2368 | - /* Value of tc->crossings may be negative in reverse */ |
2369 | - |
2370 | - *pitch = tc->rate * (float)tc->crossings / tc->pitch_ticker |
2371 | - / (tc->tc_table->resolution * 2); |
2372 | - |
2373 | - tc->crossings = 0; |
2374 | - tc->pitch_ticker = 0; |
2375 | - |
2376 | - return 0; |
2377 | + |
2378 | + process_sample(tc, primary, secondary); |
2379 | + |
2380 | + update_monitor(tc, pcm[0], pcm[1]); |
2381 | + pcm += TIMECODER_CHANNELS; |
2382 | + } |
2383 | } |
2384 | |
2385 | |
2386 | /* Return the known position in the timecode, or -1 if not known. If |
2387 | * two few bits have been error-checked, then this also counts as |
2388 | - * invalid. If 'when' is given, return the time, in input samples since |
2389 | - * this value was read. */ |
2390 | + * invalid. If 'when' is given, return the time, in seconds since this |
2391 | + * value was read. */ |
2392 | |
2393 | -signed int timecoder_get_position(struct timecoder_t *tc, int *when) |
2394 | +signed int timecoder_get_position(struct timecoder_t *tc, float *when) |
2395 | { |
2396 | signed int r; |
2397 | |
2398 | - if(tc->valid_counter > VALID_BITS) { |
2399 | - r = tc->tc_table->lookup[tc->bitstream]; |
2400 | + if (tc->valid_counter > VALID_BITS) { |
2401 | + r = lut_lookup(&tc->def->lut, tc->bitstream); |
2402 | |
2403 | - if(r >= 0) { |
2404 | - if(when) |
2405 | - *when = tc->timecode_ticker; |
2406 | + if (r >= 0) { |
2407 | + if (when) |
2408 | + *when = tc->timecode_ticker * tc->dt; |
2409 | return r; |
2410 | } |
2411 | } |
2412 | |
2413 | return -1; |
2414 | } |
2415 | - |
2416 | - |
2417 | -/* Return non-zero if there is any timecode signal available */ |
2418 | - |
2419 | -int timecoder_get_alive(struct timecoder_t *tc) |
2420 | -{ |
2421 | - if(tc->signal_level < SIGNAL_THRESHOLD) |
2422 | - return 0; |
2423 | - |
2424 | - return 1; |
2425 | -} |
2426 | - |
2427 | - |
2428 | -/* Return the last 'safe' timecode value on the record. Beyond this |
2429 | - * value, we probably want to ignore the timecode values, as we will |
2430 | - * hit the label of the record. */ |
2431 | - |
2432 | -unsigned int timecoder_get_safe(struct timecoder_t *tc) |
2433 | -{ |
2434 | - return tc->tc_table->safe; |
2435 | -} |
2436 | - |
2437 | - |
2438 | -/* Return the resolution of the timecode. This is the number of bits |
2439 | - * per second, which corresponds to the frequency of the sine wave */ |
2440 | - |
2441 | -int timecoder_get_resolution(struct timecoder_t *tc) |
2442 | -{ |
2443 | - return tc->tc_table->resolution; |
2444 | -} |
2445 | |
2446 | === modified file 'mixxx/res/skins/Outline1024x600-Netbook/skin.xml' |
2447 | --- mixxx/res/skins/Outline1024x600-Netbook/skin.xml 2011-03-22 16:24:04 +0000 |
2448 | +++ mixxx/res/skins/Outline1024x600-Netbook/skin.xml 2011-04-16 23:24:33 +0000 |
2449 | @@ -949,6 +949,83 @@ |
2450 | <ConfigKey>[Master],PeakIndicator</ConfigKey> |
2451 | </Connection> |
2452 | </StatusLight> |
2453 | + |
2454 | + <!-- |
2455 | + ********************************************** |
2456 | + Vinyl Stuff |
2457 | + ********************************************** |
2458 | + --> |
2459 | + |
2460 | + <StatusLight> |
2461 | + <Tooltip>Vinyl Status</Tooltip> |
2462 | + <NumberPos>4</NumberPos> |
2463 | + <PathBack>vinyl-status-disabled.png</PathBack> |
2464 | + <PathStatusLight>vinyl-status-green.png</PathStatusLight> |
2465 | + <PathStatusLight2>vinyl-status-yellow.png</PathStatusLight2> |
2466 | + <PathStatusLight3>vinyl-status-red.png</PathStatusLight3> |
2467 | + <Pos>357,194</Pos> |
2468 | + <Connection> |
2469 | + <ConfigKey>[Channel1],vinylcontrol_status</ConfigKey> |
2470 | + </Connection> |
2471 | + </StatusLight> |
2472 | + <StatusLight> |
2473 | + <Tooltip>Vinyl Status</Tooltip> |
2474 | + <NumberPos>4</NumberPos> |
2475 | + <PathBack>vinyl-status-disabled.png</PathBack> |
2476 | + <PathStatusLight>vinyl-status-green.png</PathStatusLight> |
2477 | + <PathStatusLight2>vinyl-status-yellow.png</PathStatusLight2> |
2478 | + <PathStatusLight3>vinyl-status-red.png</PathStatusLight3> |
2479 | + <Pos>597,194</Pos> |
2480 | + <Connection> |
2481 | + <ConfigKey>[Channel2],vinylcontrol_status</ConfigKey> |
2482 | + </Connection> |
2483 | + </StatusLight> |
2484 | + <PushButton> |
2485 | + <Tooltip>Vinyl Control Mode</Tooltip> |
2486 | + <NumberStates>3</NumberStates> |
2487 | + <State> |
2488 | + <Number>0</Number> |
2489 | + <Pressed>vinyl-abs.png</Pressed> |
2490 | + <Unpressed>vinyl-abs.png</Unpressed> |
2491 | + </State> |
2492 | + <State> |
2493 | + <Number>1</Number> |
2494 | + <Pressed>vinyl-rel.png</Pressed> |
2495 | + <Unpressed>vinyl-rel.png</Unpressed> |
2496 | + </State> |
2497 | + <State> |
2498 | + <Number>2</Number> |
2499 | + <Pressed>vinyl-const.png</Pressed> |
2500 | + <Unpressed>vinyl-const.png</Unpressed> |
2501 | + </State> |
2502 | + <Pos>380,192</Pos> |
2503 | + <Connection> |
2504 | + <ConfigKey>[Channel1],vinylcontrol_mode</ConfigKey> |
2505 | + </Connection> |
2506 | + </PushButton> |
2507 | + <PushButton> |
2508 | + <Tooltip>Vinyl Control Mode</Tooltip> |
2509 | + <NumberStates>3</NumberStates> |
2510 | + <State> |
2511 | + <Number>0</Number> |
2512 | + <Pressed>vinyl-abs.png</Pressed> |
2513 | + <Unpressed>vinyl-abs.png</Unpressed> |
2514 | + </State> |
2515 | + <State> |
2516 | + <Number>1</Number> |
2517 | + <Pressed>vinyl-rel.png</Pressed> |
2518 | + <Unpressed>vinyl-rel.png</Unpressed> |
2519 | + </State> |
2520 | + <State> |
2521 | + <Number>2</Number> |
2522 | + <Pressed>vinyl-const.png</Pressed> |
2523 | + <Unpressed>vinyl-const.png</Unpressed> |
2524 | + </State> |
2525 | + <Pos>621,192</Pos> |
2526 | + <Connection> |
2527 | + <ConfigKey>[Channel2],vinylcontrol_mode</ConfigKey> |
2528 | + </Connection> |
2529 | + </PushButton> |
2530 | |
2531 | <!-- |
2532 | ############################################################################################ |
2533 | |
2534 | === added file 'mixxx/res/skins/Outline1024x600-Netbook/vinyl-abs.png' |
2535 | Binary files mixxx/res/skins/Outline1024x600-Netbook/vinyl-abs.png 1970-01-01 00:00:00 +0000 and mixxx/res/skins/Outline1024x600-Netbook/vinyl-abs.png 2011-04-16 23:24:33 +0000 differ |
2536 | === added file 'mixxx/res/skins/Outline1024x600-Netbook/vinyl-const.png' |
2537 | Binary files mixxx/res/skins/Outline1024x600-Netbook/vinyl-const.png 1970-01-01 00:00:00 +0000 and mixxx/res/skins/Outline1024x600-Netbook/vinyl-const.png 2011-04-16 23:24:33 +0000 differ |
2538 | === added file 'mixxx/res/skins/Outline1024x600-Netbook/vinyl-rel.png' |
2539 | Binary files mixxx/res/skins/Outline1024x600-Netbook/vinyl-rel.png 1970-01-01 00:00:00 +0000 and mixxx/res/skins/Outline1024x600-Netbook/vinyl-rel.png 2011-04-16 23:24:33 +0000 differ |
2540 | === added file 'mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-disabled.png' |
2541 | Binary files mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-disabled.png 1970-01-01 00:00:00 +0000 and mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-disabled.png 2011-04-16 23:24:33 +0000 differ |
2542 | === added file 'mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-green.png' |
2543 | Binary files mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-green.png 1970-01-01 00:00:00 +0000 and mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-green.png 2011-04-16 23:24:33 +0000 differ |
2544 | === added file 'mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-red.png' |
2545 | Binary files mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-red.png 1970-01-01 00:00:00 +0000 and mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-red.png 2011-04-16 23:24:33 +0000 differ |
2546 | === added file 'mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-yellow.png' |
2547 | Binary files mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-yellow.png 1970-01-01 00:00:00 +0000 and mixxx/res/skins/Outline1024x600-Netbook/vinyl-status-yellow.png 2011-04-16 23:24:33 +0000 differ |
2548 | === modified file 'mixxx/src/cachingreader.cpp' |
2549 | --- mixxx/src/cachingreader.cpp 2010-10-23 08:41:23 +0000 |
2550 | +++ mixxx/src/cachingreader.cpp 2011-04-16 23:24:33 +0000 |
2551 | @@ -296,8 +296,9 @@ |
2552 | } |
2553 | |
2554 | int CachingReader::read(int sample, int num_samples, CSAMPLE* buffer) { |
2555 | + int zerosWritten = 0; |
2556 | // Check for bogus sample numbers |
2557 | - Q_ASSERT(sample >= 0); |
2558 | + //Q_ASSERT(sample >= 0); |
2559 | Q_ASSERT(sample % 2 == 0); |
2560 | Q_ASSERT(num_samples >= 0); |
2561 | |
2562 | @@ -309,6 +310,27 @@ |
2563 | return 0; |
2564 | } |
2565 | |
2566 | + // TODO: is it possible to move this code out of caching reader |
2567 | + // and into enginebuffer? It doesn't quite make sense here, although |
2568 | + // it makes preroll completely transparent to the rest of the code |
2569 | + |
2570 | + //if we're in preroll... |
2571 | + if (sample < 0) { |
2572 | + if (sample + num_samples <= 0) { |
2573 | + //everything is zeros, easy |
2574 | + memset(buffer, 0, sizeof(*buffer) * num_samples); |
2575 | + return num_samples; |
2576 | + } else { |
2577 | + //some of the buffer is zeros, some is from the file |
2578 | + memset(buffer, 0, sizeof(*buffer) * (0 - sample)); |
2579 | + buffer += (0 - sample); |
2580 | + num_samples = sample + num_samples; |
2581 | + zerosWritten = (0 - sample); |
2582 | + sample = 0; |
2583 | + //continue processing the rest of the chunks normally |
2584 | + } |
2585 | + } |
2586 | + |
2587 | int start_chunk = chunkForSample(sample); |
2588 | int end_chunk = chunkForSample(sample + num_samples - 1); |
2589 | |
2590 | @@ -400,7 +422,7 @@ |
2591 | samples_remaining = 0; |
2592 | |
2593 | Q_ASSERT(samples_remaining == 0); |
2594 | - return num_samples - samples_remaining; |
2595 | + return zerosWritten + num_samples - samples_remaining; |
2596 | } |
2597 | |
2598 | void CachingReader::hint(Hint& hint) { |
2599 | @@ -448,7 +470,7 @@ |
2600 | hint.sample = 0; |
2601 | } |
2602 | } |
2603 | - Q_ASSERT(hint.sample >= 0); |
2604 | + //Q_ASSERT(hint.sample >= 0); |
2605 | Q_ASSERT(hint.length >= 0); |
2606 | int start_chunk = chunkForSample(hint.sample); |
2607 | int end_chunk = chunkForSample(hint.sample + hint.length); |
2608 | |
2609 | === modified file 'mixxx/src/controlobject.cpp' |
2610 | --- mixxx/src/controlobject.cpp 2011-04-04 05:36:08 +0000 |
2611 | +++ mixxx/src/controlobject.cpp 2011-04-16 23:24:33 +0000 |
2612 | @@ -293,8 +293,11 @@ |
2613 | { |
2614 | obj = m_sqQueueThread.dequeue(); |
2615 | |
2616 | - obj->pControlObject->setValueFromThread(obj->value); |
2617 | - obj->pControlObject->updateProxies(obj->pControlObjectThread); |
2618 | + if (obj->pControlObject) |
2619 | + { |
2620 | + obj->pControlObject->setValueFromThread(obj->value); |
2621 | + obj->pControlObject->updateProxies(obj->pControlObjectThread); |
2622 | + } |
2623 | delete obj; |
2624 | } |
2625 | |
2626 | |
2627 | === modified file 'mixxx/src/controlpushbutton.cpp' |
2628 | --- mixxx/src/controlpushbutton.cpp 2010-09-17 02:20:29 +0000 |
2629 | +++ mixxx/src/controlpushbutton.cpp 2011-04-16 23:24:33 +0000 |
2630 | @@ -24,6 +24,7 @@ |
2631 | ControlPushButton::ControlPushButton(ConfigKey key) : |
2632 | ControlObject(key, false) { |
2633 | m_bIsToggleButton = false; |
2634 | + m_iNoStates = 2; |
2635 | } |
2636 | |
2637 | ControlPushButton::~ControlPushButton() |
2638 | @@ -37,6 +38,11 @@ |
2639 | m_bIsToggleButton = bIsToggleButton; |
2640 | } |
2641 | |
2642 | +void ControlPushButton::setStates(int num_states) |
2643 | +{ |
2644 | + m_iNoStates = num_states; |
2645 | +} |
2646 | + |
2647 | void ControlPushButton::setValueFromMidi(MidiCategory c, double v) |
2648 | { |
2649 | //if (m_bMidiSimulateLatching) |
2650 | @@ -44,55 +50,30 @@ |
2651 | //qDebug() << "bMidiSimulateLatching is true!"; |
2652 | // Only react on NOTE_ON midi events if simulating latching... |
2653 | |
2654 | + //qDebug() << c << v; |
2655 | |
2656 | - if (m_bIsToggleButton) //This block makes push-buttons act as toggle buttons. |
2657 | - { |
2658 | - //qDebug() << "Is a toggle button!"; |
2659 | - if (c==NOTE_ON && v>0.) //Only react to "NOTE_ON" midi events. |
2660 | - { |
2661 | - //qDebug() << "NOTE_ON caught!"; |
2662 | - if (m_dValue==0.) |
2663 | - m_dValue = 1.; |
2664 | - else |
2665 | - m_dValue = 0.; |
2666 | + if (m_bIsToggleButton) { //This block makes push-buttons act as toggle buttons. |
2667 | + if (m_iNoStates > 2) { //multistate button |
2668 | + if (v > 0.) { //looking for NOTE_ON doesn't seem to work... |
2669 | + m_dValue++; |
2670 | + if (m_dValue >= m_iNoStates) |
2671 | + m_dValue = 0; |
2672 | + } |
2673 | + } else { |
2674 | + if (c == NOTE_ON) { |
2675 | + if (v > 0.) { |
2676 | + m_dValue = !m_dValue; |
2677 | + } |
2678 | + } |
2679 | } |
2680 | - } |
2681 | - else //Not a toggle button (trigger only when button pushed) |
2682 | - { |
2683 | - //qDebug() << "Is NOT a toggle button!"; |
2684 | - if (c == NOTE_ON) |
2685 | + } else { //Not a toggle button (trigger only when button pushed) |
2686 | + if (c == NOTE_ON) { |
2687 | m_dValue = v; |
2688 | - else if (c == NOTE_OFF) |
2689 | + } else if (c == NOTE_OFF) { |
2690 | m_dValue = 0.0; |
2691 | - } |
2692 | - if (c==NOTE_OFF) |
2693 | - { |
2694 | - |
2695 | - //qDebug() << "NOTE_OFF caught!"; |
2696 | - } |
2697 | - |
2698 | - |
2699 | -/* else |
2700 | - { |
2701 | - qDebug() << "bMidiSimulateLatching is false!"; |
2702 | - qDebug() << "m_dValue is: " << m_dValue << ", v is: " << v; |
2703 | - |
2704 | - if (v==0.) |
2705 | - m_dValue = 0.; |
2706 | - else |
2707 | - m_dValue = 1.; |
2708 | - |
2709 | - } |
2710 | - */ |
2711 | -/* |
2712 | - if (v>0.) |
2713 | - { |
2714 | - if (m_dValue==0.) |
2715 | - m_dValue = 1.; |
2716 | - else |
2717 | - m_dValue = 0.; |
2718 | } |
2719 | - */ |
2720 | + } |
2721 | + |
2722 | emit(valueChanged(m_dValue)); |
2723 | } |
2724 | |
2725 | |
2726 | === modified file 'mixxx/src/controlpushbutton.h' |
2727 | --- mixxx/src/controlpushbutton.h 2010-10-07 03:05:48 +0000 |
2728 | +++ mixxx/src/controlpushbutton.h 2011-04-16 23:24:33 +0000 |
2729 | @@ -32,12 +32,14 @@ |
2730 | ControlPushButton(ConfigKey key); |
2731 | ~ControlPushButton(); |
2732 | void setToggleButton(bool bIsToggleButton); |
2733 | + void setStates(int num_states); |
2734 | |
2735 | protected: |
2736 | void setValueFromMidi(MidiCategory c, double v); |
2737 | |
2738 | private: |
2739 | bool m_bIsToggleButton; |
2740 | + int m_iNoStates; |
2741 | }; |
2742 | |
2743 | #endif |
2744 | |
2745 | === modified file 'mixxx/src/controlvaluedelegate.cpp' |
2746 | --- mixxx/src/controlvaluedelegate.cpp 2010-11-19 11:03:20 +0000 |
2747 | +++ mixxx/src/controlvaluedelegate.cpp 2011-04-16 23:24:33 +0000 |
2748 | @@ -55,6 +55,9 @@ |
2749 | m_channelControlValues.append("volume"); |
2750 | m_channelControlValues.append("wheel"); |
2751 | m_channelControlValues.append("jog"); |
2752 | + m_channelControlValues.append("vinylcontrol_enabled"); |
2753 | + m_channelControlValues.append("vinylcontrol_mode"); |
2754 | + m_channelControlValues.append("vinylcontrol_cueing"); |
2755 | m_channelControlValues.append("loop_in"); |
2756 | m_channelControlValues.append("loop_out"); |
2757 | m_channelControlValues.append("reloop_exit"); |
2758 | |
2759 | === modified file 'mixxx/src/dlgpreferences.cpp' |
2760 | --- mixxx/src/dlgpreferences.cpp 2011-03-17 06:49:23 +0000 |
2761 | +++ mixxx/src/dlgpreferences.cpp 2011-04-16 23:24:33 +0000 |
2762 | @@ -18,6 +18,8 @@ |
2763 | |
2764 | #ifdef __VINYLCONTROL__ |
2765 | #include "dlgprefvinyl.h" |
2766 | +#else |
2767 | +#include "dlgprefnovinyl.h" |
2768 | #endif |
2769 | |
2770 | #ifdef __SHOUTCAST__ |
2771 | @@ -75,6 +77,8 @@ |
2772 | wrecord = new DlgPrefRecord(this, config); |
2773 | #ifdef __VINYLCONTROL__ |
2774 | wvinylcontrol = new DlgPrefVinyl(this, soundman, config); |
2775 | +#else |
2776 | + wnovinylcontrol = new DlgPrefNoVinyl(this, soundman, config); |
2777 | #endif |
2778 | #ifdef __SHOUTCAST__ |
2779 | wshoutcast = new DlgPrefShoutcast(this, config); |
2780 | @@ -96,6 +100,8 @@ |
2781 | pagesWidget->addWidget(wreplaygain); |
2782 | #ifdef __VINYLCONTROL__ |
2783 | pagesWidget->addWidget(wvinylcontrol); |
2784 | +#else |
2785 | + pagesWidget->addWidget(wnovinylcontrol); |
2786 | #endif |
2787 | #ifdef __SHOUTCAST__ |
2788 | pagesWidget->addWidget(wshoutcast); |
2789 | @@ -134,6 +140,7 @@ |
2790 | |
2791 | #ifdef __VINYLCONTROL__ |
2792 | connect(wvinylcontrol, SIGNAL(refreshVCProxies()), wsound, SLOT(forceApply())); |
2793 | + connect(wvinylcontrol, SIGNAL(applySound()), wsound, SLOT(slotApply())); |
2794 | connect(buttonBox, SIGNAL(accepted()), wvinylcontrol, SLOT(slotApply())); //It's important for this to be before the |
2795 | //connect for wsound... |
2796 | #endif |
2797 | @@ -242,6 +249,14 @@ |
2798 | m_pVinylControlButton->setText(0, tr("Vinyl Control")); |
2799 | m_pVinylControlButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
2800 | m_pVinylControlButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); |
2801 | +#else |
2802 | + m_pVinylControlButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); |
2803 | + //QT screws up my nice vinyl svg for some reason, so we'll use a PNG version |
2804 | + //instead... |
2805 | + m_pVinylControlButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_vinyl.png")); |
2806 | + m_pVinylControlButton->setText(0, tr("Vinyl Control")); |
2807 | + m_pVinylControlButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
2808 | + m_pVinylControlButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); |
2809 | #endif |
2810 | |
2811 | #ifdef __SHOUTCAST__ |
2812 | @@ -277,11 +292,14 @@ |
2813 | else if (current == m_pBPMdetectButton) |
2814 | pagesWidget->setCurrentWidget(wbpm); |
2815 | else if (current == m_pReplayGainButton) |
2816 | - pagesWidget->setCurrentWidget(wreplaygain); |
2817 | + pagesWidget->setCurrentWidget(wreplaygain); |
2818 | |
2819 | #ifdef __VINYLCONTROL__ |
2820 | else if (current == m_pVinylControlButton) |
2821 | pagesWidget->setCurrentWidget(wvinylcontrol); |
2822 | +#else |
2823 | + else if (current == m_pVinylControlButton) |
2824 | + pagesWidget->setCurrentWidget(wnovinylcontrol); |
2825 | #endif |
2826 | #ifdef __SHOUTCAST__ |
2827 | else if (current == m_pShoutcastButton) |
2828 | |
2829 | === modified file 'mixxx/src/dlgpreferences.h' |
2830 | --- mixxx/src/dlgpreferences.h 2011-03-17 06:49:23 +0000 |
2831 | +++ mixxx/src/dlgpreferences.h 2011-04-16 23:24:33 +0000 |
2832 | @@ -42,6 +42,7 @@ |
2833 | class DlgPrefRecord; |
2834 | class DlgPrefBpm; |
2835 | class DlgPrefVinyl; |
2836 | +class DlgPrefNoVinyl; |
2837 | class DlgPrefShoutcast; |
2838 | class DlgPrefReplayGain; |
2839 | class PowerMate; |
2840 | @@ -89,19 +90,20 @@ |
2841 | DlgPrefRecord *wrecord; |
2842 | DlgPrefBpm *wbpm; |
2843 | DlgPrefVinyl *wvinylcontrol; |
2844 | + DlgPrefNoVinyl *wnovinylcontrol; |
2845 | DlgPrefShoutcast *wshoutcast; |
2846 | DlgPrefReplayGain *wreplaygain; |
2847 | |
2848 | - QTreeWidgetItem* m_pSoundButton; |
2849 | - QTreeWidgetItem* m_pPlaylistButton; |
2850 | - QTreeWidgetItem* m_pControlsButton; |
2851 | - QTreeWidgetItem* m_pEqButton; |
2852 | - QTreeWidgetItem* m_pCrossfaderButton; |
2853 | - QTreeWidgetItem* m_pRecordingButton; |
2854 | - QTreeWidgetItem* m_pBPMdetectButton; |
2855 | - QTreeWidgetItem* m_pVinylControlButton; |
2856 | - QTreeWidgetItem* m_pShoutcastButton; |
2857 | - QTreeWidgetItem* m_pReplayGainButton; |
2858 | + QTreeWidgetItem* m_pSoundButton; |
2859 | + QTreeWidgetItem* m_pPlaylistButton; |
2860 | + QTreeWidgetItem* m_pControlsButton; |
2861 | + QTreeWidgetItem* m_pEqButton; |
2862 | + QTreeWidgetItem* m_pCrossfaderButton; |
2863 | + QTreeWidgetItem* m_pRecordingButton; |
2864 | + QTreeWidgetItem* m_pBPMdetectButton; |
2865 | + QTreeWidgetItem* m_pVinylControlButton; |
2866 | + QTreeWidgetItem* m_pShoutcastButton; |
2867 | + QTreeWidgetItem* m_pReplayGainButton; |
2868 | QTreeWidgetItem* m_pMIDITreeItem; |
2869 | QList<QTreeWidgetItem*> m_midiBindingsButtons; |
2870 | |
2871 | |
2872 | === added file 'mixxx/src/dlgprefnovinyl.cpp' |
2873 | --- mixxx/src/dlgprefnovinyl.cpp 1970-01-01 00:00:00 +0000 |
2874 | +++ mixxx/src/dlgprefnovinyl.cpp 2011-04-16 23:24:33 +0000 |
2875 | @@ -0,0 +1,32 @@ |
2876 | +/*************************************************************************** |
2877 | + DlgPrefNoVinyl.cpp - description |
2878 | + ------------------- |
2879 | + begin : Thu Feb 24 2011 |
2880 | + copyright : (C) 2011 by Owen Williams |
2881 | + email : owen-bugs@ywwg.com |
2882 | +***************************************************************************/ |
2883 | + |
2884 | +/*************************************************************************** |
2885 | +* * |
2886 | +* This program is free software; you can redistribute it and/or modify * |
2887 | +* it under the terms of the GNU General Public License as published by * |
2888 | +* the Free Software Foundation; either version 2 of the License, or * |
2889 | +* (at your option) any later version. * |
2890 | +* * |
2891 | +***************************************************************************/ |
2892 | + |
2893 | + |
2894 | +#include <QtCore> |
2895 | +#include <QtDebug> |
2896 | +#include <QtGui> |
2897 | +#include "dlgprefnovinyl.h" |
2898 | + |
2899 | +DlgPrefNoVinyl::DlgPrefNoVinyl(QWidget * parent, SoundManager * soundman, |
2900 | + ConfigObject<ConfigValue> * _config) : QWidget(parent), Ui::DlgPrefNoVinylDlg() |
2901 | +{ |
2902 | + setupUi(this); |
2903 | +} |
2904 | + |
2905 | +DlgPrefNoVinyl::~DlgPrefNoVinyl() |
2906 | +{ |
2907 | +} |
2908 | |
2909 | === added file 'mixxx/src/dlgprefnovinyl.h' |
2910 | --- mixxx/src/dlgprefnovinyl.h 1970-01-01 00:00:00 +0000 |
2911 | +++ mixxx/src/dlgprefnovinyl.h 2011-04-16 23:24:33 +0000 |
2912 | @@ -0,0 +1,39 @@ |
2913 | +/*************************************************************************** |
2914 | + dlgprefnovinyl.h - description |
2915 | + ------------------- |
2916 | + begin : Thu Feb 24 2011 |
2917 | + copyright : (C) 2011 by Owen Williams |
2918 | + email : owen-bugs@ywwg.com |
2919 | + ***************************************************************************/ |
2920 | + |
2921 | +/*************************************************************************** |
2922 | + * * |
2923 | + * This program is free software; you can redistribute it and/or modify * |
2924 | + * it under the terms of the GNU General Public License as published by * |
2925 | + * the Free Software Foundation; either version 2 of the License, or * |
2926 | + * (at your option) any later version. * |
2927 | + * * |
2928 | + ***************************************************************************/ |
2929 | + |
2930 | +#ifndef DLGPREFNOVINYL_H |
2931 | +#define DLGPREFNOVINYL_H |
2932 | + |
2933 | +#include "ui_dlgprefnovinyldlg.h" |
2934 | +#include "configobject.h" |
2935 | + |
2936 | +class QWidget; |
2937 | +class SoundManager; |
2938 | + |
2939 | +/** |
2940 | + *@author Stefan Langhammer |
2941 | + *@author Albert Santoni |
2942 | + */ |
2943 | + |
2944 | +class DlgPrefNoVinyl : public QWidget, Ui::DlgPrefNoVinylDlg { |
2945 | + Q_OBJECT |
2946 | +public: |
2947 | + DlgPrefNoVinyl(QWidget *parent, SoundManager* soundman, ConfigObject<ConfigValue> *_config); |
2948 | + ~DlgPrefNoVinyl(); |
2949 | +}; |
2950 | + |
2951 | +#endif |
2952 | |
2953 | === added file 'mixxx/src/dlgprefnovinyldlg.ui' |
2954 | --- mixxx/src/dlgprefnovinyldlg.ui 1970-01-01 00:00:00 +0000 |
2955 | +++ mixxx/src/dlgprefnovinyldlg.ui 2011-04-16 23:24:33 +0000 |
2956 | @@ -0,0 +1,475 @@ |
2957 | +<?xml version="1.0" encoding="UTF-8"?> |
2958 | +<ui version="4.0"> |
2959 | + <class>DlgPrefNoVinylDlg</class> |
2960 | + <widget class="QWidget" name="DlgPrefNoVinylDlg"> |
2961 | + <property name="enabled"> |
2962 | + <bool>true</bool> |
2963 | + </property> |
2964 | + <property name="geometry"> |
2965 | + <rect> |
2966 | + <x>0</x> |
2967 | + <y>0</y> |
2968 | + <width>514</width> |
2969 | + <height>547</height> |
2970 | + </rect> |
2971 | + </property> |
2972 | + <property name="windowTitle"> |
2973 | + <string>Form1</string> |
2974 | + </property> |
2975 | + <layout class="QGridLayout" name="gridLayout_3"> |
2976 | + <item row="0" column="0" colspan="3"> |
2977 | + <widget class="QGroupBox" name="groupBox_2"> |
2978 | + <property name="enabled"> |
2979 | + <bool>false</bool> |
2980 | + </property> |
2981 | + <property name="title"> |
2982 | + <string>Input</string> |
2983 | + </property> |
2984 | + <layout class="QVBoxLayout" name="verticalLayout"> |
2985 | + <item> |
2986 | + <widget class="QLabel" name="devicesLabel"> |
2987 | + <property name="text"> |
2988 | + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> |
2989 | +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> |
2990 | +p, li { white-space: pre-wrap; } |
2991 | +</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> |
2992 | +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Select sound devices for vinyl control in the Sound Hardware pane.</span></p></body></html></string> |
2993 | + </property> |
2994 | + </widget> |
2995 | + </item> |
2996 | + <item> |
2997 | + <widget class="QLabel" name="label_2"> |
2998 | + <property name="text"> |
2999 | + <string><a href="http://www.mixxx.org/wiki/doku.php/vinyl_control#troubleshooting">Troubleshooting</a></string> |
3000 | + </property> |
3001 | + <property name="openExternalLinks"> |
3002 | + <bool>true</bool> |
3003 | + </property> |
3004 | + </widget> |
3005 | + </item> |
3006 | + <item> |
3007 | + <layout class="QHBoxLayout" name="horizontalLayout_2"> |
3008 | + <item> |
3009 | + <widget class="QLabel" name="textLabel1_3"> |
3010 | + <property name="text"> |
3011 | + <string>Turntable Preamp</string> |
3012 | + </property> |
3013 | + <property name="wordWrap"> |
3014 | + <bool>false</bool> |
3015 | + </property> |
3016 | + </widget> |
3017 | + </item> |
3018 | + <item> |
3019 | + <widget class="QSlider" name="VinylGain"> |
3020 | + <property name="minimum"> |
3021 | + <number>1</number> |
3022 | + </property> |
3023 | + <property name="maximum"> |
3024 | + <number>150</number> |
3025 | + </property> |
3026 | + <property name="orientation"> |
3027 | + <enum>Qt::Horizontal</enum> |
3028 | + </property> |
3029 | + </widget> |
3030 | + </item> |
3031 | + </layout> |
3032 | + </item> |
3033 | + <item> |
3034 | + <layout class="QHBoxLayout" name="horizontalLayout"> |
3035 | + <item> |
3036 | + <spacer name="horizontalSpacer_3"> |
3037 | + <property name="orientation"> |
3038 | + <enum>Qt::Horizontal</enum> |
3039 | + </property> |
3040 | + <property name="sizeType"> |
3041 | + <enum>QSizePolicy::Fixed</enum> |
3042 | + </property> |
3043 | + <property name="sizeHint" stdset="0"> |
3044 | + <size> |
3045 | + <width>100</width> |
3046 | + <height>20</height> |
3047 | + </size> |
3048 | + </property> |
3049 | + </spacer> |
3050 | + </item> |
3051 | + <item> |
3052 | + <widget class="QLabel" name="textLabel2"> |
3053 | + <property name="text"> |
3054 | + <string>1 (Off)</string> |
3055 | + </property> |
3056 | + <property name="wordWrap"> |
3057 | + <bool>false</bool> |
3058 | + </property> |
3059 | + </widget> |
3060 | + </item> |
3061 | + <item> |
3062 | + <spacer name="horizontalSpacer"> |
3063 | + <property name="orientation"> |
3064 | + <enum>Qt::Horizontal</enum> |
3065 | + </property> |
3066 | + <property name="sizeHint" stdset="0"> |
3067 | + <size> |
3068 | + <width>40</width> |
3069 | + <height>20</height> |
3070 | + </size> |
3071 | + </property> |
3072 | + </spacer> |
3073 | + </item> |
3074 | + <item> |
3075 | + <widget class="QLabel" name="textLabel3"> |
3076 | + <property name="sizePolicy"> |
3077 | + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> |
3078 | + <horstretch>0</horstretch> |
3079 | + <verstretch>0</verstretch> |
3080 | + </sizepolicy> |
3081 | + </property> |
3082 | + <property name="text"> |
3083 | + <string>150</string> |
3084 | + </property> |
3085 | + <property name="wordWrap"> |
3086 | + <bool>false</bool> |
3087 | + </property> |
3088 | + </widget> |
3089 | + </item> |
3090 | + </layout> |
3091 | + </item> |
3092 | + </layout> |
3093 | + </widget> |
3094 | + </item> |
3095 | + <item row="1" column="0" colspan="3"> |
3096 | + <widget class="QGroupBox" name="groupBox_3"> |
3097 | + <property name="enabled"> |
3098 | + <bool>false</bool> |
3099 | + </property> |
3100 | + <property name="title"> |
3101 | + <string>Vinyl Configuration</string> |
3102 | + </property> |
3103 | + <layout class="QGridLayout" name="gridLayout_2"> |
3104 | + <item row="0" column="0"> |
3105 | + <widget class="QLabel" name="TextLabel29"> |
3106 | + <property name="enabled"> |
3107 | + <bool>false</bool> |
3108 | + </property> |
3109 | + <property name="sizePolicy"> |
3110 | + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> |
3111 | + <horstretch>0</horstretch> |
3112 | + <verstretch>0</verstretch> |
3113 | + </sizepolicy> |
3114 | + </property> |
3115 | + <property name="font"> |
3116 | + <font/> |
3117 | + </property> |
3118 | + <property name="text"> |
3119 | + <string>Deck 1 Vinyl Type</string> |
3120 | + </property> |
3121 | + <property name="wordWrap"> |
3122 | + <bool>false</bool> |
3123 | + </property> |
3124 | + </widget> |
3125 | + </item> |
3126 | + <item row="0" column="2"> |
3127 | + <widget class="QComboBox" name="ComboBoxVinylType1"> |
3128 | + <property name="enabled"> |
3129 | + <bool>false</bool> |
3130 | + </property> |
3131 | + <property name="sizePolicy"> |
3132 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
3133 | + <horstretch>0</horstretch> |
3134 | + <verstretch>0</verstretch> |
3135 | + </sizepolicy> |
3136 | + </property> |
3137 | + <property name="font"> |
3138 | + <font/> |
3139 | + </property> |
3140 | + </widget> |
3141 | + </item> |
3142 | + <item row="0" column="3"> |
3143 | + <widget class="QComboBox" name="ComboBoxVinylSpeed1"> |
3144 | + <property name="enabled"> |
3145 | + <bool>false</bool> |
3146 | + </property> |
3147 | + <property name="sizePolicy"> |
3148 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
3149 | + <horstretch>0</horstretch> |
3150 | + <verstretch>0</verstretch> |
3151 | + </sizepolicy> |
3152 | + </property> |
3153 | + <property name="font"> |
3154 | + <font/> |
3155 | + </property> |
3156 | + </widget> |
3157 | + </item> |
3158 | + <item row="1" column="0"> |
3159 | + <widget class="QLabel" name="TextLabel28"> |
3160 | + <property name="enabled"> |
3161 | + <bool>false</bool> |
3162 | + </property> |
3163 | + <property name="sizePolicy"> |
3164 | + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> |
3165 | + <horstretch>0</horstretch> |
3166 | + <verstretch>0</verstretch> |
3167 | + </sizepolicy> |
3168 | + </property> |
3169 | + <property name="font"> |
3170 | + <font/> |
3171 | + </property> |
3172 | + <property name="text"> |
3173 | + <string>Deck 2 Vinyl Type</string> |
3174 | + </property> |
3175 | + <property name="wordWrap"> |
3176 | + <bool>false</bool> |
3177 | + </property> |
3178 | + </widget> |
3179 | + </item> |
3180 | + <item row="1" column="2"> |
3181 | + <widget class="QComboBox" name="ComboBoxVinylType2"> |
3182 | + <property name="enabled"> |
3183 | + <bool>false</bool> |
3184 | + </property> |
3185 | + <property name="sizePolicy"> |
3186 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
3187 | + <horstretch>0</horstretch> |
3188 | + <verstretch>0</verstretch> |
3189 | + </sizepolicy> |
3190 | + </property> |
3191 | + <property name="font"> |
3192 | + <font/> |
3193 | + </property> |
3194 | + </widget> |
3195 | + </item> |
3196 | + <item row="1" column="3"> |
3197 | + <widget class="QComboBox" name="ComboBoxVinylSpeed2"> |
3198 | + <property name="enabled"> |
3199 | + <bool>false</bool> |
3200 | + </property> |
3201 | + <property name="sizePolicy"> |
3202 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
3203 | + <horstretch>0</horstretch> |
3204 | + <verstretch>0</verstretch> |
3205 | + </sizepolicy> |
3206 | + </property> |
3207 | + <property name="font"> |
3208 | + <font/> |
3209 | + </property> |
3210 | + </widget> |
3211 | + </item> |
3212 | + <item row="2" column="0"> |
3213 | + <widget class="QLabel" name="TextLabel2_2"> |
3214 | + <property name="enabled"> |
3215 | + <bool>false</bool> |
3216 | + </property> |
3217 | + <property name="sizePolicy"> |
3218 | + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> |
3219 | + <horstretch>0</horstretch> |
3220 | + <verstretch>0</verstretch> |
3221 | + </sizepolicy> |
3222 | + </property> |
3223 | + <property name="font"> |
3224 | + <font/> |
3225 | + </property> |
3226 | + <property name="text"> |
3227 | + <string>Lead-in time</string> |
3228 | + </property> |
3229 | + <property name="wordWrap"> |
3230 | + <bool>false</bool> |
3231 | + </property> |
3232 | + </widget> |
3233 | + </item> |
3234 | + <item row="2" column="2"> |
3235 | + <widget class="QLineEdit" name="LeadinTime"> |
3236 | + <property name="sizePolicy"> |
3237 | + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
3238 | + <horstretch>0</horstretch> |
3239 | + <verstretch>0</verstretch> |
3240 | + </sizepolicy> |
3241 | + </property> |
3242 | + </widget> |
3243 | + </item> |
3244 | + <item row="2" column="3"> |
3245 | + <widget class="QLabel" name="TextLabel2_2_2"> |
3246 | + <property name="enabled"> |
3247 | + <bool>false</bool> |
3248 | + </property> |
3249 | + <property name="font"> |
3250 | + <font/> |
3251 | + </property> |
3252 | + <property name="text"> |
3253 | + <string>seconds</string> |
3254 | + </property> |
3255 | + <property name="wordWrap"> |
3256 | + <bool>false</bool> |
3257 | + </property> |
3258 | + </widget> |
3259 | + </item> |
3260 | + <item row="3" column="1"> |
3261 | + <spacer name="horizontalSpacer_2"> |
3262 | + <property name="orientation"> |
3263 | + <enum>Qt::Horizontal</enum> |
3264 | + </property> |
3265 | + <property name="sizeHint" stdset="0"> |
3266 | + <size> |
3267 | + <width>40</width> |
3268 | + <height>20</height> |
3269 | + </size> |
3270 | + </property> |
3271 | + </spacer> |
3272 | + </item> |
3273 | + </layout> |
3274 | + </widget> |
3275 | + </item> |
3276 | + <item row="2" column="0" rowspan="3"> |
3277 | + <widget class="QGroupBox" name="groupBox"> |
3278 | + <property name="enabled"> |
3279 | + <bool>false</bool> |
3280 | + </property> |
3281 | + <property name="sizePolicy"> |
3282 | + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> |
3283 | + <horstretch>0</horstretch> |
3284 | + <verstretch>0</verstretch> |
3285 | + </sizepolicy> |
3286 | + </property> |
3287 | + <property name="maximumSize"> |
3288 | + <size> |
3289 | + <width>16777215</width> |
3290 | + <height>16777215</height> |
3291 | + </size> |
3292 | + </property> |
3293 | + <property name="title"> |
3294 | + <string>Control Mode</string> |
3295 | + </property> |
3296 | + <property name="alignment"> |
3297 | + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> |
3298 | + </property> |
3299 | + <layout class="QVBoxLayout"> |
3300 | + <item> |
3301 | + <widget class="QRadioButton" name="AbsoluteMode"> |
3302 | + <property name="text"> |
3303 | + <string>Absolute Mode</string> |
3304 | + </property> |
3305 | + </widget> |
3306 | + </item> |
3307 | + <item> |
3308 | + <widget class="QRadioButton" name="RelativeMode"> |
3309 | + <property name="text"> |
3310 | + <string>Relative Mode</string> |
3311 | + </property> |
3312 | + </widget> |
3313 | + </item> |
3314 | + <item> |
3315 | + <widget class="QCheckBox" name="NeedleSkipEnable"> |
3316 | + <property name="text"> |
3317 | + <string>Enable Needle Skip Prevention</string> |
3318 | + </property> |
3319 | + </widget> |
3320 | + </item> |
3321 | + <item> |
3322 | + <spacer name="verticalSpacer_3"> |
3323 | + <property name="orientation"> |
3324 | + <enum>Qt::Vertical</enum> |
3325 | + </property> |
3326 | + <property name="sizeHint" stdset="0"> |
3327 | + <size> |
3328 | + <width>20</width> |
3329 | + <height>40</height> |
3330 | + </size> |
3331 | + </property> |
3332 | + </spacer> |
3333 | + </item> |
3334 | + </layout> |
3335 | + </widget> |
3336 | + </item> |
3337 | + <item row="2" column="1" colspan="2"> |
3338 | + <widget class="QGroupBox" name="groupBoxSignalQuality"> |
3339 | + <property name="enabled"> |
3340 | + <bool>false</bool> |
3341 | + </property> |
3342 | + <property name="title"> |
3343 | + <string>Signal Quality</string> |
3344 | + </property> |
3345 | + <property name="alignment"> |
3346 | + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> |
3347 | + </property> |
3348 | + <layout class="QGridLayout"> |
3349 | + <item row="0" column="0"> |
3350 | + <spacer name="verticalSpacer_2"> |
3351 | + <property name="orientation"> |
3352 | + <enum>Qt::Vertical</enum> |
3353 | + </property> |
3354 | + <property name="sizeHint" stdset="0"> |
3355 | + <size> |
3356 | + <width>20</width> |
3357 | + <height>40</height> |
3358 | + </size> |
3359 | + </property> |
3360 | + </spacer> |
3361 | + </item> |
3362 | + </layout> |
3363 | + </widget> |
3364 | + </item> |
3365 | + <item row="3" column="1" colspan="2"> |
3366 | + <widget class="QLabel" name="label"> |
3367 | + <property name="enabled"> |
3368 | + <bool>false</bool> |
3369 | + </property> |
3370 | + <property name="toolTip"> |
3371 | + <string>http://www.xwax.co.uk</string> |
3372 | + </property> |
3373 | + <property name="text"> |
3374 | + <string>Powered by xwax</string> |
3375 | + </property> |
3376 | + <property name="alignment"> |
3377 | + <set>Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing</set> |
3378 | + </property> |
3379 | + </widget> |
3380 | + </item> |
3381 | + <item row="4" column="2"> |
3382 | + <widget class="QPushButton" name="applyButton"> |
3383 | + <property name="enabled"> |
3384 | + <bool>false</bool> |
3385 | + </property> |
3386 | + <property name="sizePolicy"> |
3387 | + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> |
3388 | + <horstretch>0</horstretch> |
3389 | + <verstretch>0</verstretch> |
3390 | + </sizepolicy> |
3391 | + </property> |
3392 | + <property name="sizeHint" stdset="0"> |
3393 | + <size> |
3394 | + <width>85</width> |
3395 | + <height>27</height> |
3396 | + </size> |
3397 | + </property> |
3398 | + <property name="text"> |
3399 | + <string>Apply</string> |
3400 | + </property> |
3401 | + </widget> |
3402 | + </item> |
3403 | + <item row="5" column="2"> |
3404 | + <spacer name="verticalSpacer"> |
3405 | + <property name="orientation"> |
3406 | + <enum>Qt::Vertical</enum> |
3407 | + </property> |
3408 | + <property name="sizeHint" stdset="0"> |
3409 | + <size> |
3410 | + <width>0</width> |
3411 | + <height>0</height> |
3412 | + </size> |
3413 | + </property> |
3414 | + </spacer> |
3415 | + </item> |
3416 | + <item row="5" column="0" colspan="2"> |
3417 | + <widget class="QLabel" name="label_3"> |
3418 | + <property name="text"> |
3419 | + <string><b>This version of Mixxx does not support vinyl control.</b> <br> Please visit <a href="http://mixxx.org">Mixxx.org</a> for more information.</string> |
3420 | + </property> |
3421 | + <property name="wordWrap"> |
3422 | + <bool>true</bool> |
3423 | + </property> |
3424 | + </widget> |
3425 | + </item> |
3426 | + </layout> |
3427 | + </widget> |
3428 | + <layoutdefault spacing="6" margin="11"/> |
3429 | + <resources/> |
3430 | + <connections/> |
3431 | +</ui> |
3432 | |
3433 | === modified file 'mixxx/src/dlgprefsound.cpp' |
3434 | --- mixxx/src/dlgprefsound.cpp 2011-04-08 06:04:27 +0000 |
3435 | +++ mixxx/src/dlgprefsound.cpp 2011-04-16 23:24:33 +0000 |
3436 | @@ -120,8 +120,11 @@ |
3437 | #ifdef __VINYLCONTROL__ |
3438 | // Scratchlib sucks, throw rocks at it |
3439 | // XXX(bkgood) HACKS DELETE THIS WHEN SCRATCHLIB GETS NUKED KTHX |
3440 | - if (m_pConfig->getValueString(ConfigKey("[VinylControl]", "strVinylType")) |
3441 | - == MIXXX_VINYL_FINALSCRATCH && |
3442 | + if ((m_pConfig->getValueString(ConfigKey("[Channel1]", "vinylcontrol_vinyl_type")) |
3443 | + == MIXXX_VINYL_FINALSCRATCH || |
3444 | + m_pConfig->getValueString(ConfigKey("[Channel2]", "vinylcontrol_vinyl_type")) |
3445 | + == MIXXX_VINYL_FINALSCRATCH) |
3446 | + && |
3447 | sampleRateComboBox->itemData(sampleRateComboBox->currentIndex()).toUInt() |
3448 | != 44100) { |
3449 | QMessageBox::warning(this, tr("Mixxx Error"), |
3450 | |
3451 | === modified file 'mixxx/src/dlgprefvinyl.cpp' |
3452 | --- mixxx/src/dlgprefvinyl.cpp 2011-03-30 06:00:45 +0000 |
3453 | +++ mixxx/src/dlgprefvinyl.cpp 2011-04-16 23:24:33 +0000 |
3454 | @@ -31,7 +31,7 @@ |
3455 | |
3456 | DlgPrefVinyl::DlgPrefVinyl(QWidget * parent, SoundManager * soundman, |
3457 | ConfigObject<ConfigValue> * _config) : QWidget(parent), Ui::DlgPrefVinylDlg(), |
3458 | - m_COTMode(ControlObject::getControl(ConfigKey("[VinylControl]", "Mode"))) |
3459 | + m_COTMode(ControlObject::getControl(ConfigKey("[VinylControl]", "mode"))) |
3460 | { |
3461 | m_pSoundManager = soundman; |
3462 | config = _config; |
3463 | @@ -43,29 +43,9 @@ |
3464 | QButtonGroup vinylControlMode; |
3465 | vinylControlMode.addButton(AbsoluteMode); |
3466 | vinylControlMode.addButton(RelativeMode); |
3467 | - vinylControlMode.addButton(ScratchMode); |
3468 | - |
3469 | - //Get access to the timecode strength ControlObjects |
3470 | - m_timecodeQuality1 = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Channel1]", "VinylControlQuality"))); |
3471 | - m_timecodeQuality2 = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Channel2]", "VinylControlQuality"))); |
3472 | - |
3473 | - m_vinylControlInput1L = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Channel1]", "VinylControlInputL"))); |
3474 | - m_vinylControlInput1R = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Channel1]", "VinylControlInputR"))); |
3475 | - m_vinylControlInput2L = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Channel2]", "VinylControlInputL"))); |
3476 | - m_vinylControlInput2R = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Channel2]", "VinylControlInputR"))); |
3477 | - |
3478 | - |
3479 | - |
3480 | - m_signalWidget1.setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); |
3481 | - m_signalWidget2.setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); |
3482 | - const unsigned signalWidgetWidth = 75; |
3483 | - const unsigned signalWidgetHeight = 75; |
3484 | - m_signalWidget1.setMinimumSize(signalWidgetWidth, signalWidgetHeight); |
3485 | - m_signalWidget2.setMinimumSize(signalWidgetWidth, signalWidgetHeight); |
3486 | - m_signalWidget1.setMaximumSize(signalWidgetWidth, signalWidgetHeight); |
3487 | - m_signalWidget2.setMaximumSize(signalWidgetWidth, signalWidgetHeight); |
3488 | - m_signalWidget1.setupWidget(); |
3489 | - m_signalWidget2.setupWidget(); |
3490 | + |
3491 | + m_signalWidget1.setSize(MIXXX_VINYL_SCOPE_SIZE); |
3492 | + m_signalWidget2.setSize(MIXXX_VINYL_SCOPE_SIZE); |
3493 | |
3494 | delete groupBoxSignalQuality->layout(); |
3495 | QHBoxLayout *layout = new QHBoxLayout; |
3496 | @@ -74,22 +54,38 @@ |
3497 | groupBoxSignalQuality->setLayout(layout); |
3498 | |
3499 | // Add vinyl types |
3500 | - ComboBoxVinylType->addItem(MIXXX_VINYL_SERATOCV02VINYLSIDEA); |
3501 | - ComboBoxVinylType->addItem(MIXXX_VINYL_SERATOCV02VINYLSIDEB); |
3502 | - ComboBoxVinylType->addItem(MIXXX_VINYL_SERATOCD); |
3503 | - ComboBoxVinylType->addItem(MIXXX_VINYL_TRAKTORSCRATCHSIDEA); |
3504 | - ComboBoxVinylType->addItem(MIXXX_VINYL_TRAKTORSCRATCHSIDEB); |
3505 | - |
3506 | + ComboBoxVinylType1->addItem(MIXXX_VINYL_SERATOCV02VINYLSIDEA); |
3507 | + ComboBoxVinylType1->addItem(MIXXX_VINYL_SERATOCV02VINYLSIDEB); |
3508 | + ComboBoxVinylType1->addItem(MIXXX_VINYL_SERATOCD); |
3509 | + ComboBoxVinylType1->addItem(MIXXX_VINYL_TRAKTORSCRATCHSIDEA); |
3510 | + ComboBoxVinylType1->addItem(MIXXX_VINYL_TRAKTORSCRATCHSIDEB); |
3511 | + |
3512 | + ComboBoxVinylType2->addItem(MIXXX_VINYL_SERATOCV02VINYLSIDEA); |
3513 | + ComboBoxVinylType2->addItem(MIXXX_VINYL_SERATOCV02VINYLSIDEB); |
3514 | + ComboBoxVinylType2->addItem(MIXXX_VINYL_SERATOCD); |
3515 | + ComboBoxVinylType2->addItem(MIXXX_VINYL_TRAKTORSCRATCHSIDEA); |
3516 | + ComboBoxVinylType2->addItem(MIXXX_VINYL_TRAKTORSCRATCHSIDEB); |
3517 | + |
3518 | + ComboBoxVinylSpeed1->addItem(MIXXX_VINYL_SPEED_33); |
3519 | + ComboBoxVinylSpeed1->addItem(MIXXX_VINYL_SPEED_45); |
3520 | + |
3521 | + ComboBoxVinylSpeed2->addItem(MIXXX_VINYL_SPEED_33); |
3522 | + ComboBoxVinylSpeed2->addItem(MIXXX_VINYL_SPEED_45); |
3523 | + |
3524 | + connect(applyButton, SIGNAL(clicked()), this, SLOT(slotApply())); |
3525 | connect(VinylGain, SIGNAL(sliderReleased()), this, SLOT(VinylGainSlotApply())); |
3526 | //connect(ComboBoxDeviceDeck1, SIGNAL(currentIndexChanged()), this, SLOT(())); |
3527 | |
3528 | connect(VinylGain, SIGNAL(sliderReleased()), this, SLOT(settingsChanged())); |
3529 | - connect(ComboBoxVinylType, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsChanged())); |
3530 | + connect(ComboBoxVinylType1, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsChanged())); |
3531 | + connect(ComboBoxVinylType2, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsChanged())); |
3532 | + connect(ComboBoxVinylSpeed1, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsChanged())); |
3533 | + connect(ComboBoxVinylSpeed2, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsChanged())); |
3534 | connect(LeadinTime, SIGNAL(textChanged(const QString&)), this, SLOT(settingsChanged())); |
3535 | connect(NeedleSkipEnable, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged())); |
3536 | connect(AbsoluteMode, SIGNAL(toggled(bool)), this, SLOT(settingsChanged())); |
3537 | connect(RelativeMode, SIGNAL(toggled(bool)), this, SLOT(settingsChanged())); |
3538 | - connect(ScratchMode, SIGNAL(toggled(bool)), this, SLOT(settingsChanged())); |
3539 | + connect(m_pSoundManager, SIGNAL(devicesSetup()), this, SLOT(settingsChanged())); |
3540 | } |
3541 | |
3542 | DlgPrefVinyl::~DlgPrefVinyl() |
3543 | @@ -99,14 +95,11 @@ |
3544 | /** @brief Performs any necessary actions that need to happen when the prefs dialog is opened */ |
3545 | void DlgPrefVinyl::slotShow() |
3546 | { |
3547 | - //Connect the signal quality ControlObjects to this dialog, so they start updating |
3548 | - connect(m_timecodeQuality1, SIGNAL(valueChanged(double)), this, SLOT(updateSignalQuality1(double))); |
3549 | - connect(m_timecodeQuality2, SIGNAL(valueChanged(double)), this, SLOT(updateSignalQuality2(double))); |
3550 | - |
3551 | - connect(m_vinylControlInput1L, SIGNAL(valueChanged(double)), this, SLOT(updateInputLevelLeft1(double))); |
3552 | - connect(m_vinylControlInput1R, SIGNAL(valueChanged(double)), this, SLOT(updateInputLevelRight1(double))); |
3553 | - connect(m_vinylControlInput2L, SIGNAL(valueChanged(double)), this, SLOT(updateInputLevelLeft2(double))); |
3554 | - connect(m_vinylControlInput2R, SIGNAL(valueChanged(double)), this, SLOT(updateInputLevelRight2(double))); |
3555 | + QList<VinylControlProxy*> VCProxiesList = m_pSoundManager->getVinylControlProxies(); |
3556 | + if (VCProxiesList.value(0) != NULL) |
3557 | + m_signalWidget1.setVinylControlProxy(VCProxiesList.value(0)); |
3558 | + if (VCProxiesList.value(1) != NULL) |
3559 | + m_signalWidget2.setVinylControlProxy(VCProxiesList.value(1)); |
3560 | |
3561 | //(Re)Initialize the signal quality indicators |
3562 | m_signalWidget1.resetWidget(); |
3563 | @@ -122,12 +115,6 @@ |
3564 | //Stop updating the vinyl control signal indicators when the prefs dialog is closed. |
3565 | m_signalWidget1.stopDrawing(); |
3566 | m_signalWidget2.stopDrawing(); |
3567 | - m_timecodeQuality1->disconnect(this); |
3568 | - m_timecodeQuality2->disconnect(this); |
3569 | - m_vinylControlInput1L->disconnect(this); |
3570 | - m_vinylControlInput1R->disconnect(this); |
3571 | - m_vinylControlInput2L->disconnect(this); |
3572 | - m_vinylControlInput2R->disconnect(this); |
3573 | } |
3574 | |
3575 | void DlgPrefVinyl::slotUpdate() |
3576 | @@ -135,33 +122,44 @@ |
3577 | m_dontForce = true; // otherwise all the signals fired in here will cause |
3578 | // DlgPrefSound to call setupDevices needlessly :) -- bkgood |
3579 | // Set vinyl control types in the comboboxes |
3580 | - int combo_index = ComboBoxVinylType->findText(config->getValueString(ConfigKey("[VinylControl]","strVinylType"))); |
3581 | - if (combo_index != -1) |
3582 | - ComboBoxVinylType->setCurrentIndex(combo_index); |
3583 | + int combo_index = ComboBoxVinylType1->findText(config->getValueString(ConfigKey("[Channel1]","vinylcontrol_vinyl_type"))); |
3584 | + if (combo_index != -1) |
3585 | + ComboBoxVinylType1->setCurrentIndex(combo_index); |
3586 | + |
3587 | + combo_index = ComboBoxVinylType2->findText(config->getValueString(ConfigKey("[Channel2]","vinylcontrol_vinyl_type"))); |
3588 | + if (combo_index != -1) |
3589 | + ComboBoxVinylType2->setCurrentIndex(combo_index); |
3590 | + |
3591 | + combo_index = ComboBoxVinylSpeed1->findText(config->getValueString(ConfigKey("[Channel1]","vinylcontrol_speed_type"))); |
3592 | + if (combo_index != -1) |
3593 | + ComboBoxVinylSpeed1->setCurrentIndex(combo_index); |
3594 | + |
3595 | + combo_index = ComboBoxVinylSpeed2->findText(config->getValueString(ConfigKey("[Channel2]","vinylcontrol_speed_type"))); |
3596 | + if (combo_index != -1) |
3597 | + ComboBoxVinylSpeed2->setCurrentIndex(combo_index); |
3598 | |
3599 | // set lead-in time |
3600 | - LeadinTime->setText (config->getValueString(ConfigKey("[VinylControl]","LeadInTime")) ); |
3601 | + LeadinTime->setText (config->getValueString(ConfigKey("[VinylControl]","lead_in_time")) ); |
3602 | |
3603 | // set Relative mode |
3604 | - int iMode = config->getValueString(ConfigKey("[VinylControl]","Mode")).toInt(); |
3605 | + int iMode = config->getValueString(ConfigKey("[VinylControl]","mode")).toInt(); |
3606 | if (iMode == MIXXX_VCMODE_ABSOLUTE) |
3607 | AbsoluteMode->setChecked(true); |
3608 | else if (iMode == MIXXX_VCMODE_RELATIVE) |
3609 | RelativeMode->setChecked(true); |
3610 | - else if (iMode == MIXXX_VCMODE_SCRATCH) |
3611 | - ScratchMode->setChecked(true); |
3612 | |
3613 | // Honour the Needle Skip Prevention setting. |
3614 | - NeedleSkipEnable->setChecked( (bool)config->getValueString( ConfigKey("[VinylControl]", "NeedleSkipPrevention") ).toInt() ); |
3615 | + NeedleSkipEnable->setChecked( (bool)config->getValueString( ConfigKey("[VinylControl]", "needle_skip_prevention") ).toInt() ); |
3616 | |
3617 | //set vinyl control gain |
3618 | - VinylGain->setValue( config->getValueString(ConfigKey("[VinylControl]","VinylControlGain")).toInt()); |
3619 | + VinylGain->setValue( config->getValueString(ConfigKey("[VinylControl]","gain")).toInt()); |
3620 | m_dontForce = false; |
3621 | } |
3622 | |
3623 | // Update the config object with parameters from dialog |
3624 | void DlgPrefVinyl::slotApply() |
3625 | { |
3626 | + QString device2; |
3627 | qDebug() << "DlgPrefVinyl::Apply"; |
3628 | |
3629 | // Lead-in time |
3630 | @@ -169,9 +167,9 @@ |
3631 | bool isInteger; |
3632 | strLeadIn.toInt(&isInteger); |
3633 | if (isInteger) |
3634 | - config->set(ConfigKey("[VinylControl]","LeadInTime"), strLeadIn); |
3635 | + config->set(ConfigKey("[VinylControl]","lead_in_time"), strLeadIn); |
3636 | else |
3637 | - config->set(ConfigKey("[VinylControl]","LeadInTime"), MIXXX_VC_DEFAULT_LEADINTIME); |
3638 | + config->set(ConfigKey("[VinylControl]","lead_in_time"), MIXXX_VC_DEFAULT_LEADINTIME); |
3639 | |
3640 | //Apply updates for everything else... |
3641 | VinylTypeSlotApply(); |
3642 | @@ -182,12 +180,12 @@ |
3643 | iMode = MIXXX_VCMODE_ABSOLUTE; |
3644 | if (RelativeMode->isChecked()) |
3645 | iMode = MIXXX_VCMODE_RELATIVE; |
3646 | - if (ScratchMode->isChecked()) |
3647 | - iMode = MIXXX_VCMODE_SCRATCH; |
3648 | |
3649 | + ControlObject::getControl(ConfigKey("[Channel1]", "vinylcontrol_mode"))->set(iMode); |
3650 | + ControlObject::getControl(ConfigKey("[Channel2]", "vinylcontrol_mode"))->set(iMode); |
3651 | m_COTMode.slotSet(iMode); |
3652 | - config->set(ConfigKey("[VinylControl]","Mode"), ConfigValue(iMode)); |
3653 | - config->set(ConfigKey("[VinylControl]","NeedleSkipPrevention" ), ConfigValue( (int)(NeedleSkipEnable->isChecked( )) ) ); |
3654 | + config->set(ConfigKey("[VinylControl]","mode"), ConfigValue(iMode)); |
3655 | + config->set(ConfigKey("[VinylControl]","needle_skip_prevention" ), ConfigValue( (int)(NeedleSkipEnable->isChecked( )) ) ); |
3656 | |
3657 | slotUpdate(); |
3658 | } |
3659 | @@ -197,80 +195,39 @@ |
3660 | |
3661 | } |
3662 | |
3663 | -void DlgPrefVinyl::EnableScratchModeSlotApply() |
3664 | -{ |
3665 | - |
3666 | -} |
3667 | - |
3668 | void DlgPrefVinyl::VinylTypeSlotApply() |
3669 | { |
3670 | - config->set(ConfigKey("[VinylControl]","strVinylType"), ConfigValue(ComboBoxVinylType->currentText())); |
3671 | + config->set(ConfigKey("[Channel1]","vinylcontrol_vinyl_type"), ConfigValue(ComboBoxVinylType1->currentText())); |
3672 | + config->set(ConfigKey("[Channel2]","vinylcontrol_vinyl_type"), ConfigValue(ComboBoxVinylType2->currentText())); |
3673 | + config->set(ConfigKey("[Channel1]","vinylcontrol_speed_type"), ConfigValue(ComboBoxVinylSpeed1->currentText())); |
3674 | + config->set(ConfigKey("[Channel2]","vinylcontrol_speed_type"), ConfigValue(ComboBoxVinylSpeed2->currentText())); |
3675 | + emit(applySound()); |
3676 | } |
3677 | |
3678 | void DlgPrefVinyl::VinylGainSlotApply() |
3679 | { |
3680 | qDebug() << "in VinylGainSlotApply()" << "with gain:" << VinylGain->value(); |
3681 | //Update the config key... |
3682 | - config->set(ConfigKey("[VinylControl]","VinylControlGain"), ConfigValue(VinylGain->value())); |
3683 | + config->set(ConfigKey("[VinylControl]","gain"), ConfigValue(VinylGain->value())); |
3684 | |
3685 | //Update the ControlObject... |
3686 | - ControlObject* pControlObjectVinylControlGain = ControlObject::getControl(ConfigKey("[VinylControl]", "VinylControlGain")); |
3687 | + ControlObject* pControlObjectVinylControlGain = ControlObject::getControl(ConfigKey("[VinylControl]", "gain")); |
3688 | pControlObjectVinylControlGain->set(VinylGain->value()); |
3689 | - |
3690 | - //qDebug() << "Setting Gain Text"; |
3691 | - //gain->setText(config->getValueString(ConfigKey("[VinylControl]","VinylControlGain"))); //this is probably ineffecient... |
3692 | -} |
3693 | - |
3694 | -/** @brief Wraps updateSignalQuality to work nicely with slots |
3695 | - * @param value The new signal quality level for channel 1 (0.0f-1.0f) |
3696 | - */ |
3697 | -void DlgPrefVinyl::updateSignalQuality1(double value) |
3698 | -{ |
3699 | - m_signalWidget1.updateSignalQuality(VINYLCONTROL_SIGQUALITY, value); |
3700 | -} |
3701 | - |
3702 | -/** @brief Wraps updateSignalQuality to work nicely with slots |
3703 | - * @param value The new signal quality level for channel 2 (0.0f-1.0f) |
3704 | - */ |
3705 | -void DlgPrefVinyl::updateSignalQuality2(double value) |
3706 | -{ |
3707 | - m_signalWidget2.updateSignalQuality(VINYLCONTROL_SIGQUALITY, value); |
3708 | -} |
3709 | - |
3710 | -/** @brief Wraps updateSignalQuality to work nicely with slots |
3711 | - * @param value The new input level for the left channel of the first deck (0.0f-1.0f) |
3712 | - */ |
3713 | -void DlgPrefVinyl::updateInputLevelLeft1(double value) |
3714 | -{ |
3715 | - m_signalWidget1.updateSignalQuality(VINYLCONTROL_SIGLEFTCHANNEL, value); |
3716 | -} |
3717 | - |
3718 | -/** @brief Wraps updateSignalQuality to work nicely with slots |
3719 | - * @param value The new input level for the right channel of the first deck (0.0f-1.0f) |
3720 | - */ |
3721 | -void DlgPrefVinyl::updateInputLevelRight1(double value) |
3722 | -{ |
3723 | - m_signalWidget1.updateSignalQuality(VINYLCONTROL_SIGRIGHTCHANNEL, value); |
3724 | -} |
3725 | - |
3726 | -/** @brief Wraps updateSignalQuality to work nicely with slots |
3727 | - * @param value The new input level for the left channel of the second deck (0.0f-1.0f) |
3728 | - */ |
3729 | -void DlgPrefVinyl::updateInputLevelLeft2(double value) |
3730 | -{ |
3731 | - m_signalWidget2.updateSignalQuality(VINYLCONTROL_SIGLEFTCHANNEL, value); |
3732 | -} |
3733 | - |
3734 | -/** @brief Wraps updateSignalQuality to work nicely with slots |
3735 | - * @param value The new input level for the right channel of the second deck (0.0f-1.0f) |
3736 | - */ |
3737 | -void DlgPrefVinyl::updateInputLevelRight2(double value) |
3738 | -{ |
3739 | - m_signalWidget2.updateSignalQuality(VINYLCONTROL_SIGRIGHTCHANNEL, value); |
3740 | } |
3741 | |
3742 | void DlgPrefVinyl::settingsChanged() { |
3743 | if (!m_dontForce) { |
3744 | emit(refreshVCProxies()); |
3745 | } |
3746 | + |
3747 | + QList<VinylControlProxy*> VCProxiesList = m_pSoundManager->getVinylControlProxies(); |
3748 | + if (VCProxiesList.value(0) != NULL) { |
3749 | + m_signalWidget1.setVinylControlProxy(VCProxiesList.value(0)); |
3750 | + } |
3751 | + if (VCProxiesList.value(1) != NULL) { |
3752 | + m_signalWidget2.setVinylControlProxy(VCProxiesList.value(1)); |
3753 | + } |
3754 | + |
3755 | + m_signalWidget1.setVinylActive(m_pSoundManager->hasVinylInput(0)); |
3756 | + m_signalWidget2.setVinylActive(m_pSoundManager->hasVinylInput(1)); |
3757 | } |
3758 | |
3759 | === modified file 'mixxx/src/dlgprefvinyl.h' |
3760 | --- mixxx/src/dlgprefvinyl.h 2011-03-30 06:00:45 +0000 |
3761 | +++ mixxx/src/dlgprefvinyl.h 2011-04-16 23:24:33 +0000 |
3762 | @@ -44,22 +44,16 @@ |
3763 | /** Update widget */ |
3764 | void slotUpdate(); |
3765 | void slotApply(); |
3766 | - void EnableRelativeModeSlotApply(); |
3767 | - void EnableScratchModeSlotApply(); |
3768 | - void VinylTypeSlotApply(); |
3769 | + void EnableRelativeModeSlotApply(); |
3770 | + void VinylTypeSlotApply(); |
3771 | void VinylGainSlotApply(); |
3772 | - void updateSignalQuality1(double value); |
3773 | - void updateSignalQuality2(double value); |
3774 | - void updateInputLevelLeft1(double value); |
3775 | - void updateInputLevelRight1(double value); |
3776 | - void updateInputLevelLeft2(double value); |
3777 | - void updateInputLevelRight2(double value); |
3778 | void slotClose(); |
3779 | void slotShow(); |
3780 | |
3781 | signals: |
3782 | void apply(); |
3783 | void refreshVCProxies(); |
3784 | + void applySound(); |
3785 | private slots: |
3786 | void settingsChanged(); |
3787 | private: |
3788 | |
3789 | === modified file 'mixxx/src/dlgprefvinyldlg.ui' |
3790 | --- mixxx/src/dlgprefvinyldlg.ui 2010-11-29 21:23:11 +0000 |
3791 | +++ mixxx/src/dlgprefvinyldlg.ui 2011-04-16 23:24:33 +0000 |
3792 | @@ -14,7 +14,7 @@ |
3793 | <string>Form1</string> |
3794 | </property> |
3795 | <layout class="QGridLayout" name="gridLayout_3"> |
3796 | - <item row="0" column="0" colspan="2"> |
3797 | + <item row="0" column="0" colspan="3"> |
3798 | <widget class="QGroupBox" name="groupBox_2"> |
3799 | <property name="title"> |
3800 | <string>Input</string> |
3801 | @@ -128,21 +128,16 @@ |
3802 | </layout> |
3803 | </item> |
3804 | </layout> |
3805 | - <zorder>devicesLabel</zorder> |
3806 | - <zorder>label_2</zorder> |
3807 | - <zorder>horizontalLayoutWidget</zorder> |
3808 | - <zorder>textLabel1_3</zorder> |
3809 | - <zorder>horizontalLayoutWidget_2</zorder> |
3810 | </widget> |
3811 | </item> |
3812 | - <item row="1" column="0" colspan="2"> |
3813 | + <item row="1" column="0" colspan="3"> |
3814 | <widget class="QGroupBox" name="groupBox_3"> |
3815 | <property name="title"> |
3816 | <string>Vinyl Configuration</string> |
3817 | </property> |
3818 | <layout class="QGridLayout" name="gridLayout_2"> |
3819 | <item row="0" column="0"> |
3820 | - <widget class="QLabel" name="TextLabel2"> |
3821 | + <widget class="QLabel" name="TextLabel29"> |
3822 | <property name="enabled"> |
3823 | <bool>true</bool> |
3824 | </property> |
3825 | @@ -156,15 +151,31 @@ |
3826 | <font/> |
3827 | </property> |
3828 | <property name="text"> |
3829 | - <string>Vinyl Type</string> |
3830 | + <string>Deck 1 Vinyl Type</string> |
3831 | </property> |
3832 | <property name="wordWrap"> |
3833 | <bool>false</bool> |
3834 | </property> |
3835 | </widget> |
3836 | </item> |
3837 | - <item row="0" column="2" colspan="2"> |
3838 | - <widget class="QComboBox" name="ComboBoxVinylType"> |
3839 | + <item row="0" column="2"> |
3840 | + <widget class="QComboBox" name="ComboBoxVinylType1"> |
3841 | + <property name="enabled"> |
3842 | + <bool>true</bool> |
3843 | + </property> |
3844 | + <property name="sizePolicy"> |
3845 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
3846 | + <horstretch>0</horstretch> |
3847 | + <verstretch>0</verstretch> |
3848 | + </sizepolicy> |
3849 | + </property> |
3850 | + <property name="font"> |
3851 | + <font/> |
3852 | + </property> |
3853 | + </widget> |
3854 | + </item> |
3855 | + <item row="0" column="3"> |
3856 | + <widget class="QComboBox" name="ComboBoxVinylSpeed1"> |
3857 | <property name="enabled"> |
3858 | <bool>true</bool> |
3859 | </property> |
3860 | @@ -180,6 +191,60 @@ |
3861 | </widget> |
3862 | </item> |
3863 | <item row="1" column="0"> |
3864 | + <widget class="QLabel" name="TextLabel28"> |
3865 | + <property name="enabled"> |
3866 | + <bool>true</bool> |
3867 | + </property> |
3868 | + <property name="sizePolicy"> |
3869 | + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> |
3870 | + <horstretch>0</horstretch> |
3871 | + <verstretch>0</verstretch> |
3872 | + </sizepolicy> |
3873 | + </property> |
3874 | + <property name="font"> |
3875 | + <font/> |
3876 | + </property> |
3877 | + <property name="text"> |
3878 | + <string>Deck 2 Vinyl Type</string> |
3879 | + </property> |
3880 | + <property name="wordWrap"> |
3881 | + <bool>false</bool> |
3882 | + </property> |
3883 | + </widget> |
3884 | + </item> |
3885 | + <item row="1" column="2"> |
3886 | + <widget class="QComboBox" name="ComboBoxVinylType2"> |
3887 | + <property name="enabled"> |
3888 | + <bool>true</bool> |
3889 | + </property> |
3890 | + <property name="sizePolicy"> |
3891 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
3892 | + <horstretch>0</horstretch> |
3893 | + <verstretch>0</verstretch> |
3894 | + </sizepolicy> |
3895 | + </property> |
3896 | + <property name="font"> |
3897 | + <font/> |
3898 | + </property> |
3899 | + </widget> |
3900 | + </item> |
3901 | + <item row="1" column="3"> |
3902 | + <widget class="QComboBox" name="ComboBoxVinylSpeed2"> |
3903 | + <property name="enabled"> |
3904 | + <bool>true</bool> |
3905 | + </property> |
3906 | + <property name="sizePolicy"> |
3907 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
3908 | + <horstretch>0</horstretch> |
3909 | + <verstretch>0</verstretch> |
3910 | + </sizepolicy> |
3911 | + </property> |
3912 | + <property name="font"> |
3913 | + <font/> |
3914 | + </property> |
3915 | + </widget> |
3916 | + </item> |
3917 | + <item row="2" column="0"> |
3918 | <widget class="QLabel" name="TextLabel2_2"> |
3919 | <property name="enabled"> |
3920 | <bool>true</bool> |
3921 | @@ -201,7 +266,7 @@ |
3922 | </property> |
3923 | </widget> |
3924 | </item> |
3925 | - <item row="1" column="2"> |
3926 | + <item row="2" column="2"> |
3927 | <widget class="QLineEdit" name="LeadinTime"> |
3928 | <property name="sizePolicy"> |
3929 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
3930 | @@ -211,7 +276,7 @@ |
3931 | </property> |
3932 | </widget> |
3933 | </item> |
3934 | - <item row="1" column="3"> |
3935 | + <item row="2" column="3"> |
3936 | <widget class="QLabel" name="TextLabel2_2_2"> |
3937 | <property name="enabled"> |
3938 | <bool>true</bool> |
3939 | @@ -227,7 +292,7 @@ |
3940 | </property> |
3941 | </widget> |
3942 | </item> |
3943 | - <item row="1" column="1"> |
3944 | + <item row="3" column="1"> |
3945 | <spacer name="horizontalSpacer_2"> |
3946 | <property name="orientation"> |
3947 | <enum>Qt::Horizontal</enum> |
3948 | @@ -243,17 +308,26 @@ |
3949 | </layout> |
3950 | </widget> |
3951 | </item> |
3952 | - <item row="2" column="0" rowspan="2"> |
3953 | + <item row="2" column="0" rowspan="3"> |
3954 | <widget class="QGroupBox" name="groupBox"> |
3955 | <property name="sizePolicy"> |
3956 | - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |
3957 | + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> |
3958 | <horstretch>0</horstretch> |
3959 | <verstretch>0</verstretch> |
3960 | </sizepolicy> |
3961 | </property> |
3962 | + <property name="maximumSize"> |
3963 | + <size> |
3964 | + <width>16777215</width> |
3965 | + <height>16777215</height> |
3966 | + </size> |
3967 | + </property> |
3968 | <property name="title"> |
3969 | <string>Control Mode</string> |
3970 | </property> |
3971 | + <property name="alignment"> |
3972 | + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> |
3973 | + </property> |
3974 | <layout class="QVBoxLayout"> |
3975 | <item> |
3976 | <widget class="QRadioButton" name="AbsoluteMode"> |
3977 | @@ -270,27 +344,36 @@ |
3978 | </widget> |
3979 | </item> |
3980 | <item> |
3981 | - <widget class="QRadioButton" name="ScratchMode"> |
3982 | - <property name="text"> |
3983 | - <string>Scratch Mode</string> |
3984 | - </property> |
3985 | - </widget> |
3986 | - </item> |
3987 | - <item> |
3988 | <widget class="QCheckBox" name="NeedleSkipEnable"> |
3989 | <property name="text"> |
3990 | <string>Enable Needle Skip Prevention</string> |
3991 | </property> |
3992 | </widget> |
3993 | </item> |
3994 | + <item> |
3995 | + <spacer name="verticalSpacer_3"> |
3996 | + <property name="orientation"> |
3997 | + <enum>Qt::Vertical</enum> |
3998 | + </property> |
3999 | + <property name="sizeHint" stdset="0"> |
4000 | + <size> |
4001 | + <width>20</width> |
4002 | + <height>40</height> |
4003 | + </size> |
4004 | + </property> |
4005 | + </spacer> |
4006 | + </item> |
4007 | </layout> |
4008 | </widget> |
4009 | </item> |
4010 | - <item row="2" column="1"> |
4011 | + <item row="2" column="1" colspan="2"> |
4012 | <widget class="QGroupBox" name="groupBoxSignalQuality"> |
4013 | <property name="title"> |
4014 | <string>Signal Quality</string> |
4015 | </property> |
4016 | + <property name="alignment"> |
4017 | + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> |
4018 | + </property> |
4019 | <layout class="QGridLayout"> |
4020 | <item row="0" column="0"> |
4021 | <spacer name="verticalSpacer_2"> |
4022 | @@ -308,7 +391,7 @@ |
4023 | </layout> |
4024 | </widget> |
4025 | </item> |
4026 | - <item row="3" column="1"> |
4027 | + <item row="3" column="1" colspan="2"> |
4028 | <widget class="QLabel" name="label"> |
4029 | <property name="enabled"> |
4030 | <bool>false</bool> |
4031 | @@ -324,15 +407,34 @@ |
4032 | </property> |
4033 | </widget> |
4034 | </item> |
4035 | - <item row="4" column="1"> |
4036 | + <item row="4" column="2"> |
4037 | + <widget class="QPushButton" name="applyButton"> |
4038 | + <property name="sizePolicy"> |
4039 | + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> |
4040 | + <horstretch>0</horstretch> |
4041 | + <verstretch>0</verstretch> |
4042 | + </sizepolicy> |
4043 | + </property> |
4044 | + <property name="sizeHint" stdset="0"> |
4045 | + <size> |
4046 | + <width>85</width> |
4047 | + <height>27</height> |
4048 | + </size> |
4049 | + </property> |
4050 | + <property name="text"> |
4051 | + <string>Apply</string> |
4052 | + </property> |
4053 | + </widget> |
4054 | + </item> |
4055 | + <item row="5" column="2"> |
4056 | <spacer name="verticalSpacer"> |
4057 | <property name="orientation"> |
4058 | <enum>Qt::Vertical</enum> |
4059 | </property> |
4060 | <property name="sizeHint" stdset="0"> |
4061 | <size> |
4062 | - <width>51</width> |
4063 | - <height>59</height> |
4064 | + <width>0</width> |
4065 | + <height>0</height> |
4066 | </size> |
4067 | </property> |
4068 | </spacer> |
4069 | |
4070 | === modified file 'mixxx/src/engine/bpmcontrol.cpp' |
4071 | --- mixxx/src/engine/bpmcontrol.cpp 2011-04-03 20:51:04 +0000 |
4072 | +++ mixxx/src/engine/bpmcontrol.cpp 2011-04-16 23:24:33 +0000 |
4073 | @@ -78,7 +78,7 @@ |
4074 | } |
4075 | |
4076 | void BpmControl::slotFileBpmChanged(double bpm) { |
4077 | - qDebug() << this << "slotFileBpmChanged" << bpm; |
4078 | + //qDebug() << this << "slotFileBpmChanged" << bpm; |
4079 | // Adjust the file-bpm with the current setting of the rate to get the |
4080 | // engine BPM. |
4081 | double dRate = 1.0 + m_pRateDir->get() * m_pRateRange->get() * m_pRateSlider->get(); |
4082 | |
4083 | === modified file 'mixxx/src/engine/enginebuffer.cpp' |
4084 | --- mixxx/src/engine/enginebuffer.cpp 2011-04-07 05:08:22 +0000 |
4085 | +++ mixxx/src/engine/enginebuffer.cpp 2011-04-16 23:24:33 +0000 |
4086 | @@ -39,6 +39,11 @@ |
4087 | #include "engine/quantizecontrol.h" |
4088 | |
4089 | |
4090 | +#ifdef __VINYLCONTROL__ |
4091 | +#include "vinylcontrol.h" |
4092 | +#include "library/dao/cue.h" |
4093 | +#endif |
4094 | + |
4095 | #include "trackinfoobject.h" |
4096 | |
4097 | #ifdef _MSC_VER |
4098 | @@ -72,8 +77,11 @@ |
4099 | m_pScaleLinear(NULL), |
4100 | m_pScaleST(NULL), |
4101 | m_bScalerChanged(false), |
4102 | - m_fLastSampleValue(0.), |
4103 | - m_bLastBufferPaused(true) { |
4104 | + m_bLastBufferPaused(true), |
4105 | + m_fRampValue(0.0) { |
4106 | + |
4107 | + m_fLastSampleValue[0] = 0; |
4108 | + m_fLastSampleValue[1] = 0; |
4109 | |
4110 | m_pReader = new CachingReader(_group, _config); |
4111 | connect(m_pReader, SIGNAL(trackLoaded(TrackPointer, int, int)), |
4112 | @@ -83,7 +91,6 @@ |
4113 | this, SLOT(slotTrackLoadFailed(TrackPointer, QString)), |
4114 | Qt::DirectConnection); |
4115 | |
4116 | - |
4117 | // Play button |
4118 | playButton = new ControlPushButton(ConfigKey(group, "play")); |
4119 | playButton->setToggleButton(true); |
4120 | @@ -126,14 +133,15 @@ |
4121 | rateEngine = new ControlObject(ConfigKey(group, "rateEngine")); |
4122 | |
4123 | // Slider to show and change song position |
4124 | - playposSlider = new ControlPotmeter(ConfigKey(group, "playposition"), 0., 1.); |
4125 | + //these bizarre choices map conveniently to the 0-127 range of midi |
4126 | + playposSlider = new ControlPotmeter(ConfigKey(group, "playposition"), -0.14, 1.14); |
4127 | connect(playposSlider, SIGNAL(valueChanged(double)), |
4128 | this, SLOT(slotControlSeek(double)), |
4129 | Qt::DirectConnection); |
4130 | |
4131 | // Control used to communicate ratio playpos to GUI thread |
4132 | visualPlaypos = |
4133 | - new ControlPotmeter(ConfigKey(group, "visual_playposition"), 0., 1.); |
4134 | + new ControlPotmeter(ConfigKey(group, "visual_playposition"), -0.14, 1.14); |
4135 | |
4136 | // m_pTrackEnd is used to signal when at end of file during |
4137 | // playback. TODO(XXX) This should not even be a control object because it |
4138 | @@ -145,6 +153,14 @@ |
4139 | m_pRepeat = new ControlPushButton(ConfigKey(group, "repeat")); |
4140 | m_pRepeat->setToggleButton(true); |
4141 | |
4142 | +#ifdef __VINYLCONTROL__ |
4143 | + m_pVinylStatus = new ControlObject(ConfigKey(group,"vinylcontrol_status")); |
4144 | + m_pVinylSeek = new ControlObject(ConfigKey(group,"vinylcontrol_seek")); |
4145 | + connect(m_pVinylSeek, SIGNAL(valueChanged(double)), |
4146 | + this, SLOT(slotControlVinylSeek(double)), |
4147 | + Qt::DirectConnection); |
4148 | +#endif |
4149 | + |
4150 | // Sample rate |
4151 | m_pSampleRate = ControlObject::getControl(ConfigKey("[Master]","samplerate")); |
4152 | |
4153 | @@ -192,10 +208,17 @@ |
4154 | this, SLOT(slotEjectTrack(double)), |
4155 | Qt::DirectConnection); |
4156 | |
4157 | + //m_iRampIter = 0; |
4158 | + |
4159 | + /*df.setFileName("mixxx-debug.csv"); |
4160 | + df.open(QIODevice::WriteOnly | QIODevice::Text); |
4161 | + writer.setDevice(&df);*/ |
4162 | } |
4163 | |
4164 | EngineBuffer::~EngineBuffer() |
4165 | { |
4166 | + //close the writer |
4167 | + /*df.close();*/ |
4168 | delete m_pReadAheadManager; |
4169 | delete m_pReader; |
4170 | |
4171 | @@ -353,21 +376,85 @@ |
4172 | emit(trackUnloaded(pTrack)); |
4173 | } |
4174 | |
4175 | +void EngineBuffer::slotControlVinylSeek(double change) |
4176 | +{ |
4177 | +#ifdef __VINYLCONTROL__ |
4178 | + if(isnan(change) || change > 1.14 || change < -1.14) { |
4179 | + // This seek is ridiculous. |
4180 | + return; |
4181 | + } |
4182 | + |
4183 | + double new_playpos = round(change*file_length_old); |
4184 | + |
4185 | + ControlObject *pVinylMode = ControlObject::getControl(ConfigKey(group,"vinylcontrol_mode")); |
4186 | + ControlObject *pVinylEnabled = ControlObject::getControl(ConfigKey(group,"vinylcontrol_enabled")); |
4187 | + |
4188 | + if (m_pCurrentTrack != NULL && pVinylEnabled != NULL && pVinylMode != NULL) { |
4189 | + if (pVinylEnabled->get() && pVinylMode->get() == MIXXX_VCMODE_RELATIVE) { |
4190 | + int cuemode = (int)ControlObject::getControl(ConfigKey(group,"vinylcontrol_cueing"))->get(); |
4191 | + |
4192 | + //if in preroll, always seek |
4193 | + if (new_playpos < 0) { |
4194 | + slotControlSeek(change); |
4195 | + return; |
4196 | + } else if (cuemode == MIXXX_RELATIVE_CUE_OFF) { |
4197 | + return; //if off, do nothing |
4198 | + } else if (cuemode == MIXXX_RELATIVE_CUE_ONECUE) { |
4199 | + //if onecue, just seek to the regular cue |
4200 | + slotControlSeekAbs(m_pCurrentTrack->getCuePoint()); |
4201 | + return; |
4202 | + } |
4203 | + |
4204 | + double distance = 0; |
4205 | + int nearest_playpos = -1; |
4206 | + |
4207 | + QList<Cue*> cuePoints = m_pCurrentTrack->getCuePoints(); |
4208 | + QListIterator<Cue*> it(cuePoints); |
4209 | + while (it.hasNext()) { |
4210 | + Cue* pCue = it.next(); |
4211 | + if (pCue->getType() != Cue::CUE || pCue->getHotCue() == -1) |
4212 | + continue; |
4213 | + |
4214 | + int cue_position = pCue->getPosition(); |
4215 | + //pick cues closest to new_playpos |
4216 | + if ((nearest_playpos == -1) || |
4217 | + (fabs(new_playpos - cue_position) < distance)) { |
4218 | + nearest_playpos = cue_position; |
4219 | + distance = fabs(new_playpos - cue_position); |
4220 | + } |
4221 | + } |
4222 | + |
4223 | + if (nearest_playpos == -1) { |
4224 | + if (new_playpos >= 0) |
4225 | + //never found an appropriate cue, so don't seek? |
4226 | + return; |
4227 | + //if negative, allow a seek by falling down to the bottom |
4228 | + } else { |
4229 | + slotControlSeekAbs((float)nearest_playpos); |
4230 | + return; |
4231 | + } |
4232 | + } |
4233 | + } |
4234 | + //just seek where it wanted to originally |
4235 | + slotControlSeek(change); |
4236 | +#endif |
4237 | +} |
4238 | |
4239 | // WARNING: This method runs in both the GUI thread and the Engine Thread |
4240 | void EngineBuffer::slotControlSeek(double change) |
4241 | { |
4242 | - if(isnan(change) || change > 1.0 || change < 0.0) { |
4243 | + if(isnan(change) || change > 1.14 || change < -1.14) { |
4244 | // This seek is ridiculous. |
4245 | return; |
4246 | } |
4247 | |
4248 | // Find new playpos, restrict to valid ranges. |
4249 | double new_playpos = round(change*file_length_old); |
4250 | + |
4251 | if (new_playpos > file_length_old) |
4252 | new_playpos = file_length_old; |
4253 | - if (new_playpos < 0.) |
4254 | - new_playpos = 0.; |
4255 | + //if (new_playpos < 0.) |
4256 | + // new_playpos = 0.; |
4257 | |
4258 | // Ensure that the file position is even (remember, stereo channel files...) |
4259 | if (!even((int)new_playpos)) |
4260 | @@ -445,6 +532,7 @@ |
4261 | CSAMPLE * pOutput = (CSAMPLE *)pOut; |
4262 | |
4263 | bool bCurBufferPaused = false; |
4264 | + double rate; |
4265 | |
4266 | if (!m_pTrackEnd->get() && pause.tryLock()) { |
4267 | |
4268 | @@ -462,19 +550,22 @@ |
4269 | |
4270 | bool paused = playButton->get() != 0.0f ? false : true; |
4271 | |
4272 | - double rate = m_pRateControl->calculateRate(baserate, paused); |
4273 | + rate = m_pRateControl->calculateRate(baserate, paused); |
4274 | //qDebug() << "rate" << rate << " paused" << paused; |
4275 | |
4276 | // If the rate has changed, set it in the scale object |
4277 | if (rate != rate_old || m_bScalerChanged) { |
4278 | // The rate returned by the scale object can be different from the wanted rate! |
4279 | - |
4280 | - //XXX: Trying to force RAMAN to read from correct |
4281 | - // playpos when rate changes direction - Albert |
4282 | - if (m_bScalerChanged || (rate_old <= 0 && rate > 0) || |
4283 | - (rate_old >= 0 && rate < 0)) |
4284 | - { |
4285 | + // Make sure new scaler has proper position |
4286 | + if (m_bScalerChanged) { |
4287 | setNewPlaypos(filepos_play); |
4288 | + } else if (m_pScale != m_pScaleLinear) { //linear scaler does this part for us now |
4289 | + //XXX: Trying to force RAMAN to read from correct |
4290 | + // playpos when rate changes direction - Albert |
4291 | + if ((rate_old <= 0 && rate > 0) || |
4292 | + (rate_old >= 0 && rate < 0)) { |
4293 | + setNewPlaypos(filepos_play); |
4294 | + } |
4295 | } |
4296 | |
4297 | rate_old = rate; |
4298 | @@ -493,9 +584,10 @@ |
4299 | // If we're playing past the end, playing before the start, or standing |
4300 | // still then by definition the buffer is paused. |
4301 | bCurBufferPaused = rate == 0 || |
4302 | - (at_start && backwards) || |
4303 | + //(at_start && backwards) || |
4304 | (at_end && !backwards); |
4305 | |
4306 | + |
4307 | // If the buffer is not paused, then scale the audio. |
4308 | if (!bCurBufferPaused) { |
4309 | CSAMPLE *output; |
4310 | @@ -531,9 +623,9 @@ |
4311 | |
4312 | // Adjust filepos_play by the amount we processed. |
4313 | filepos_play += idx; |
4314 | - filepos_play = math_max(0, filepos_play); |
4315 | - // We need the above protection against negative playpositions |
4316 | - // in case SoundTouch/EngineBufferSoundTouch gives us too many samples. |
4317 | + //filepos_play = math_max(0, filepos_play); |
4318 | + //// We need the above protection against negative playpositions |
4319 | + //// in case SoundTouch/EngineBufferSoundTouch gives us too many samples. |
4320 | |
4321 | // Get rid of annoying decimals that the scaler sometimes produces |
4322 | filepos_play = round(filepos_play); |
4323 | @@ -599,7 +691,7 @@ |
4324 | |
4325 | bool repeat_enabled = m_pRepeat->get() != 0.0f; |
4326 | |
4327 | - bool end_of_track = (at_start && backwards) || |
4328 | + bool end_of_track = //(at_start && backwards) || |
4329 | (at_end && !backwards); |
4330 | |
4331 | // If playbutton is pressed, check if we are at start or end of track |
4332 | @@ -624,27 +716,57 @@ |
4333 | // (hopefully) before the next callback. |
4334 | m_pReader->wake(); |
4335 | |
4336 | - // Force ramp in if this is the first buffer during a play |
4337 | if (m_bLastBufferPaused && !bCurBufferPaused) { |
4338 | - // Ramp from zero |
4339 | - int iLen = math_min(iBufferSize, kiRampLength); |
4340 | - float fStep = pOutput[iLen-1]/(float)iLen; |
4341 | - for (int i=0; i<iLen; ++i) |
4342 | - pOutput[i] = fStep*i; |
4343 | - |
4344 | - } |
4345 | - // Force ramp out if this is the first buffer to be paused. |
4346 | - else if (!m_bLastBufferPaused && bCurBufferPaused) { |
4347 | - rampOut(pOut, iBufferSize); |
4348 | - } |
4349 | - // If the previous buffer was paused and we are currently paused, then |
4350 | - // memset the output to zero. |
4351 | - else if (m_bLastBufferPaused && bCurBufferPaused) { |
4352 | - memset(pOutput, 0, sizeof(pOutput[0])*iBufferSize); |
4353 | + if (fabs(rate) > 0.005) //at very slow forward rates, don't ramp up |
4354 | + m_iRampState = ENGINE_RAMP_UP; |
4355 | + } else if (!m_bLastBufferPaused && bCurBufferPaused) { |
4356 | + m_iRampState = ENGINE_RAMP_DOWN; |
4357 | + } else { //we are not changing state |
4358 | + //make sure we aren't accidentally ramping down |
4359 | + //this is how we make sure that ramp value will become 1.0 eventually |
4360 | + if (fabs(rate) > 0.005 && m_iRampState != ENGINE_RAMP_UP && m_fRampValue < 1.0) |
4361 | + m_iRampState = ENGINE_RAMP_UP; |
4362 | + } |
4363 | + |
4364 | + //let's try holding the last sample value constant, and pull it |
4365 | + //towards zero |
4366 | + float ramp_inc = 0; |
4367 | + if (m_iRampState == ENGINE_RAMP_UP) { |
4368 | + ramp_inc = (m_iRampState * 0.2) / iBufferSize; //ramp up quickly (5 frames) |
4369 | + } else if (m_iRampState == ENGINE_RAMP_DOWN) { |
4370 | + ramp_inc = (m_iRampState * 0.08) / iBufferSize; //but down slowly |
4371 | + } |
4372 | + |
4373 | + //float fakerate = rate * 30000 == 0 ? -5000 : rate*30000; |
4374 | + for (int i=0; i<iBufferSize; i+=2) { |
4375 | + if (bCurBufferPaused) { |
4376 | + float dither = (float)(rand() % 32768) / 32768 - 0.5; // dither |
4377 | + pOutput[i] = m_fLastSampleValue[0] * m_fRampValue + dither; |
4378 | + pOutput[i+1] = m_fLastSampleValue[1] * m_fRampValue + dither; |
4379 | + } else { |
4380 | + pOutput[i] = pOutput[i] * m_fRampValue; |
4381 | + pOutput[i+1] = pOutput[i+1] * m_fRampValue; |
4382 | + } |
4383 | + |
4384 | + //writer << pOutput[i] << "\n"; |
4385 | + m_fRampValue += ramp_inc; |
4386 | + if (m_fRampValue >= 1.0) { |
4387 | + m_iRampState = ENGINE_RAMP_NONE; |
4388 | + m_fRampValue = 1.0; |
4389 | + } |
4390 | + if (m_fRampValue <= 0.0) { |
4391 | + m_iRampState = ENGINE_RAMP_NONE; |
4392 | + m_fRampValue = 0.0; |
4393 | + } |
4394 | + } |
4395 | + |
4396 | + if ((!bCurBufferPaused && m_iRampState == ENGINE_RAMP_NONE) || |
4397 | + (bCurBufferPaused && m_fRampValue == 0.0)) { |
4398 | + m_fLastSampleValue[0] = pOutput[iBufferSize-2]; |
4399 | + m_fLastSampleValue[1] = pOutput[iBufferSize-1]; |
4400 | } |
4401 | |
4402 | m_bLastBufferPaused = bCurBufferPaused; |
4403 | - m_fLastSampleValue = pOutput[iBufferSize-1]; |
4404 | } |
4405 | |
4406 | |
4407 | @@ -656,15 +778,24 @@ |
4408 | |
4409 | // Ramp to zero |
4410 | int i=0; |
4411 | - if (m_fLastSampleValue!=0.) |
4412 | - { |
4413 | - int iLen = math_min(iBufferSize, kiRampLength); |
4414 | - float fStep = m_fLastSampleValue/(float)iLen; |
4415 | + if (m_fLastSampleValue[0]!=0.) { |
4416 | // TODO(XXX) SSE |
4417 | - while (i<iLen) |
4418 | - { |
4419 | - pOutput[i] = fStep*(iLen-(i+1)); |
4420 | - ++i; |
4421 | + if (pOutput[0] == 0) { |
4422 | + while (i<iBufferSize) { |
4423 | + float sigmoid = sigmoid_zero((float)(iBufferSize - i), (float)iBufferSize); |
4424 | + float dither = (float)(rand() % 32768) / 32768 - 0.5; // dither |
4425 | + pOutput[i] = (float)m_fLastSampleValue[0] * sigmoid + dither; |
4426 | + pOutput[i+1] = (float)m_fLastSampleValue[1] * sigmoid + dither; |
4427 | + i+=2; |
4428 | + } |
4429 | + } else { |
4430 | + while (i<iBufferSize) { |
4431 | + float sigmoid = sigmoid_zero((float)(iBufferSize - i), (float)iBufferSize); |
4432 | + float dither = (float)(rand() % 32768) / 32768 - 0.5; // dither |
4433 | + pOutput[i] = (float)pOutput[i] * sigmoid + dither; |
4434 | + pOutput[i+1] = (float)pOutput[i+1] * sigmoid + dither; |
4435 | + i+=2; |
4436 | + } |
4437 | } |
4438 | } |
4439 | |
4440 | @@ -684,7 +815,7 @@ |
4441 | |
4442 | double fFractionalPlaypos = 0.0; |
4443 | if (file_length_old!=0.) { |
4444 | - fFractionalPlaypos = math_max(0.,math_min(filepos_play,file_length_old)); |
4445 | + fFractionalPlaypos = math_min(filepos_play,file_length_old); |
4446 | fFractionalPlaypos /= file_length_old; |
4447 | } else { |
4448 | fFractionalPlaypos = 0.; |
4449 | |
4450 | === modified file 'mixxx/src/engine/enginebuffer.h' |
4451 | --- mixxx/src/engine/enginebuffer.h 2011-04-07 04:23:53 +0000 |
4452 | +++ mixxx/src/engine/enginebuffer.h 2011-04-16 23:24:33 +0000 |
4453 | @@ -25,6 +25,8 @@ |
4454 | #include "trackinfoobject.h" |
4455 | #include "configobject.h" |
4456 | #include "rotary.h" |
4457 | +//for the writer |
4458 | +//#include <QtCore> |
4459 | |
4460 | class EngineControl; |
4461 | class BpmControl; |
4462 | @@ -64,10 +66,18 @@ |
4463 | const int TRACK_END_MODE_LOOP = 2; |
4464 | const int TRACK_END_MODE_PING = 3; |
4465 | |
4466 | -// Maximum number of samples used to ramp to or from zero when playback is |
4467 | -// stopped or started. |
4468 | -const int kiRampLength = 50; |
4469 | - |
4470 | +//vinyl status constants |
4471 | +//XXX: move this to vinylcontrol.h once thread startup is moved |
4472 | +const int VINYL_STATUS_DISABLED = 0; |
4473 | +const int VINYL_STATUS_OK = 1; |
4474 | +const int VINYL_STATUS_WARNING = 2; |
4475 | +const int VINYL_STATUS_ERROR = 3; |
4476 | + |
4477 | +const int ENGINE_RAMP_DOWN = -1; |
4478 | +const int ENGINE_RAMP_NONE = 0; |
4479 | +const int ENGINE_RAMP_UP = 1; |
4480 | + |
4481 | +//const int kiRampLength = 3; |
4482 | |
4483 | class EngineBuffer : public EngineObject |
4484 | { |
4485 | @@ -105,6 +115,7 @@ |
4486 | void slotControlStart(double); |
4487 | void slotControlEnd(double); |
4488 | void slotControlSeek(double); |
4489 | + void slotControlVinylSeek(double); |
4490 | void slotControlSeekAbs(double); |
4491 | |
4492 | // Request that the EngineBuffer load a track. Since the process is |
4493 | @@ -205,6 +216,9 @@ |
4494 | // Whether or not to repeat the track when at the end |
4495 | ControlPushButton* m_pRepeat; |
4496 | |
4497 | + ControlObject *m_pVinylStatus; //Status of vinyl control |
4498 | + ControlObject *m_pVinylSeek; |
4499 | + |
4500 | /** Fwd and back controls, start and end of track control */ |
4501 | ControlPushButton *startButton, *endButton; |
4502 | |
4503 | @@ -219,11 +233,16 @@ |
4504 | |
4505 | /** Holds the last sample value of the previous buffer. This is used when ramping to |
4506 | * zero in case of an immediate stop of the playback */ |
4507 | - float m_fLastSampleValue; |
4508 | + float m_fLastSampleValue[2]; |
4509 | /** Is true if the previous buffer was silent due to pausing */ |
4510 | bool m_bLastBufferPaused; |
4511 | + float m_fRampValue; |
4512 | + int m_iRampState; |
4513 | + //int m_iRampIter; |
4514 | |
4515 | TrackPointer m_pCurrentTrack; |
4516 | + /*QFile df; |
4517 | + QTextStream writer;*/ |
4518 | }; |
4519 | |
4520 | #endif |
4521 | |
4522 | === modified file 'mixxx/src/engine/enginebufferscalelinear.cpp' |
4523 | --- mixxx/src/engine/enginebufferscalelinear.cpp 2011-04-03 20:40:17 +0000 |
4524 | +++ mixxx/src/engine/enginebufferscalelinear.cpp 2011-04-16 23:24:33 +0000 |
4525 | @@ -20,8 +20,6 @@ |
4526 | #include "mathstuff.h" |
4527 | #include "sampleutil.h" |
4528 | |
4529 | -#define RATE_LERP_LENGTH 200 |
4530 | - |
4531 | EngineBufferScaleLinear::EngineBufferScaleLinear(ReadAheadManager *pReadAheadManager) : |
4532 | EngineBufferScale(), |
4533 | m_pReadAheadManager(pReadAheadManager) |
4534 | @@ -30,16 +28,25 @@ |
4535 | m_dTempo = 0.0f; |
4536 | m_fOldTempo = 1.0f; |
4537 | m_fOldBaseRate = 1.0f; |
4538 | - m_fPreviousL = 0.0f; |
4539 | - m_fPreviousR = 0.0f; |
4540 | - m_scaleRemainder = 0.0f; |
4541 | + m_dCurSampleIndex = 0.0f; |
4542 | + m_dNextSampleIndex = 0.0f; |
4543 | + |
4544 | + for (int i=0; i<2; i++) |
4545 | + m_fPrevSample[i] = 0.0f; |
4546 | |
4547 | buffer_int = new CSAMPLE[kiLinearScaleReadAheadLength]; |
4548 | + buffer_int_size = 0; |
4549 | + |
4550 | + /*df.setFileName("mixxx-debug-scaler.csv"); |
4551 | + df.open(QIODevice::WriteOnly | QIODevice::Text); |
4552 | + writer.setDevice(&df); |
4553 | + buffer_count=0;*/ |
4554 | SampleUtil::applyGain(buffer_int, 0.0, kiLinearScaleReadAheadLength); |
4555 | } |
4556 | |
4557 | EngineBufferScaleLinear::~EngineBufferScaleLinear() |
4558 | { |
4559 | + //df.close(); |
4560 | delete [] buffer_int; |
4561 | } |
4562 | |
4563 | @@ -50,16 +57,18 @@ |
4564 | |
4565 | m_dTempo = _tempo; |
4566 | |
4567 | - if (m_dTempo>MAX_SEEK_SPEED) |
4568 | + if (m_dTempo>MAX_SEEK_SPEED) { |
4569 | m_dTempo = MAX_SEEK_SPEED; |
4570 | - else if (m_dTempo < -MAX_SEEK_SPEED) |
4571 | + } else if (m_dTempo < -MAX_SEEK_SPEED) { |
4572 | m_dTempo = -MAX_SEEK_SPEED; |
4573 | + } |
4574 | |
4575 | // Determine playback direction |
4576 | - if (m_dTempo<0.) |
4577 | + if (m_dTempo<0.) { |
4578 | m_bBackwards = true; |
4579 | - else |
4580 | + } else { |
4581 | m_bBackwards = false; |
4582 | + } |
4583 | |
4584 | return m_dTempo; |
4585 | } |
4586 | @@ -75,7 +84,6 @@ |
4587 | void EngineBufferScaleLinear::clear() |
4588 | { |
4589 | m_bClear = true; |
4590 | - m_scaleRemainder = 0.0f; |
4591 | } |
4592 | |
4593 | |
4594 | @@ -90,9 +98,67 @@ |
4595 | return ((((a * frac_pos) - b_neg) * frac_pos + c) * frac_pos + x0); |
4596 | } |
4597 | |
4598 | -/** Stretch a buffer worth of audio using linear interpolation */ |
4599 | -CSAMPLE * EngineBufferScaleLinear::scale(double, unsigned long buf_size, |
4600 | - CSAMPLE*, unsigned long) |
4601 | +/** Determine if we're changing directions (scratching) and then perform |
4602 | + a stretch */ |
4603 | +CSAMPLE * EngineBufferScaleLinear::scale(double playpos, unsigned long buf_size, |
4604 | + CSAMPLE* pBase, unsigned long iBaseLength) |
4605 | +{ |
4606 | + float rate_add_new = m_dBaseRate; |
4607 | + float rate_add_old = m_fOldBaseRate; //Smoothly interpolate to new playback rate |
4608 | + |
4609 | + // Guard against buf_size == 0 |
4610 | + if ((int)buf_size == 0) |
4611 | + return buffer; |
4612 | + |
4613 | + if (rate_add_new * rate_add_old < 0) { |
4614 | + //calculate half buffer going one way, and half buffer going |
4615 | + //the other way. |
4616 | + |
4617 | + //first half: rate goes from old rate to zero |
4618 | + m_fOldBaseRate = rate_add_old; |
4619 | + m_dBaseRate = 0.0; |
4620 | + buffer = do_scale(buffer, buf_size/2, pBase, iBaseLength); |
4621 | + |
4622 | + //reset prev sample so we can now read in the other direction |
4623 | + //(may not be necessary?) |
4624 | + if ((int)ceil(m_dCurSampleIndex)*2+1 < buffer_int_size) { |
4625 | + m_fPrevSample[0] = buffer_int[(int)ceil(m_dNextSampleIndex)*2]; |
4626 | + m_fPrevSample[1] = buffer_int[(int)ceil(m_dNextSampleIndex)*2+1]; |
4627 | + } |
4628 | + |
4629 | + //if the buffer has extra samples, do a read so RAMAN ends up back where |
4630 | + //it should be |
4631 | + int extra_samples = buffer_int_size - (int)ceil(m_dCurSampleIndex)*2 - 2; |
4632 | + if (extra_samples > 0) { |
4633 | + if (extra_samples % 2 != 0) |
4634 | + extra_samples++; |
4635 | + qDebug() << "extra samples" << extra_samples; |
4636 | + |
4637 | + m_pReadAheadManager->getNextSamples(rate_add_new,buffer_int, |
4638 | + extra_samples); |
4639 | + |
4640 | + } |
4641 | + //force a buffer read: |
4642 | + buffer_int_size=0; |
4643 | + //make sure the indexes stay correct for interpolation |
4644 | + m_dCurSampleIndex = 0 - m_dCurSampleIndex + floor(m_dCurSampleIndex); |
4645 | + m_dNextSampleIndex = 1.0 - (m_dNextSampleIndex - floor(m_dNextSampleIndex)); |
4646 | + |
4647 | + //second half: rate goes from zero to new rate |
4648 | + m_fOldBaseRate = 0.0; |
4649 | + m_dBaseRate = rate_add_new; |
4650 | + //pass the address of the sample at the halfway point |
4651 | + do_scale(&buffer[buf_size/2], buf_size/2, pBase, iBaseLength); |
4652 | + |
4653 | + return buffer; |
4654 | + } |
4655 | + |
4656 | + return do_scale(buffer, buf_size, pBase, iBaseLength); |
4657 | +} |
4658 | + |
4659 | +/** Stretch a specified buffer worth of audio using linear interpolation */ |
4660 | +CSAMPLE * EngineBufferScaleLinear::do_scale(CSAMPLE* buf, unsigned long buf_size, |
4661 | + CSAMPLE* pBase, unsigned long iBaseLength) |
4662 | { |
4663 | |
4664 | long unscaled_samples_needed; |
4665 | @@ -102,9 +168,6 @@ |
4666 | float rate_add_diff = rate_add_new - rate_add_old; |
4667 | double rate_add_abs; |
4668 | |
4669 | - if ( rate_add_diff ) |
4670 | - m_scaleRemainder = 0.0f; |
4671 | - |
4672 | //Update the old base rate because we only need to |
4673 | //interpolate/ramp up the pitch changes once. |
4674 | m_fOldBaseRate = m_dBaseRate; |
4675 | @@ -113,158 +176,194 @@ |
4676 | // the new EngineBuffer implementation) |
4677 | new_playpos = 0.0; |
4678 | |
4679 | - const int iRateLerpLength = math_min(RATE_LERP_LENGTH, buf_size); |
4680 | + int iRateLerpLength = (int)buf_size; |
4681 | |
4682 | // Guard against buf_size == 0 |
4683 | if (iRateLerpLength == 0) |
4684 | - return buffer; |
4685 | + return buf; |
4686 | + |
4687 | + |
4688 | + //We check for scratch condition in the public function, so this |
4689 | + //shouldn't happen |
4690 | + Q_ASSERT(rate_add_new * rate_add_old >= 0); |
4691 | |
4692 | // Simulate the loop to estimate how many samples we need |
4693 | double samples = 0; |
4694 | |
4695 | - for (int j = 0; j < iRateLerpLength; j+=2) |
4696 | - { |
4697 | - rate_add = (rate_add_diff) / iRateLerpLength * j + rate_add_old; |
4698 | + for (int j = 0; j < iRateLerpLength; j += 2) { |
4699 | + rate_add = (rate_add_diff) / (float)iRateLerpLength * (float)j + rate_add_old; |
4700 | samples += fabs(rate_add); |
4701 | } |
4702 | |
4703 | rate_add = rate_add_new; |
4704 | rate_add_abs = fabs(rate_add); |
4705 | |
4706 | - samples += (rate_add_abs * ((buf_size - iRateLerpLength)/2)); |
4707 | - unscaled_samples_needed = ceil(samples); |
4708 | - |
4709 | - if ( samples != unscaled_samples_needed) |
4710 | - m_scaleRemainder += (double)unscaled_samples_needed - samples; |
4711 | - |
4712 | - bool carry_remainder = false; |
4713 | - if ((m_scaleRemainder > 1) || (m_scaleRemainder < 1)) |
4714 | - { |
4715 | - long rem = (long)floor(m_scaleRemainder); |
4716 | - |
4717 | - |
4718 | - // Be very defensive about equating the remainder |
4719 | - // back into unscaled_samples_needed |
4720 | - if ((unscaled_samples_needed - rem) >= 1) |
4721 | - { |
4722 | - carry_remainder = true; |
4723 | - m_scaleRemainder -= rem; |
4724 | - } |
4725 | + //we're calculating mono samples, so divide remaining buffer by 2; |
4726 | + samples += (rate_add_abs * ((float)(buf_size - iRateLerpLength)/2)); |
4727 | + unscaled_samples_needed = floor(samples); |
4728 | + |
4729 | + //if the current position fraction plus the future position fraction |
4730 | + //loops over 1.0, we need to round up |
4731 | + if (m_dNextSampleIndex - floor(m_dNextSampleIndex) + samples - floor(samples) > 1.0) { |
4732 | + unscaled_samples_needed++; |
4733 | } |
4734 | |
4735 | // Multiply by 2 because it is predicting mono rates, while we want a stereo |
4736 | // number of samples. |
4737 | unscaled_samples_needed *= 2; |
4738 | |
4739 | - Q_ASSERT(unscaled_samples_needed >= 0); |
4740 | - Q_ASSERT(unscaled_samples_needed != 0); |
4741 | - |
4742 | - int buffer_size = 0; |
4743 | - double buffer_index = 0; |
4744 | - |
4745 | - long current_sample = 0; |
4746 | - long prev_sample = 0; |
4747 | + //0 is never the right answer |
4748 | + unscaled_samples_needed = math_max(2,unscaled_samples_needed); |
4749 | + |
4750 | bool last_read_failed = false; |
4751 | + CSAMPLE prev_sample[2]; |
4752 | + CSAMPLE cur_sample[2]; |
4753 | + double prevIndex=0; |
4754 | |
4755 | - // Use new_playpos to count the new samples we touch. |
4756 | - new_playpos = 0; |
4757 | + prev_sample[0]=0; |
4758 | + prev_sample[1]=0; |
4759 | + cur_sample[0]=0; |
4760 | + cur_sample[1]=0; |
4761 | |
4762 | int i = 0; |
4763 | int screwups = 0; |
4764 | - while(i < buf_size) |
4765 | - { |
4766 | - prev_sample = current_sample; |
4767 | - |
4768 | - current_sample = floor(buffer_index) * 2; |
4769 | - if (!even(current_sample)) |
4770 | - current_sample++; |
4771 | - |
4772 | - Q_ASSERT(current_sample % 2 == 0); |
4773 | - Q_ASSERT(current_sample >= 0); |
4774 | - |
4775 | - //This code is so messed up. These ASSERTs should be enabled, but they actually |
4776 | - //fire because of bug(s). |
4777 | - //Q_ASSERT(prev_sample >= 0); |
4778 | - //Q_ASSERT(prev_sample-1 < kiLinearScaleReadAheadLength); |
4779 | - //the prev_sample-1 leaves room for the other sample in the stereo frame |
4780 | - //Instead, we're going to workaround the bug by just clamping prev_sample |
4781 | - //to make sure it stays in bounds: |
4782 | - prev_sample = math_min(kiLinearScaleReadAheadLength, prev_sample); |
4783 | - prev_sample = math_max(0, prev_sample); |
4784 | - |
4785 | - |
4786 | - if (prev_sample != current_sample) { |
4787 | - m_fPreviousL = buffer_int[prev_sample]; |
4788 | - m_fPreviousR = buffer_int[prev_sample+1]; |
4789 | - } |
4790 | - |
4791 | - if (current_sample+1 >= buffer_size) { |
4792 | + while(i < buf_size) { |
4793 | + //shift indicies |
4794 | + prevIndex = m_dCurSampleIndex; |
4795 | + m_dCurSampleIndex = m_dNextSampleIndex; |
4796 | + |
4797 | + //we're going to be interpolating between two samples, a lower (prev) |
4798 | + //and upper (cur) sample. If the lower sample is off the end of the buffer, |
4799 | + //load it from the saved globals |
4800 | + |
4801 | + if ((int)floor(m_dCurSampleIndex)*2+1 < buffer_int_size && m_dCurSampleIndex >= 0.0) { |
4802 | + m_fPrevSample[0] = prev_sample[0] = buffer_int[(int)floor(m_dCurSampleIndex)*2]; |
4803 | + m_fPrevSample[1] = prev_sample[1] = buffer_int[(int)floor(m_dCurSampleIndex)*2+1]; |
4804 | + } else { |
4805 | + prev_sample[0] = m_fPrevSample[0]; |
4806 | + prev_sample[1] = m_fPrevSample[1]; |
4807 | + } |
4808 | + |
4809 | + //Smooth any changes in the playback rate over iRateLerpLength |
4810 | + //samples. This prevents the change from being discontinuous and helps |
4811 | + //improve sound quality. |
4812 | + if (i < iRateLerpLength) { |
4813 | + rate_add = (float)i * (rate_add_diff) / (float)iRateLerpLength + rate_add_old; |
4814 | + //rate_add = sigmoid_zero((float)i,(float)iRateLerpLength) * rate_add_diff + rate_add_old; |
4815 | + } else { |
4816 | + rate_add = rate_add_new; |
4817 | + } |
4818 | + |
4819 | + // if we don't have enough samples, load some more |
4820 | + while ((int)ceil(m_dCurSampleIndex)*2+1 >= buffer_int_size) { |
4821 | + int old_bufsize = buffer_int_size; |
4822 | + //qDebug() << "buffer" << buffer_count << rate_add_old << rate_add_new << rate_add << i << m_dCurSampleIndex << buffer_int_size << unscaled_samples_needed; |
4823 | //Q_ASSERT(unscaled_samples_needed > 0); |
4824 | if (unscaled_samples_needed == 0) { |
4825 | - unscaled_samples_needed = 2; |
4826 | - screwups++; |
4827 | + //qDebug() << "screwup" << m_dCurSampleIndex << (int)ceil(m_dCurSampleIndex)*2+1 << buffer_int_size; |
4828 | + unscaled_samples_needed = 2; |
4829 | + screwups++; |
4830 | } |
4831 | |
4832 | int samples_to_read = math_min(kiLinearScaleReadAheadLength, |
4833 | unscaled_samples_needed); |
4834 | |
4835 | - buffer_size = m_pReadAheadManager |
4836 | - ->getNextSamples(m_dBaseRate,buffer_int, |
4837 | - samples_to_read); |
4838 | - |
4839 | - if (m_dBaseRate > 0) |
4840 | - new_playpos += buffer_size; |
4841 | - else if (m_dBaseRate < 0) |
4842 | - new_playpos -= buffer_size; |
4843 | - |
4844 | - |
4845 | - if (buffer_size == 0 && last_read_failed) { |
4846 | + if(rate_add_new == 0) { |
4847 | + //qDebug() << "new rate was zero"; |
4848 | + buffer_int_size = m_pReadAheadManager |
4849 | + ->getNextSamples(rate_add_old,buffer_int, |
4850 | + samples_to_read); |
4851 | + if (rate_add_old > 0) { |
4852 | + new_playpos += buffer_int_size; |
4853 | + } else if (rate_add_old < 0) { |
4854 | + new_playpos -= buffer_int_size; |
4855 | + } |
4856 | + } else { |
4857 | + buffer_int_size = m_pReadAheadManager |
4858 | + ->getNextSamples(rate_add_new,buffer_int, |
4859 | + samples_to_read); |
4860 | + if (rate_add_new > 0) |
4861 | + new_playpos += buffer_int_size; |
4862 | + else if (rate_add_new < 0) |
4863 | + new_playpos -= buffer_int_size; |
4864 | + } |
4865 | + |
4866 | + if (buffer_int_size == 0 && last_read_failed) { |
4867 | break; |
4868 | } |
4869 | - last_read_failed = buffer_size == 0; |
4870 | - |
4871 | - unscaled_samples_needed -= buffer_size; |
4872 | - buffer_index = buffer_index - floor(buffer_index); |
4873 | - |
4874 | - continue; |
4875 | - } |
4876 | - |
4877 | - //Smooth any changes in the playback rate over iRateLerpLength |
4878 | - //samples. This prevents the change from being discontinuous and helps |
4879 | - //improve sound quality. |
4880 | - if (i < iRateLerpLength) { |
4881 | - rate_add = (rate_add_diff) / iRateLerpLength * i + rate_add_old; |
4882 | - } |
4883 | - else { |
4884 | - rate_add = rate_add_new; |
4885 | - } |
4886 | - |
4887 | - CSAMPLE frac = buffer_index - floor(buffer_index); |
4888 | + last_read_failed = buffer_int_size == 0; |
4889 | + |
4890 | + unscaled_samples_needed -= buffer_int_size; |
4891 | + //shift the index by the size of the old buffer |
4892 | + m_dCurSampleIndex -= old_bufsize / 2; |
4893 | + prevIndex -= old_bufsize / 2; |
4894 | + //fractions below 0 is ok, the ceil will bring it up to 0 |
4895 | + //this happens sometimes, somehow? |
4896 | + //Q_ASSERT(m_dCurSampleIndex > -1.0); |
4897 | + |
4898 | + //not sure this actually does anything, but it seems to help |
4899 | + if ((int)floor(m_dCurSampleIndex)*2 >= 0.0) { |
4900 | + m_fPrevSample[0] = prev_sample[0] = buffer_int[(int)floor(m_dCurSampleIndex)*2]; |
4901 | + m_fPrevSample[1] = prev_sample[1] = buffer_int[(int)floor(m_dCurSampleIndex)*2+1]; |
4902 | + } |
4903 | + } |
4904 | + //I guess? |
4905 | + if (last_read_failed) |
4906 | + break; |
4907 | + |
4908 | + cur_sample[0] = buffer_int[(int)ceil(m_dCurSampleIndex)*2]; |
4909 | + cur_sample[1] = buffer_int[(int)ceil(m_dCurSampleIndex)*2+1]; |
4910 | + |
4911 | + //rate_add was here |
4912 | + |
4913 | + //for the current index, what percentage is it between the previous and the next? |
4914 | + CSAMPLE frac = m_dCurSampleIndex - floor(m_dCurSampleIndex); |
4915 | |
4916 | //Perform linear interpolation |
4917 | - buffer[i] = m_fPreviousL + frac * (buffer_int[current_sample] - m_fPreviousL); |
4918 | - buffer[i+1] = m_fPreviousR + frac * (buffer_int[current_sample+1] - m_fPreviousR); |
4919 | - |
4920 | + buf[i] = (float)prev_sample[0] + frac * ((float)cur_sample[0] - (float)prev_sample[0]); |
4921 | + buf[i+1] = (float)prev_sample[1] + frac * ((float)cur_sample[1] - (float)prev_sample[1]); |
4922 | + |
4923 | + //at extremely low speeds, dampen the gain to hide pops and clicks |
4924 | + //this does cause odd-looking linear waveforms that go to zero and back |
4925 | + |
4926 | + //although enginevinylsoundemu does this, it works much better here |
4927 | + //because the gain ramps as the rate does |
4928 | + if (fabs(rate_add) < 0.5) { |
4929 | + float dither = (float)(rand() % 32768) / 32768 - 0.5; // dither |
4930 | + //float dither = 0; |
4931 | + float gainfrac = fabs(rate_add) / 0.5; |
4932 | + buf[i] = gainfrac * (float)buf[i] + dither; |
4933 | + buf[i+1] = gainfrac * (float)buf[i+1] + dither; |
4934 | + } |
4935 | + |
4936 | + /*writer << QString("%1,%2,%3,%4\n").arg(buffer_count) |
4937 | + .arg(buffer[i]) |
4938 | + .arg(prev_sample[0]) |
4939 | + .arg(cur_sample[0]); |
4940 | + buffer_count++;*/ |
4941 | + |
4942 | + //increment the index for the next loop |
4943 | if (i < iRateLerpLength) |
4944 | - buffer_index += fabs(rate_add); |
4945 | + m_dNextSampleIndex = m_dCurSampleIndex + fabs(rate_add); |
4946 | else |
4947 | - buffer_index += rate_add_abs; |
4948 | + m_dNextSampleIndex = m_dCurSampleIndex + rate_add_abs; |
4949 | |
4950 | i+=2; |
4951 | - } |
4952 | - |
4953 | - if ( carry_remainder ) |
4954 | - { |
4955 | - m_fPreviousL = buffer_int[buffer_size-2]; |
4956 | - m_fPreviousR = buffer_int[buffer_size-1]; |
4957 | - } |
4958 | - |
4959 | + |
4960 | + } |
4961 | // If we broke out of the loop, zero the remaining samples |
4962 | - SampleUtil::applyGain(&buffer[i], 0.0f, buf_size-i); |
4963 | + // TODO(XXX) memset |
4964 | + //for (; i < buf_size; i += 2) { |
4965 | + // buf[i] = 0.0f; |
4966 | + // buf[i+1] = 0.0f; |
4967 | + //} |
4968 | + |
4969 | + //Q_ASSERT(i>=buf_size); |
4970 | + SampleUtil::applyGain(&buf[i], 0.0f, buf_size-i); |
4971 | |
4972 | // It's possible that we will exit this function without having satisfied |
4973 | // this requirement. We may be trying to read past the end of the file. |
4974 | //Q_ASSERT(unscaled_samples_needed == 0); |
4975 | |
4976 | - return buffer; |
4977 | + return buf; |
4978 | } |
4979 | |
4980 | === modified file 'mixxx/src/engine/enginebufferscalelinear.h' |
4981 | --- mixxx/src/engine/enginebufferscalelinear.h 2010-05-23 06:36:06 +0000 |
4982 | +++ mixxx/src/engine/enginebufferscalelinear.h 2011-04-16 23:24:33 +0000 |
4983 | @@ -34,13 +34,17 @@ |
4984 | public: |
4985 | EngineBufferScaleLinear(ReadAheadManager *pReadAheadManager); |
4986 | ~EngineBufferScaleLinear(); |
4987 | - CSAMPLE *scale(double playpos, unsigned long buf_size, |
4988 | + CSAMPLE *scale(double playpos, unsigned long buf_size, |
4989 | CSAMPLE* pBase, unsigned long iBaseLength); |
4990 | + |
4991 | void setBaseRate(double dBaseRate); |
4992 | double setTempo(double dTempo); |
4993 | void clear(); |
4994 | |
4995 | private: |
4996 | + CSAMPLE *do_scale(CSAMPLE* buf, unsigned long buf_size, |
4997 | + CSAMPLE* pBase, unsigned long iBaseLength); |
4998 | + |
4999 | /** Holds playback direction */ |
5000 | bool m_bBackwards; |
This doesn't merge cleanly with the latest trunk. Can you please merge with trunk? The entire SConscript was refactored. It looks like from your diff, you only added two lines to the SConscript. I've produced a diff of what you need to change in the new SConscript based on that. When you merge with trunk, only SConscript.env will be conflicted. Replace whatever is in your copy with the trunk version, and apply this patch to mixxx/build/ features. py:
=== modified file 'mixxx/ build/features. py' features. py 2010-10-06 18:34:43 +0000 features. py 2010-10-06 23:20:56 +0000
sources. append( "#lib/xwax/ timecoder_ win32.c" )
sources. append( "#lib/xwax/ timecoder. c") append( "#lib/xwax/ lut.c") append( "#lib/xwax/ pitch.c" )
--- mixxx/build/
+++ mixxx/build/
@@ -198,6 +198,8 @@
else:
+ sources.
+ sources.
return sources
class Tonal(Feature):