You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
11 KiB
362 lines
11 KiB
/*
|
|
* Copyright (C) 2010,2011,2014 by Jonathan Naylor G4KLX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "DVDongleController.h"
|
|
|
|
#include "DStarDefines.h"
|
|
|
|
const unsigned char DVD_RESP_NOP[] = {0x02, 0x00};
|
|
const unsigned int DVD_RESP_NOP_LEN = 2U;
|
|
|
|
const unsigned char DVD_REQ_START[] = {0x05, 0x00, 0x18, 0x00, 0x01};
|
|
const unsigned int DVD_REQ_START_LEN = 5U;
|
|
|
|
const unsigned char DVD_RESP_START[] = {0x05, 0x00, 0x18, 0x00, 0x01};
|
|
const unsigned int DVD_RESP_START_LEN = 5U;
|
|
|
|
const unsigned char DVD_REQ_STOP[] = {0x05, 0x00, 0x18, 0x00, 0x00};
|
|
const unsigned int DVD_REQ_STOP_LEN = 5U;
|
|
|
|
const unsigned char DVD_RESP_STOP[] = {0x05, 0x00, 0x18, 0x00, 0x00};
|
|
const unsigned int DVD_RESP_STOP_LEN = 5U;
|
|
|
|
const unsigned char DVD_REQ_NAME[] = {0x04, 0x20, 0x01, 0x00};
|
|
const unsigned int DVD_REQ_NAME_LEN = 4U;
|
|
|
|
const unsigned char DVD_RESP_NAME[] = {0x0E, 0x00, 0x01, 0x00, 'D', 'V', ' ', 'D', 'o', 'n', 'g', 'l', 'e', 0x00};
|
|
const unsigned int DVD_RESP_NAME_LEN = 14U;
|
|
|
|
const unsigned char DVD_AMBE_HEADER[] = {0x32, 0xA0};
|
|
const unsigned char DVD_AUDIO_HEADER[] = {0x42, 0x81};
|
|
const unsigned int DVD_HEADER_LEN = 2U;
|
|
|
|
const unsigned char DVD_AMBE_DEC_DATA[] = {0xEC, 0x13, 0x00, 0x00, 0x30, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x20, 0x80,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
const unsigned char DVD_AMBE_ENC_DATA[] = {0xEC, 0x13, 0x00, 0x00, 0x30, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x04, 0xF0,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
const unsigned int DVD_AMBE_HEADER_LEN = 24U;
|
|
const unsigned int DVD_AMBE_LENGTH_BYTES = 48U;
|
|
const unsigned int DVD_AUDIO_LENGTH_BYTES = 320U;
|
|
|
|
const unsigned int DVD_DATA_LEN = DVD_AMBE_LENGTH_BYTES + DVD_HEADER_LEN + DVD_AUDIO_LENGTH_BYTES + DVD_HEADER_LEN;
|
|
|
|
CDVDongleController::CDVDongleController(const wxString& device) :
|
|
m_serial(device, SERIAL_230400)
|
|
{
|
|
}
|
|
|
|
CDVDongleController::~CDVDongleController()
|
|
{
|
|
}
|
|
|
|
bool CDVDongleController::open()
|
|
{
|
|
bool res = m_serial.open();
|
|
if (!res)
|
|
return false;
|
|
|
|
m_serial.write(DVD_REQ_NAME, DVD_REQ_NAME_LEN);
|
|
|
|
for (;;) {
|
|
unsigned char buffer[DVD_AUDIO_LENGTH_BYTES];
|
|
RESP_TYPE type = getResponse(buffer, DVD_AUDIO_LENGTH_BYTES);
|
|
if (type == RESP_ERROR)
|
|
return false;
|
|
|
|
if (type == RESP_NAME)
|
|
return true;
|
|
|
|
::wxMilliSleep(5UL);
|
|
}
|
|
}
|
|
|
|
bool CDVDongleController::start()
|
|
{
|
|
m_serial.write(DVD_REQ_START, DVD_REQ_START_LEN);
|
|
|
|
for (;;) {
|
|
unsigned char buffer[DVD_AUDIO_LENGTH_BYTES];
|
|
RESP_TYPE type = getResponse(buffer, DVD_AUDIO_LENGTH_BYTES);
|
|
if (type == RESP_ERROR)
|
|
return false;
|
|
|
|
if (type == RESP_START)
|
|
return true;
|
|
|
|
::wxMilliSleep(5UL);
|
|
}
|
|
}
|
|
|
|
bool CDVDongleController::stop()
|
|
{
|
|
m_serial.write(DVD_REQ_STOP, DVD_REQ_STOP_LEN);
|
|
|
|
for (;;) {
|
|
unsigned char buffer[DVD_AUDIO_LENGTH_BYTES];
|
|
RESP_TYPE type = getResponse(buffer, DVD_AUDIO_LENGTH_BYTES);
|
|
if (type == RESP_ERROR)
|
|
return false;
|
|
|
|
if (type == RESP_STOP)
|
|
return true;
|
|
|
|
::wxMilliSleep(5UL);
|
|
}
|
|
}
|
|
|
|
void CDVDongleController::encodeIn(const wxFloat32* audio, unsigned int length)
|
|
{
|
|
wxASSERT(audio != NULL);
|
|
wxASSERT(length == DSTAR_AUDIO_BLOCK_SIZE);
|
|
|
|
unsigned char buffer[DVD_HEADER_LEN + DVD_AUDIO_LENGTH_BYTES];
|
|
unsigned char* p = buffer;
|
|
|
|
// First a dummy AMBE packet with parameter information in
|
|
::memcpy(p, DVD_AMBE_HEADER, DVD_HEADER_LEN);
|
|
p += DVD_HEADER_LEN;
|
|
|
|
::memcpy(p, DVD_AMBE_ENC_DATA, DVD_AMBE_LENGTH_BYTES);
|
|
|
|
m_serial.write(buffer, DVD_HEADER_LEN + DVD_AMBE_LENGTH_BYTES);
|
|
|
|
// Then the Audio data to be encoded
|
|
p = buffer;
|
|
::memcpy(p, DVD_AUDIO_HEADER, DVD_HEADER_LEN);
|
|
p += DVD_HEADER_LEN;
|
|
|
|
wxInt16* q = (wxInt16*)p;
|
|
|
|
for (unsigned int i = 0; i < DSTAR_AUDIO_BLOCK_SIZE; i++) {
|
|
wxInt16 word = wxInt16(audio[i] * 32767.0F);
|
|
*q++ = wxINT16_SWAP_ON_BE(word);
|
|
}
|
|
|
|
m_serial.write(buffer, DVD_HEADER_LEN + DVD_AUDIO_LENGTH_BYTES);
|
|
}
|
|
|
|
bool CDVDongleController::encodeOut(unsigned char* ambe, unsigned int length)
|
|
{
|
|
wxASSERT(ambe != NULL);
|
|
wxASSERT(length == VOICE_FRAME_LENGTH_BYTES);
|
|
|
|
bool ambeFound = false;
|
|
bool audioFound = false;
|
|
|
|
for (;;) {
|
|
unsigned char buffer[DVD_AUDIO_LENGTH_BYTES];
|
|
RESP_TYPE type = getResponse(buffer, DVD_AUDIO_LENGTH_BYTES);
|
|
if (type == RESP_ERROR)
|
|
return false;
|
|
|
|
if (type == RESP_AMBE) {
|
|
::memcpy(ambe, buffer + DVD_AMBE_HEADER_LEN, VOICE_FRAME_LENGTH_BYTES);
|
|
ambeFound = true;
|
|
} else if (type == RESP_AUDIO) {
|
|
audioFound = true;
|
|
}
|
|
|
|
if (ambeFound && audioFound)
|
|
return true;
|
|
|
|
::wxMilliSleep(5UL);
|
|
}
|
|
}
|
|
|
|
void CDVDongleController::decodeIn(const unsigned char* ambe, unsigned int length)
|
|
{
|
|
wxASSERT(ambe != NULL);
|
|
wxASSERT(length == VOICE_FRAME_LENGTH_BYTES);
|
|
|
|
unsigned char buffer[DVD_HEADER_LEN + DVD_AUDIO_LENGTH_BYTES];
|
|
unsigned char* p = buffer;
|
|
|
|
// First the received AMBE data for decoding
|
|
::memcpy(p, DVD_AMBE_HEADER, DVD_HEADER_LEN);
|
|
p += DVD_HEADER_LEN;
|
|
|
|
::memcpy(p, DVD_AMBE_DEC_DATA, DVD_AMBE_LENGTH_BYTES);
|
|
|
|
::memcpy(p + DVD_AMBE_HEADER_LEN, ambe, VOICE_FRAME_LENGTH_BYTES);
|
|
|
|
m_serial.write(buffer, DVD_HEADER_LEN + DVD_AMBE_LENGTH_BYTES);
|
|
|
|
// Then a dummy Audio packet
|
|
p = buffer;
|
|
::memcpy(p, DVD_AUDIO_HEADER, DVD_HEADER_LEN);
|
|
p += DVD_HEADER_LEN;
|
|
|
|
::memset(p, 0x00, DVD_AUDIO_LENGTH_BYTES);
|
|
|
|
m_serial.write(buffer, DVD_HEADER_LEN + DVD_AUDIO_LENGTH_BYTES);
|
|
}
|
|
|
|
bool CDVDongleController::decodeOut(wxFloat32* audio, unsigned int length)
|
|
{
|
|
wxASSERT(audio != NULL);
|
|
wxASSERT(length == DSTAR_AUDIO_BLOCK_SIZE);
|
|
|
|
bool ambeFound = false;
|
|
bool audioFound = false;
|
|
|
|
for (;;) {
|
|
unsigned char buffer[DVD_AUDIO_LENGTH_BYTES];
|
|
RESP_TYPE type = getResponse(buffer, DVD_AUDIO_LENGTH_BYTES);
|
|
if (type == RESP_ERROR)
|
|
return false;
|
|
|
|
if (type == RESP_AMBE) {
|
|
ambeFound = true;
|
|
} else if (type == RESP_AUDIO) {
|
|
wxInt16* q = (wxInt16*)buffer;
|
|
for (unsigned int i = 0U; i < DSTAR_AUDIO_BLOCK_SIZE; i++) {
|
|
wxInt16 word = wxINT16_SWAP_ON_BE(*q++);
|
|
audio[i] = wxFloat32(word) / 32768.0F;
|
|
}
|
|
audioFound = true;
|
|
}
|
|
|
|
if (ambeFound && audioFound)
|
|
return true;
|
|
|
|
::wxMilliSleep(5UL);
|
|
}
|
|
}
|
|
|
|
void CDVDongleController::close()
|
|
{
|
|
m_serial.close();
|
|
}
|
|
|
|
CDVDongleController::RESP_TYPE CDVDongleController::getResponse(unsigned char* buffer, unsigned int length)
|
|
{
|
|
wxASSERT(buffer != NULL);
|
|
wxASSERT(length >= DVD_AUDIO_LENGTH_BYTES);
|
|
|
|
unsigned char c1, c2;
|
|
int len = m_serial.read(&c1, 1U);
|
|
if (len == 0)
|
|
return RESP_NONE;
|
|
|
|
if (len < 0) {
|
|
wxLogError(wxT("Unable to receive the DVD header: len=%d"), len);
|
|
return RESP_ERROR;
|
|
}
|
|
|
|
switch (c1) {
|
|
case 0xA0U: // DVD_AMBE_HEADER
|
|
wxLogMessage(wxT("Re-synchronising with the DV-Dongle"));
|
|
buffer[0U] = 0x32U;
|
|
buffer[1U] = c1;
|
|
return processResponse(buffer, length);
|
|
|
|
case 0x81U: // DVD_AUDIO_HEADER
|
|
wxLogMessage(wxT("Re-synchronising with the DV-Dongle"));
|
|
buffer[0U] = 0x42U;
|
|
buffer[1U] = c1;
|
|
return processResponse(buffer, length);
|
|
|
|
default:
|
|
m_serial.read(&c2, 1U);
|
|
buffer[0U] = c1;
|
|
buffer[1U] = c2;
|
|
return processResponse(buffer, length);
|
|
}
|
|
}
|
|
|
|
CDVDongleController::RESP_TYPE CDVDongleController::processResponse(unsigned char* buffer, unsigned int length)
|
|
{
|
|
wxASSERT(buffer != NULL);
|
|
wxASSERT(length >= DVD_AUDIO_LENGTH_BYTES);
|
|
|
|
int len;
|
|
|
|
if (::memcmp(buffer, DVD_AUDIO_HEADER, DVD_HEADER_LEN) == 0) { // Audio data
|
|
do {
|
|
len = m_serial.read(buffer, DVD_AUDIO_LENGTH_BYTES);
|
|
if (len == 0)
|
|
::wxMilliSleep(5UL);
|
|
} while (len == 0);
|
|
|
|
if (len != int(DVD_AUDIO_LENGTH_BYTES)) {
|
|
wxLogError(wxT("Unable to receive the DVD audio data, len=%d"), len);
|
|
return RESP_ERROR;
|
|
}
|
|
|
|
return RESP_AUDIO;
|
|
} else if (::memcmp(buffer, DVD_AMBE_HEADER, DVD_HEADER_LEN) == 0) { // AMBE data
|
|
do {
|
|
len = m_serial.read(buffer, DVD_AMBE_LENGTH_BYTES);
|
|
if (len == 0)
|
|
::wxMilliSleep(5UL);
|
|
} while (len == 0);
|
|
|
|
if (len != int(DVD_AMBE_LENGTH_BYTES)) {
|
|
wxLogError(wxT("Unable to receive the DVD AMBE data, len=%d"), len);
|
|
return RESP_ERROR;
|
|
}
|
|
|
|
return RESP_AMBE;
|
|
} else if (::memcmp(buffer, DVD_RESP_STOP, DVD_HEADER_LEN) == 0) { // Start or Stop response
|
|
do {
|
|
len = m_serial.read(buffer + DVD_HEADER_LEN, DVD_RESP_STOP_LEN - DVD_HEADER_LEN);
|
|
if (len == 0)
|
|
::wxMilliSleep(5UL);
|
|
} while (len == 0);
|
|
|
|
if (len != int(DVD_RESP_STOP_LEN - DVD_HEADER_LEN)) {
|
|
wxLogError(wxT("Unable to receive the DVD start/stop response, len=%d"), len);
|
|
return RESP_ERROR;
|
|
}
|
|
|
|
bool res = ::memcmp(buffer, DVD_RESP_START, DVD_RESP_START_LEN) == 0;
|
|
if (res)
|
|
return RESP_START;
|
|
|
|
res = ::memcmp(buffer, DVD_RESP_STOP, DVD_RESP_STOP_LEN) == 0;
|
|
if (res)
|
|
return RESP_STOP;
|
|
|
|
wxLogError(wxT("Incorrect response to start/stop request: %02X %02X %02X %02X %02X"), buffer[0U], buffer[1U], buffer[2U], buffer[3U], buffer[4U]);
|
|
return RESP_ERROR;
|
|
} else if (::memcmp(buffer, DVD_RESP_NAME, DVD_HEADER_LEN) == 0) { // Dongle name
|
|
do {
|
|
len = m_serial.read(buffer + DVD_HEADER_LEN, DVD_RESP_NAME_LEN - DVD_HEADER_LEN);
|
|
if (len == 0)
|
|
::wxMilliSleep(5UL);
|
|
} while (len == 0);
|
|
|
|
if (len != int(DVD_RESP_NAME_LEN - DVD_HEADER_LEN)) {
|
|
wxLogError(wxT("Unable to receive the DVD name data, len=%d"), len);
|
|
return RESP_ERROR;
|
|
}
|
|
|
|
bool res = ::memcmp(buffer, DVD_RESP_NAME, DVD_RESP_NAME_LEN) == 0;
|
|
if (res)
|
|
return RESP_NAME;
|
|
|
|
wxLogError(wxT("Incorrect response to name request: %02X %02X %02X %02X %02X..."), buffer[0U], buffer[1U], buffer[2U], buffer[3U], buffer[4U]);
|
|
return RESP_ERROR;
|
|
} else if (::memcmp(buffer, DVD_RESP_NOP, DVD_HEADER_LEN) == 0) { // No op
|
|
return RESP_NOP;
|
|
} else { // Unknown data
|
|
wxLogError(wxT("Unknown DV Dongle header: %02X %02X"), buffer[0U], buffer[1U]);
|
|
return RESP_UNKNOWN;
|
|
}
|
|
}
|