*BUILD LOG* 'CDJ 2000'-style DIY Traktor controller
*BUILD LOG* 'CDJ 2000'-style DIY Traktor controller Posted on: 14.10.2010 by Arcelia Siebeneck Just in the design stages at the moment but I'm pretty excited by this controller concept... believe it's pretty unique and is a good compromise between a performance 'midi-fighter-esque' controller and a more traditional CDJ/mixer combo. See what you believe:The screen is a 7" USB screen by Samsung (model U70) and is the most expensive part (about | |
Lillia Mestdagh 15.04.2011 |
Originally Posted by MiL0
Originally Posted by MiL0
http://www.djranking s.com/community /sho...372#post268372 |
Arcelia Siebeneck 15.04.2011 | This place is amazing sometimes - there can't be anywhere else on the internet that is taking what you can do with Traktor to such an extreme and advanced level. Infact, often when I'm researching for info on various DIY midi controller sites via google, djranking
s is nearly always at the top of the google results. With that in mind, and trying to keep things as organised as possible; would you mind starting a new thread for all this information? I don't mind it being posted in here but it might get lost if anyone tries to search for info about arduino's and lcd displays. |
Lillia Mestdagh 15.04.2011 |
Is that a double sided board? Assuming you did it at home; very very nicely done... I've nailed down single sided boards, but I'm a little wary of trying double sided, any tricks?
This guy has some wicked stuff to read... http://www.robotroom.com/ and here is the double sided board section... http://www.robotroom.com/PCB2.html its where I got all the tips on building PCBs including double-sided. Patience and care is the key to getting it right and a drill-press is an absolute must! I also ignored all that stuff you read online about using tungsten carbide drills with FR4, HSS is fine for one off boards, in my opinion your more likely to break a tungsten carbide drill bit before you wear out a HSS when making home brew pcbs! I used a 0.6mm drill bit for most of this board, it took me the better part of a day to drill the thing and my HSS drill bit was still going strong at the end of it! 90 - 95% of the (extremely fine!) traces came out absolutely fine because I scrubbed the board with wire wool first, then cleaned it with surgical spirit and wore rubber gloves while cleaning and applying the transfer (P-n-p blue) to avoid finger prints. For the few areas that did not quite make a connection, I used a Sharpie fine tip permanent marker to touch up before etching. |
Lillia Mestdagh 15.04.2011 | Nice!! I like the code that deals with the fact that Denon missed out CC#06, I know the switch case was prob not the neatest way of doing it!
It seems as though a few of the midi commands specific to the font and the segments have changed...
|
Louisa Oberc 14.04.2011 | Here's what I came up with... It's not a library, but it is fairly self contained (independent of the midi code): Code:
#include <WProgram.h> #include <LiquidCrystal.h> #include <Midi.h> struct DENON_SEGMENT { boolean valid; byte seg; byte col; byte val; }; class MyMidi : public Midi { public: MyMidi(HardwareSerial &s) : Midi(s) {} void handleControlChange(unsigned int channel, unsigned int controller, unsigned int value) { if (channel == 1 || channel == 2) { DENON_SEGMENT data = checkForSegmentData(controller, value); if (data.valid == true) track_name(data.seg, data.col+2, data.val); // col+2 to center it on my 2x16 screen (the denon only has a 2x12) } } }; MyMidi midi(Serial); LiquidCrystal lcd(8, 11, 9, 4, 5, 6, 7); // initialize the library with the numbers of the interface pins void setup() { lcd.begin(2, 16); digitalWrite(14, HIGH); midi.begin(0); } void loop() { midi.poll(); } struct DENON_SEGMENT checkForSegmentData(byte controller, byte value) { static byte seg1MSB, seg2MSB = 0; if ((controller >= 0x01 && controller <= 0x05) || (controller >= 0x07 && controller <= 0x0D)) { // segment 1 MSB seg1MSB = value; return (DENON_SEGMENT){false,0,0,0}; // incomplete packet } if (controller >= 0x0E && controller <= 0x19) { // segment 2 MSB seg2MSB = value; return (DENON_SEGMENT){false,0,0,0}; // incomplete packet } if ((controller >= 0x21 && controller <= 0x25) || (controller >= 0x27 && controller <= 0x2D)) { // segment 1 LSB return (DENON_SEGMENT){true, 0, getColumn(controller, 1), ((seg1MSB<<4) + value)}; // return completed packet } if (controller >= 0x2E && controller <= 0x39) { // segment 2 LSB return (DENON_SEGMENT){true, 1, getColumn(controller, 2), ((seg2MSB<<4) + value)}; // return completed packet } } // parameters: cc- controller number, seg- segment number // returns: column number (zero indexed) or 255 if invalid segment byte getColumn(byte cc, byte seg) { byte col=255; if (seg == 1) { col = cc - 33; // convert into segment number (column) if (col >= 6) col--; // work around the fact that Denon skipped 0x06 for segment 1-6 (they got segment 2 correct) } else if (seg == 2) { col = cc - 46; // convert into segment number (column) } return col; } void track_name(byte lcd_row, byte lcd_col, byte lcd_data){ lcd.setCursor(lcd_col, lcd_row); lcd.print(lcd_data); } http://www.youtube.com/watch?v=vWo-atD1ufw |
Louisa Oberc 14.04.2011 |
Originally Posted by MiL0
Wouldn't it be something like: Msgbox X= Chr(%X%) ? Though I don't know the language... however, assuming X is the decimal value, and Chr() works as it does everywhere else, that should work... |
Arcelia Siebeneck 14.04.2011 | okay, so without any understanding of Autohotkey syntax(!), does this look like it would do the job: Code:
!F1:: ; adds a windows hothey (alt+F1) X:= 0x32 ; Hex 32 is equivalent to Decimal 50 X:= X + 0 ; adding zero to a Hex number converts it to Dec Msgbox X=%X% ; this Msgbox command should display Dec 50 msgbox % Chr(X) ; this Msgbox command should display Chr 2 RETURN |
Louisa Oberc 14.04.2011 |
Originally Posted by Siytek
Compare the pdf you posted with this one (taken from the hc4500 product page): http://www.denondj.com/Assets/DImage...1916faea9a.pdf Thanks so much for your code and post.. I'm going to have fun picking through it in a bit. Depending on my available time I may even turn it into a library to make things easier for all MiL0: You may want to look into converting the hex to decimal and then (assuming ahk has the ability) use something similar to chr() to print out the ascii... That should get you all the text from the font table (you won't get the custom characters that way though). |
Arcelia Siebeneck 14.04.2011 | This is just a note to myself really (at work now heh): http://www.autohotkey.com/community /post-407905.html http://www.autohotkey.com/community /topic54618.html Autohotkey scripts that converts hex to ascii... |
Lillia Mestdagh 14.04.2011 |
so do you plan to commercialise the pcb you're prototyping? you could make a tidy sum if they came fully programmed and with all the components soldered on to the board.
edit: this probably needs a new thread?
do I need anything else than a Arduino + Display to make that hack possible?
You can buy a MIDI IN shield like this one from sparkfun (it has I and O)... http://www.sparkfun.com/products/9595 or you can make one like this... http://www.instructables.com/id/Arduino-MIDI-in-shield/ Anyways... I dont want to get too off the track of the thread! when my project is documented ill post a link / make a thread etc... ill continue to be involved with this thread tho, im interested to see if anyone develops the LCD stuff a bit more |
Wava Egizi 14.04.2011 | Siytek, do I need anything else than a Arduino + Display to make that hack possible? |
Arcelia Siebeneck 14.04.2011 | that's some next level bizness you've got going there man so do you plan to commercialise the pcb you're prototyping? you could make a tidy sum if they came fully programmed and with all the components soldered on to the board. edit: this probably needs a new thread? you could add your findings to this thread: http://www.djranking s.com/community /showthread.php?t=18512 or start a new one... |
Lillia Mestdagh 14.04.2011 | EDIT: Soz guys, just saw you are steaming ahead with the PHP stuff!! Thought this would be good for anyone stumbling upon the thread from scratch anyways... and my code at the bottom is basically your PHP code but for Arduino
you're designing a PCB that has the Arduino-based hardware, all the muxes and the I/O, all on one board?
and my big question would be, how the hell are you getting the track title / artist information out of Traktor?! There isn't any way that I know of (and I've tried literally everything!) apart from using the shoutcast/podcast hack. The problem with that is the title information gets exported after a long delay which obviously isn't ideal.
Here is the sheet I used, Denon MIDI (page 7) : DN-S3700 As only 7 bits of the MIDI data messages are available (the first bit is used to identify the message type) and the LCD needs 8 bits of data to display a character, each character needs to be transmitted in two control change messages. The first bit is used to idenfify the message, nothing to do with LCD but will be used in your code by the MIDI IN routine. The next three bits are discarded, and the final 4 bits are used for the data. The first control change message contains the first 4 bits of the 8-bit LCD character data, and the second CC message contains the second 4 bits, so.... X000AAAA + X000BBBB = AAAABBBB Where X is the message identifier bit, the '0s' are discarded, A is the first CC and B is the second CC. Assuming you have Arduino sucessfully receiving MIDI CC, by loading two CC messages into cc_data1 and cc_data2 the following code is a simplified way of getting the MIDI messages to the LCD. The actual code uses bitwise (see arduino website) to join the two nibbles together and simplified looks like this... This line shifts the first 4 bits of the first message along 4 places to make 0000AAAA into AAAA0000 lcd_nibble1 = (cc_data1 << 4); This line takes the data from the second CC message and passes it to a variable for the LCD routine lcd_nibble2 = cc_data2; Now we have the following lcd_nibble1 = AAAA0000 lcd_nibble2 = 0000BBBB Now we need to merge the two using the bitwise OR operator... lcd_output = lcd_nibble1 | lcd_nibble2; so now 'lcd_output' is AAAABBBB, a complete byte which can be passed straight to the lcd.print command using the standard Arduino LCD library... lcd.print(lcd_output); Now this code could be better as the library will break the byte up back into nibbles to pass to the LCD using the 4-bit interface (as my design uses 4-bit connection) so when I get some time, ill most likely make a few mods to the library to save some considerable processing time! However at the moment in development, this method works great and is a good way to simply use the standard Arduino LCD library in 4 or 8 bit mode. There is one more thing, I described how to pass the CC data to the LCD but nothing about CC numbers. The value of the CC message relates to the character but the CC number determines both the character position and identifies whether the message is the first or second nibble of data. Its all in the Denon MIDI sheet. For example.... If you (or Traktor!) wanted to display the letter 'A' in the second (of 16) character position and on the first line you would use the following... The character 8-bit code (from Denon or Hitachi LCD datasheet) is 0100 for the first nibble and 0001 for the second nibble, so... CC message 1 = 0001 = 1 (in decimal) CC message 2 = 0100 = 4 From the Denon sheet, CC number 2 (0x02 in hex) represents the first nibble for the second character on the first line of the display. CC number 34 (0x22 in hex) represents the second nibble for the second character on the first line of the display, so... CC#02 value 1 CC#34 value 4 will display the letter 'A' in the second char position on the first line. BTW Deck A will output all this info on channel 1 and Deck B will output it on channel 2... so my advice if your designing a controller with the Denon LCD system, reserve ch1 and ch2 for the LCD data! Here is the actual code with bits removed that dont relate to the LCD (easier to understand! ), please note the MIDI channel is currently ignored but a simple if or switch statement could be included to check the channel is correct or identify whether the data is from deck A or deck B, just havent got round to doing it yet! to use this code the receiveMIDI routine is fired whenever the serial port data is read and verified to be a recognised MIDI message. The following variables must be passed to the routine... statusType = type of message, CC, note on, note off etc... MIDIchannel = self explanatory! param1 = first parameter of data (for CC this would be the CC number) param2 = second parameter of data (for CC this would be the CC value) Code:
void receiveMIDI(byte statusType, byte MIDIchannel, byte param1, byte param2){ switch (statusType) { break; case 0xB0: ///// IF MESSAGE IS CONTROL CHANGE MESSAGE (0xB0) ///// switch(param1){ // CHECKS CC NUMBER, MSB GETS PUT IN tn_byte1 case 0x01: tn_byte1 = (param2 << 4); break; case 0x02: tn_byte1 = (param2 << 4); break; case 0x03: tn_byte1 = (param2 << 4); break; case 0x04: tn_byte1 = (param2 << 4); break; case 0x05: tn_byte1 = (param2 << 4); break; case 0x07: tn_byte1 = (param2 << 4); break; case 0x08: tn_byte1 = (param2 << 4); break; case 0x09: tn_byte1 = (param2 << 4); break; case 0x21: // BEGIN LSB, MSB IS READY IN tn_byte1 tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(0 , 0 , tn_output); // FIRES LCD PRINT WITH CO-ORDS AND CHAR DATA break; case 0x22: tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(1 , 0 , tn_output); break; case 0x23: tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(2 , 0 , tn_output); break; case 0x24: tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(3 , 0 , tn_output); break; case 0x25: tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(4 , 0 , tn_output); break; case 0x27: tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(5 , 0 , tn_output); break; case 0x28: tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(6 , 0 , tn_output); break; case 0x29: tn_byte2 = param2; tn_output = tn_byte1 | tn_byte2; track_name(7 , 0 , tn_output); break; } } } void track_name(byte lcd_col, byte lcd_row, byte lcd_data){ lcd.setCursor(lcd_col , lcd_row); lcd.print(lcd_data); } |
Arcelia Siebeneck 14.04.2011 | AHK uses a sortof C style markup I guess... but it's much easier to use. It's basically used to create advanced Windows macros but can be used to create very powerful software that can interact with Windows at quite a low level. The midi library for AHK makes it very useful for a lot of Traktor related utilities (I used it to create the Traktor Klone software that duplicates parts of Traktor's GUI). But anyway, don't worry too much about cleaning up the PHP code (unless you really want to). It's given me some food for thought on how to approach the AHK script. I imagine the most useful way to implement the title/artists information would be as part of the Arduino code? The only reason I'd like to write this in AHK is that anyone could use the exe without needing to own an Arduino (you'd just need a usb LCD screen like the ones they recommend for use with LCDSmartie). See my "VU meter" thread in my signature for more details. |
Louisa Oberc 14.04.2011 | Well in that case I'll clean the code up and get it to fully support the protocol... Would you use the php directly? or is there a language the ahk uses? I've never used it before.. |
Arcelia Siebeneck 14.04.2011 | good work!! the PHP is useful actually... my plan would be to write a script in Autohotkey that does the same thing. The good thing about AHK is that it has midi functionality now and also you can save your script as an exe easily. So basically the AHK script would sit between Traktor and the controller, converting the midi into text. Once this is done, it's relatively trivial to get AHK to export the text to a text file and have LCDSmartie import the text to be displayed on a small LCD display |
Louisa Oberc 13.04.2011 | Ok, I believe I have this licked... Using php (my main language) I've been able to go over the capture I made and extract the following:
limaxInMyimax In My Hmax In My Heax In My Heax In My Head In My Head In My Head n My Head My Head CMy Head Cly Head Cli Head ClimHead Climaead Climaxad Climax d Climax I Climax InClimax In Climax In MClimax In Mylimax In My imax In My Hmax In My Heax In My Heax In My Head In My Head In My Head n My Head My Head CMy Head Cly Head Cli Head ClimHead Climaead Climaxad Climax d Climax I Climax InClimax In Climax In MClimax In Mylimax In My imax In My Hmax In My Heax In My Heax In My Head In My Head
Dirty2DDirty 2 Diirty 2 Dirrty 2 Dirtty 2 Dirtyy 2 Dirty 2 Dirty 2 Dirty 2 Dirty 22 Dirty 2 Dirty 2 DDirty 2 Diirty 2 Dirrty 2 Dirtty 2 Dirtyy 2 Dirty 2 Dirty 2 Dirty 2 Dirty 22 Dirty 2 Dirty 2 DDirty 2 Diirty 2 Dirrty 2 Dirtty 2 Dirtyy 2 Dirty 2 Dirty 2 Dirty 2 Dirty 22 Dirty 2 Dirty 2 DDirty 2 Diirty 2 Dirrty 2 Dirtty 2 Dirtyy 2 Dirty 2 Dirty 2 Dirty 2 Dirty 22 Dirty 2 Dirty 2 DDirty 2 Diirty 2 Dirrty 2 Dirtty 2 Dirtyy 2 Dirty The first block is the first line, the second block is of course the second line. The 'init' messages I initially saw were in fact messages for the denon to turn on the 'ELAPSED', 'm', 's', 'f' display symbols... The font table they're using seems to match up with the extended ascii (albeit slightly customized) table, so converting the combined (msb and lsb) font value to ascii is dead simple... After reading that pdf in detail I can say that I really really want one of those hc4500's... the control you have is amazing... set any led, any display icon/segment, read any button, any fader, encoder, etc... A-Mazing... The quick'n'dirty php code I used is below... not sure if anyone cares about php code since it's useless for midi controllers, but it may help.. PHP Code:
|
Louisa Oberc 13.04.2011 | Completely disregard my previous post... After some search keyword trial and error, I figured out that it isn't denon supporting traktor... It's that traktor has support for the denon... In that traktor sends midi out according to DENON's SPEC not their own (as I originally assumed)... It seems as though denon's midi protocol is wide open to the public.. You can find it in the PDF here: http://www.denondj.com/Assets/DImage...1916faea9a.pdf It starts on page 16... It uses a 2 byte system and a font table as opposed to encoding the string data in the midi... Now I get to play some more |
Louisa Oberc 13.04.2011 | See next post... In short, my guess was wrong Here's a capture I made and what was going on inside traktor at the time. Before the capture started I added a blank denon device then I started the capture. First: I set the midi out port (this generated what seems to be an init message consisting of 4 midi messages [the first 4 in the file] to be sent) Second: I loaded a track: "Climax In My Head" by "2 Dirty", the file has a genre of "Electro House" and a total playtime of 6 minutes and 0 seconds. Third: I waited about 10 seconds before changing the midi out port to n/a to stop the midi messages. Note: I did NOT play the track, I'm purely trying to find the track name in the midst of a couple thousand midi messages. It seems to be sending deck b's details on channel 2 and deck a's on channel 1. The values have an upper limit of 0x0E (14) and seem to be rotating in a pattern of some sort. This would mean that they are sending the track data encoded in the control channels themselves as opposed to the cc value... Very interesting so far... |
Moshe Gariti 13.04.2011 | This controller looks like it would be awesome. |
Arcelia Siebeneck 13.04.2011 | well if that's the case then it should be easier to decipher / reverse engineer... unless it's sysex? |
Louisa Oberc 13.04.2011 | HID is human interface device (keyboard/mouse)... I am not sure that they work with output, only input... I'd bet that it is standard midi, using some sort of unique encoding scheme to translate ascii -> hex... I believe I'm going to have to play with this... |
Arcelia Siebeneck 13.04.2011 | hmm I need to look into that... would be perfect for my controller. I guess I need to pick up TP2 and see how the track information is being exported... HID perhaps? |
Louisa Oberc 13.04.2011 |
Originally Posted by MiL0
Originally Posted by MiL0
Due to that issue I never really took the time to see what data was being sent... Any chance of seeing the code for that part? Is that a double sided board? Assuming you did it at home; very very nicely done... I've nailed down single sided boards, but I'm a little wary of trying double sided, any tricks? |
Arcelia Siebeneck 13.04.2011 | Nice choice of music you got playing there :P and your project looks very impressive... what's your ambition? are you looking to comercialise it somehow? edit: it looks as if you're actually designing your own Arduino hardware yourself? Let me get this right... you're designing a PCB that has the Arduino-based hardware, all the muxes and the I/O, all on one board? and my big question would be, how the hell are you getting the track title / artist information out of Traktor?! There isn't any way that I know of (and I've tried literally everything!) apart from using the shoutcast/podcast hack. The problem with that is the title information gets exported after a long delay which obviously isn't ideal. |
Lillia Mestdagh 13.04.2011 | Hi All Just been reading this thread, you guys seem to be a nice little way into building a controller from seeing the pics, I have spent a heck of a lot of time recently developing Arduino MIDI stuff so just thought id share my current project if any of you were interested. Would be more than happy to help/get involved, particularly if your developing with Arduino. I was reading earlier in the thread that a few of you were talking about an Arduino + MUX shield sollution... my board is fundimentally an "Arduino + MUX Shield + MIDI IO Shield" on one board My design started life as a general Sensor --> MIDI project that could be shaped into any MIDI controller (for those wanting to build but without wanting to do the electronics) however it seems the project is leaning towards Traktor/Ableton recently. Heres the spec... * Arduino based design using ATMega168 (or 328) with on-board MIDI I/O port * 48 Analogue / Digital inputs or outputs (6 ports with 8 pins, each can be either I or O) by use of on-board multiplexers * Built-in twin 8-LED bargraph VU meter, controlled by MIDI CC * 8x2 (or 16x2 if you want) LCD display * Menu system allows for changes without reprogramming, so Arduino is optional, the MIDI config can be changed with the LCD menu * Supports the display of Traktor track name, artist and time on the LCD display (Uses/hijacks the data Traktor sends out for the Denon HC4500 LCD display) There is no built-in FTDI chip like there is with the Arduino boards however there is a connector for an FTDI programmer, I use the Modern Device USB-BUB, http://shop.moderndevice.com/products/usb-bub, the little red board shown in my picture below Im keen to get some feedback about whether peeps are interested in this kind of product aimed specifically at Traktor or Ableton. This is only a prototype board meant for testing all the features, so it looks a bit of a mess! Please excuse rubbish mobile phone camera pictures too! |
Maryanne Weatherill 26.03.2011 |
Originally Posted by extraclassic
|
Joan Kollmorgen 26.03.2011 | As mentioned before Tactile switches along with some caps might be worth considering. Some of these And these or these Also get some strip board to mount them on. |
Arcelia Siebeneck 26.03.2011 | do you mean that the lock nuts might touch each other or overlap? or do you mean that because the holes are so close together, that the 5-7mm section of aluminium between each hole is too narrow and might snap? surely thicker aluminium would solve this? sorry if I'm being a bit dim... heavy evening last evening lol |
Shay Wyche 26.03.2011 | With regard to the aluminium sheet, it's not the thickness of the aluminium I was worried about, but the spacing between the buttons. If they are too close together then you can have problems when you tighten the lock nuts up. Obviously the more holes you drill closer together, the weaker the metal will become. |
Arcelia Siebeneck 26.03.2011 | you know what I'd really like to use for my buttons? laptop keyboard keys! I'm gonna see if I can remove the individual buttons from an old keyboard I believe... |
DJ MENSAH 26.03.2011 | ...unless you are gonna use a button matrix. http://www.dribin.org/dave/keyboard/one_html/ |
Allene Manitta 26.03.2011 |
Originally Posted by MiL0
|
Arcelia Siebeneck 26.03.2011 | anyone had any experience with the Livid keypads? http://shop.lividinstruments.com/bui...-pads-4x4.html edit: actually, these look better suited (cheaper too): http://proto-pic.co.uk/products/Butt...akout-PCB.html bit pricey but meh, i really don't want to cut any corners on this project... might as well get the best I can... would hate to finish this and regret certain design choices at the end. btw - it seems Livid use 1.6mm thick aluminium for their panels... much thinner than I was planning: http://blog.lividinstruments.com/for...ic.php?id=1336 |
Stephnie Godbole 26.03.2011 |
Originally Posted by MiL0
|
Louisa Oberc 26.03.2011 | There is a way to invert it in code... but trust me, it becomes a evening mare trying to keep track of everything if some of your buttons output a 1 for being pushed and other output a 0 for being pushed (it would also require different wiring for the NC buttons) Try to get them all either NO or NC... it's easier |
Arcelia Siebeneck 25.03.2011 |
Originally Posted by Archies'bald
nice one - those 2 look just the job not sure if the 4th button will be a problem; probably not a great idea having a midi CC being outputted the whole time (unless de-pressed).. perhaps there's a way to reverse it in the Arduino code? |
Tatum Ansaldo 25.03.2011 |
Originally Posted by derschaich
|
Allene Manitta 25.03.2011 |
Originally Posted by lexion610
|
Diogo Dj Dragão 24.03.2011 | Look for tact switches instead. There are plenty of good sealed momentary tact switches out there with really short throws. Look for a long life switch (80K-100K cycles) and find a switch cap you like and you're in business. If you get a switch cap that has a lip at the bottom, you can use the faceplate to keep from ever loosing it. |
<< Back to Reviews of DJ equipment Reply