> Attention!

New threads need to be created only inroot partition! In the future, they will be processed by moderators.



Real Tone cabel do-it-yourself for Rocksmith 2014 | Realtone Cable Assembly Guide



Rep: (9)
Stumbled on the internet onan articleGearWalker about creating a realtone cable for the Rocksmith 2014 PC version, I remembered my long-standing dream of playing this game on the PS3.
After thinking, I decided to try to collect it.
As the basis of this cable, I used the C-media USB sound interface purchased a few years ago
Attached Image
.

It cost a penny and is not used at the moment. As I understood fromoriginal article, a USB device with an CM108 audio chip was used. The same chip was in mine. To turn this device into Realtone, you must change the device ID to the ID of the original cable. There is no direct access to these parameters of the CM108 chip. The author suggests using a pre-programmed EEPROM connected to the chip. In the original article, the author uses AT93C46 (for working and programming this EEPROM in 16-bit mode, it is necessary to close the 8th and 6th leg of the chip and connect it to + 5V power supply). I did not find this EEPROM in my electronic trash, but I found S93C46. Having smoked the datasheets, I found that their only difference is that my version without a jumper works in 16 bit mode. After that I started programming. As a programmer, ARDUINO MEGA was used as in the original article.
Attached Image

Attached Image

To program this type of chip, an x-wire data exchange interface is used, which is present in the Arduino I2C / TWI library.
This is how the code for Arduino looks like
#include<SoftwareWire.h>
#define PIN_SK 52 // SCK - Digital 52
#define PIN_DO 50 // MISO - Digital 50
#define PIN_DI 51 // MOSI - Digital 51
#define PIN_CS 53 // SS - Digital 53

#define BIT_LENGTH 16

// # define _DEBUG_MODE_
#ifdef _DEBUG_MODE_
#define DEBUG_LOG (x) Serial.print (x)
#else
#define DEBUG_LOG (x)
#endif

union DATA {
uint16_t W;
uint8_t B [2];
};
uint8_t DESC_DATA [] = {0x05,0x67,0xBA, 0x12,0xFF, 0x00,0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0x18,0x00,0x52,0x6F, \
0x63.0x6B, 0x73.0x6D, 0x69.0x74.0x68.0x20, \
0x47.0x75.0x69.0x74.0x61.0x72.0x20.0x41, \
0x64.0x61.0x70.0x74.0x65.0x72.0x00.0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0x07,0x00,0x55,0x42, \
0x49.0x53.0x4F, 0x46.0x54.0x00.0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint8_t DESC_COUNT = 88;

void mwiOut (uint16_t value, uint8_t bitOrder, uint8_t bitAmount)
{
uint8_t dataPin = PIN_DI;
uint8_t clockPin = PIN_SK;
uint8_t x = (bitAmount>BIT_LENGTH)? BIT_LENGTH: bitAmount;
uint8_t i;
uint8_t dataBit = 0;

DEBUG_LOG ("W [");
for (i = 0; i<x; i ++)
{
if (bitOrder == LSBFIRST)
{
dataBit = (1 & (value>>i));
}
else
{
dataBit = (1 & (value>>((BIT_LENGTH-1) -i)));
}
digitalWrite (dataPin, dataBit);
DEBUG_LOG (dataBit);
digitalWrite (clockPin, HIGH);
digitalWrite (clockPin, LOW);
}
DEBUG_LOG ("]");
}

uint16_t mwiIn (uint8_t bitOrder, uint8_t bitAmount)
{
uint16_t value = 0;
uint8_t dataPin = PIN_DO;
uint8_t clockPin = PIN_SK;
uint8_t i;
uint8_t x = (bitAmount>BIT_LENGTH)? BIT_LENGTH: bitAmount;

DEBUG_LOG ("R [");
for (i = 0; i<x; ++ i) {
digitalWrite (clockPin, HIGH);
digitalWrite (clockPin, LOW);
DEBUG_LOG (digitalRead (dataPin));
if (bitOrder == LSBFIRST)
value | = digitalRead (dataPin)<<i;
else
value | = digitalRead (dataPin)<<((BIT_LENGTH-1) - i);
}
DEBUG_LOG ("]");
return value;
}

void waitReady (void)
{
// Wait ready operation
EEPROM_ENABLE (true);
bool bReady = false;
do
{
bReady = (digitalRead (PIN_DO) == HIGH);
digitalWrite (PIN_SK, HIGH);
digitalWrite (PIN_SK, LOW);
Serial.print (".");
} while (! bReady);
Serial.println ("DONE!");
EEPROM_ENABLE (false);
}

void EEPROM_ENABLE (bool value)
{
if (value)
{
digitalWrite (PIN_CS, HIGH);
digitalWrite (PIN_SK, LOW);
digitalWrite (PIN_DI, LOW);
}
else
{
digitalWrite (PIN_CS, LOW);
digitalWrite (PIN_SK, LOW);
digitalWrite (PIN_DI, LOW);
}
}

void EEPROM_EWEN ()
{
uint16_t cmd = 0b1001100000000000;

EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 9);
EEPROM_ENABLE (false);
}

void EEPROM_EWDS ()
{
uint16_t cmd = 0b1000000000000000;

EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 9);
EEPROM_ENABLE (false);
}

uint16_t EEPROM_READ (uint16_t address)
{
uint16_t value = 0;
uint16_t cmd = 0b1100000000000000;
uint16_t add = address<< 10;

EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 3);
mwiOut (add, MSBFIRST, 6);
value = mwiIn (MSBFIRST, 16);
EEPROM_ENABLE (false);
return value;
}

void EEPROM_WRITE (uint16_t address, uint16_t data)
{
uint16_t cmd = 0b1010000000000000;
uint16_t add = address<< 10;

// Write operation
EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 3);
mwiOut (add, MSBFIRST, 6);
mwiOut (data, MSBFIRST, 16);
EEPROM_ENABLE (false);
waitReady ();
}

void setup ()
{
// start serial port at 9600 bps:
Serial.begin (9600);
Serial.println ("ROCKSMITH EEPROM WRITER (AT93C46)");

// setup software 3-Wire interface
pinMode (PIN_SK, OUTPUT);
pinMode (PIN_CS, OUTPUT);
pinMode (PIN_DI, OUTPUT);
pinMode (PIN_DO, INPUT);
EEPROM_ENABLE (false);
}

void loop ()
{
Serial.print ("EEPROM_ENABLE (TRUE):");
EEPROM_ENABLE (true);
Serial.println ("DONE!");
Serial.print ("EEPROM_EWEN:");
EEPROM_EWEN ();
Serial.println ("DONE!");
for (int i = 0; i<= DESC_COUNT; i + = 2)
{
Serial.print ("EEPROM_WRITE (");
Serial.print (i);
Serial.print ("/");
Serial.print (DESC_COUNT);
Serial.print (")");
EEPROM_WRITE ((i>>1), ((DESC_DATA [i + 1]<<8) | (DESC_DATA [i])));
}
Serial.print ("EEPROM_EWDS:");
EEPROM_EWDS ();
Serial.println ("DONE!");

Serial.println ();
Serial.println ("VERIFY EEPROM");
DATA temp;
bool error = false;
for (uint16_t i = 0; i<= 44; i ++)
{
temp.W = EEPROM_READ (i);
Serial.print ("EEPROM_READ (0x");
Serial.print (i, HEX);
Serial.print ("): 0x");
Serial.print (temp.W, HEX);

if ((temp.B [0] == DESC_DATA [(i<<1)]) && (temp.B [1] == DESC_DATA [(i<<1)+1]))
{
Serial.println ("PASSED!");
}
else
{
Serial.println ("FAILED!");
error = true;
}
}

if (error)
Serial.println ("DATA ERROR. PROGRAM TERMINATED!");
else
Serial.println ("ALL PROCESS DONE!");

delay (1000);
exit (0);
}

After connecting and programming, we start port monitoring and see that the chip programming was successful.
Next, solder the EEPROM to the chip according to the scheme.
Attached Image

For convenience, the LED was soldered. In principle, it is not needed at all.
For me, soldering was the most difficult in the whole procedure (too finely).
He performed it with a piece of IDE cable from CD-rom with a soldering iron with a thin sting under the small scope.
Next, we fix the loop to the board, because he holds on to the small spots of tin and does not hold securely.
Attached Image

Attached Image

After all the procedures, we connect and verify that the device ID in Windows has changed:
It was
Attached Image
It became
Attached Image
The author of the original article checked the performance in the PC version of the game, I checked in the PS3 version. (I think it will work on PC as well)
Works great.
For convenience, I assembled an adapter for a guitar cable.
Attached Image


So as not to fall apart, pulled the case with electrical tape:
Attached Image


As a result:
The cost of manufacturing 300r.
Perhaps later I will post a video in working with the game.



Rep: (857)
Fine!



Rep: (1)
Works? I would like this to box. Ready to buy for an adequate price tag



Rep: (9)
It works on PS3, I did not check it on my computer. Apparently the cable is universal for all platforms.



Rep: (0)
Today at work I soldered a device for this recipe. The system decided on the desired VID_PID_. In the evening there will be field trials on the PC and on the PS3. Tomorrow I will unsubscribe how it behaves in the game. To the author and TS - a huge respect.



Rep: (9)
* Resv,
I think if the pids are determined correctly, there will be no problems. In any case, unsubscribe and, if possible, a photo of what happened in the studio.
Still, I think it will be useful which components and where they were used from.
Curious))))



Rep: (0)
There will certainly be a small report, but only after practical tests. Report on a non-working (?) Device - not right)))



Rep: (0)
So. After the weekend spent playing with the guitar in my hands, I can say that the home-made RealTone works fine. I did not notice problems with it. It is determined stably, does not fall off, doesn’t particularly fonit and the notes are read perfectly by the game. It is a pity there is no way to compare with the original cable, but I suspect that there will be no significant differences. So everyone who wants to save 4K rubles, I advise you to take parts, a soldering iron and assemble this device. The issue price is a penny and some free time.
To ease the rest a little, I will write where and what I found. To my surprise, the most difficult was to find the sound card itself with a full chip. For new ones, almost without a trace is a chip (with contacts), but only a crystal, flooded with a compound. This is also written in the original article. I tried to remove the compound and expose the crystal itself. It turned out, but alas, there is nowhere to solder mikruhu. So if you see a sound card with a black drop on the board - do not mess)). So, finding the “right” sound can be a daunting task. I was lucky, and a friend drove me an old external USB sound with the “right” chip. The next step was to find the right mikruha. Buying is not our method. Moreover, in my city I found such EEPROMs only on order with an expectation of 2-3 weeks. Therefore, rummaging around on the Internet and in my bins, I found an old motherboard (in my case, it was a Gigabyte GA-P35-S3L) with such an element. Further, I did everything according to the same scenario as in the first message, so I will omit this part. The only difference is that my EEPROM is not mounted on a piece of PCB, but simply "hangs" over the board. The rigidity of the connection is enough so that it does not hang out, and there will still not be any physical stress on it. Just in case, I put a piece of dielectric under it - a strip of paper)). Photos, maybe I'll add later, when I learn how to add them and hide them under the spoiler))).

пїЅпїЅпїЅпїЅP.S. Separately, I want to write an approximate list of devices where you can search for the very 93C46. Information taken from personal experience and the Internet.

They are found in:
  • Motherboard. Usually in old chipsets like 945 or P35, as in my case.
  • Network cards. They can also be found on new nets, but new ones haven’t come to my hand. But on the two old ones, they were on both, albeit in different buildings.
  • Receivers from wireless mice. This is a thing that looks like a flash drive. which is inserted into the computer. (NOT nano receivers that are quite small)
  • Printers.
  • Car radios. There is no specifics, because I just met mentions on the Internet that such memory chips are used in radio tape recorders.
  • DECT phones. It's also about old phones.
  • Well, in all the famous Chinese online store.

And a couple more tips. When connected to the PS3, the device does not cause trouble. Absolutely. But when connected to a computer, a message like:"Your Rocksmith Real Tone cable is not configured for best performance" . This message can be ignored - everything will work as expected, but if you want to get rid of this message, then try the following:
  • In the settings of sound devices, set the correct frequency and bit rate of the audio interface, i.e. “16 bit, 44.1 kHz (CD)” or “16 bit, 48 kHz (DVD)”.
  • When determining and installing drivers, the system can give the sound card a name written in Russian. For example: Microphone. If this is your case, then rename it with any other combination of numbers and letters WITHOUT Cyrillic. In some cases, this also solves the problem.



Rep: (9)
* Resv,
Thanks for the detailed description. I am glad that you succeeded and arrived in our "regiment".
If someone else has information on finding the necessary components, you are welcome to make life easier for those who will try to make this device on their own.

P.S. moral - do not throw away old electronic (and not only) devices)))))



Rep: (0)
To be honest, I’m surprised that the implementation turned out to be so simple elegant. In fact, ID spoofing is a hardware hack. It sounds terribly difficult, but in reality ...)))

Kamerton86 @ 02.21.17, 2:43 p.m.*
P.S. moral - do not throw away old electronic (and not only) devices)))))
But this is a fact. If I threw out the old iron, then I really would have to drag this mikruhu from China or carry it on order.

Well ... I wanted to add pictures, I even prepared them, and the “change” button disappeared. Apparently not fate))

Post has been editedResv - 21.02.17, 17:07



Rep: (9)
Add a new post



Rep: (0)
Promised pictures.
Under the spoilers pictures, with a total weight of ~ 5 Mb.

EEPROM is mounted on wires from an IDE loop.
Attached Image

6 copper conductors hold the memory chip tightly enough.
Two cores in isolation - power. Go to the back of the board.

Safety is never superfluous. This is how the isolation of the contacts of the microcircuit from the contacts of the board is implemented - using plain paper for the printer. It looks extremely wretched and collective farm, but it performs its function.
Attached Image

Strip _nanoisolator_ company "Snow Maiden")).

But this sound card was "killed" for the sake of science and for the purposes of the experiment.
Attached Image

If in your handssuch sound card, it is better to look for another one. This one will not do.
Attached Image

Not a very good photo taken under a microscope, but the essence conveys.

And so, the finished device actually looks after the board returns to the case.
Attached Image

A thin red wire connects the main board of the sound card and the additional board with audio connectors. Despite the fact that the wiring looks very thin and unreliable, it does not collect stray noise and background.


Future plans: replace the USB connector with a small piece of a good, shielded USB cable, for greater flexibility; and also, change the poor board with connectors to a full jack for Jack 1/4 ".
There was an idea to do "all_of_firm_", i.e. a solid cable with sound in the middle, but decided to abandon this idea, considering the modular system more convenient and less problematic.



Rep: (0)
Guys, how do I program the EEPROM through Arduino Uno? Mega I unfortunately burned ...



Rep: (9)
As with mega, it seems, you only need to rewrite it to the necessary pins at the beginning of the sketch.



Rep: (1358)
DragonPain @ 02.24.17, 16:30*
Mega I unfortunately burned ...
as??? oo



Rep: (6)
Uno change only these values?
#define PIN_SK 52 // SCK - Digital52
#define PIN_DO 50 // MISO - Digital50
#define PIN_DI 51 // MOSI - Digital51
#define PIN_CS 53 // SS - Digital53
or all?
#define PIN_SK52// SCK - Digital52
#define PIN_DO50// MISO - Digital50
#define PIN_DI51// MOSI - Digital51
#define PIN_CS53// SS - Digital53



Rep: (6)
from this picture, I realized that you need to insert the chip into the digital outputs 10-13, in the corresponding pins, right?

Attached images
Attached Image


Post has been editedokas43 - 17.03.17, 19:11



Rep: (9)
That's right, you need digital outputs.
Everything needs to be changed.



Rep: (8)
Kamerton86 @ 11.11.16, 23:34*
#include<SoftwareWire.h>
#define PIN_SK 52 // SCK - Digital 52
#define PIN_DO 50 // MISO - Digital 50
#define PIN_DI 51 // MOSI - Digital 51
#define PIN_CS 53 // SS - Digital 53

#define BIT_LENGTH 16

// # define _DEBUG_MODE_
#ifdef _DEBUG_MODE_
#define DEBUG_LOG (x) Serial.print (x)
#else
#define DEBUG_LOG (x)
#endif

union DATA {
uint16_t W;
uint8_t B [2];
};
uint8_t DESC_DATA [] = {0x05,0x67,0xBA, 0x12,0xFF, 0x00,0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0x18,0x00,0x52,0x6F, \
0x63.0x6B, 0x73.0x6D, 0x69.0x74.0x68.0x20, \
0x47.0x75.0x69.0x74.0x61.0x72.0x20.0x41, \
0x64.0x61.0x70.0x74.0x65.0x72.0x00.0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0x07,0x00,0x55,0x42, \
0x49.0x53.0x4F, 0x46.0x54.0x00.0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint8_t DESC_COUNT = 88;

void mwiOut (uint16_t value, uint8_t bitOrder, uint8_t bitAmount)
{
uint8_t dataPin = PIN_DI;
uint8_t clockPin = PIN_SK;
uint8_t x = (bitAmount>BIT_LENGTH)? BIT_LENGTH: bitAmount;
uint8_t i;
uint8_t dataBit = 0;

DEBUG_LOG ("W [");
for (i = 0; i<x; i ++)
{
if (bitOrder == LSBFIRST)
{
dataBit = (1 & (value>>i));
}
else
{
dataBit = (1 & (value>>((BIT_LENGTH-1) -i)));
}
digitalWrite (dataPin, dataBit);
DEBUG_LOG (dataBit);
digitalWrite (clockPin, HIGH);
digitalWrite (clockPin, LOW);
}
DEBUG_LOG ("]");
}

uint16_t mwiIn (uint8_t bitOrder, uint8_t bitAmount)
{
uint16_t value = 0;
uint8_t dataPin = PIN_DO;
uint8_t clockPin = PIN_SK;
uint8_t i;
uint8_t x = (bitAmount>BIT_LENGTH)? BIT_LENGTH: bitAmount;

DEBUG_LOG ("R [");
for (i = 0; i<x; ++ i) {
digitalWrite (clockPin, HIGH);
digitalWrite (clockPin, LOW);
DEBUG_LOG (digitalRead (dataPin));
if (bitOrder == LSBFIRST)
value | = digitalRead (dataPin)<<i;
else
value | = digitalRead (dataPin)<<((BIT_LENGTH-1) - i);
}
DEBUG_LOG ("]");
return value;
}

void waitReady (void)
{
// Wait ready operation
EEPROM_ENABLE (true);
bool bReady = false;
do
{
bReady = (digitalRead (PIN_DO) == HIGH);
digitalWrite (PIN_SK, HIGH);
digitalWrite (PIN_SK, LOW);
Serial.print (".");
} while (! bReady);
Serial.println ("DONE!");
EEPROM_ENABLE (false);
}

void EEPROM_ENABLE (bool value)
{
if (value)
{
digitalWrite (PIN_CS, HIGH);
digitalWrite (PIN_SK, LOW);
digitalWrite (PIN_DI, LOW);
}
else
{
digitalWrite (PIN_CS, LOW);
digitalWrite (PIN_SK, LOW);
digitalWrite (PIN_DI, LOW);
}
}

void EEPROM_EWEN ()
{
uint16_t cmd = 0b1001100000000000;

EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 9);
EEPROM_ENABLE (false);
}

void EEPROM_EWDS ()
{
uint16_t cmd = 0b1000000000000000;

EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 9);
EEPROM_ENABLE (false);
}

uint16_t EEPROM_READ (uint16_t address)
{
uint16_t value = 0;
uint16_t cmd = 0b1100000000000000;
uint16_t add = address<< 10;

EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 3);
mwiOut (add, MSBFIRST, 6);
value = mwiIn (MSBFIRST, 16);
EEPROM_ENABLE (false);
return value;
}

void EEPROM_WRITE (uint16_t address, uint16_t data)
{
uint16_t cmd = 0b1010000000000000;
uint16_t add = address<< 10;

// Write operation
EEPROM_ENABLE (true);
mwiOut (cmd, MSBFIRST, 3);
mwiOut (add, MSBFIRST, 6);
mwiOut (data, MSBFIRST, 16);
EEPROM_ENABLE (false);
waitReady ();
}

void setup ()
{
// start serial port at 9600 bps:
Serial.begin (9600);
Serial.println ("ROCKSMITH EEPROM WRITER (AT93C46)");

// setup software 3-Wire interface
pinMode (PIN_SK, OUTPUT);
pinMode (PIN_CS, OUTPUT);
pinMode (PIN_DI, OUTPUT);
pinMode (PIN_DO, INPUT);
EEPROM_ENABLE (false);
}

void loop ()
{
Serial.print ("EEPROM_ENABLE (TRUE):");
EEPROM_ENABLE (true);
Serial.println ("DONE!");
Serial.print ("EEPROM_EWEN:");
EEPROM_EWEN ();
Serial.println ("DONE!");
for (int i = 0; i<= DESC_COUNT; i + = 2)
{
Serial.print ("EEPROM_WRITE (");
Serial.print (i);
Serial.print ("/");
Serial.print (DESC_COUNT);
Serial.print (")");
EEPROM_WRITE ((i>>1), ((DESC_DATA [i + 1]<<8) | (DESC_DATA [i])));
}
Serial.print ("EEPROM_EWDS:");
EEPROM_EWDS ();
Serial.println ("DONE!");

Serial.println ();
Serial.println ("VERIFY EEPROM");
DATA temp;
bool error = false;
for (uint16_t i = 0; i<= 44; i ++)
{
temp.W = EEPROM_READ (i);
Serial.print ("EEPROM_READ (0x");
Serial.print (i, HEX);
Serial.print ("): 0x");
Serial.print (temp.W, HEX);

if ((temp.B [0] == DESC_DATA [(i<<1)]) && (temp.B [1] == DESC_DATA [(i<<1)+1]))
{
Serial.println ("PASSED!");
}
else
{
Serial.println ("FAILED!");
error = true;
}
}

if (error)
Serial.println ("DATA ERROR. PROGRAM TERMINATED!");
else
Serial.println ("ALL PROCESS DONE!");

delay (1000);
exit (0);
}



but is there a .eep file?
Why such difficulties with eeprom memory firmware, if you can directly flash through the programmer is the easiest?
Or, in any case, will you have to fence the cart from Atmega8 \ 16 \ etc and, for example, by ... button, write to eeprom memory?

Post has been editedcollin173 - 11.04.17, 08:23



Rep: (9)
* collin173,
There is no dump with eeprom.
And there is no programmer either. There was MEGA, and it cost her. I think it is possible to register directly in eeprom, but for this you need to dig into the structure, or consider the programmer to dump eeprom from an already flashed chip and then fill it in, but (I'm definitely not sure) problems with different types of microcircuits are possible.

You can buy an Nakrainyak Arduino Uno (worth a penny) and protect it.

Who made the cable, write off what you did. (programmer, arduino (which one))
If you used Arduino types that are different from the original article, that you changed in the source code of the program.

Post has been editedKamerton86 - 11.04.17, 09:41
Reason for editing: Complemented by UNO


Full version    

Help     rules

Now: 09/15/19, 20:31