#include "cwsidetone.h" #include "logcategories.h" cwSidetone::cwSidetone(int level, int speed, int freq, double ratio, QWidget* parent) : parent(parent), volume(level), speed(speed), frequency(freq), ratio(ratio) { /* * Characters to match Icom table * Unknown characters will return '?' */ cwTable.clear(); cwTable['0'] = "-----"; cwTable['1'] = ".----"; cwTable['2'] = "..---"; cwTable['3'] = "...--"; cwTable['4'] = "....-"; cwTable['5'] = "....."; cwTable['6'] = "-...."; cwTable['7'] = "--..."; cwTable['8'] = "---.."; cwTable['9'] = "----."; cwTable['A'] = ".-"; cwTable['B'] = "-..."; cwTable['C'] = "-.-."; cwTable['D'] = "-.."; cwTable['E'] = "."; cwTable['F'] = "..-."; cwTable['G'] = "--."; cwTable['H'] = "...."; cwTable['I'] = ".."; cwTable['J'] = ".---"; cwTable['K'] = "-.-"; cwTable['L'] = ".-.."; cwTable['M'] = "--"; cwTable['N'] = "-."; cwTable['O'] = "---"; cwTable['P'] = ".--."; cwTable['Q'] = "--.-"; cwTable['R'] = ".-."; cwTable['S'] = "..."; cwTable['T'] = "-"; cwTable['U'] = "..-"; cwTable['V'] = "...-"; cwTable['W'] = ".--"; cwTable['X'] = "-..-"; cwTable['Y'] = "-.--"; cwTable['Z'] = "--.."; cwTable['/'] = "-..-."; cwTable['?'] = "..--.."; cwTable['.'] = ".-.-.-"; cwTable['-'] = "-....-"; cwTable[','] = "--..--"; cwTable[':'] = "---..."; cwTable['\''] = ".----."; cwTable['('] = "-.--.-"; cwTable[')'] = "-.--.-"; cwTable['='] = "-...-"; cwTable['+'] = ".-.-."; cwTable['"'] = ".-..-."; cwTable[' '] = " "; init(); } cwSidetone::~cwSidetone() { qInfo(logCW()) << "cwSidetone() finished"; this->stop(); output->stop(); } void cwSidetone::init() { format.setSampleRate(44100); format.setChannelCount(1); #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) format.setCodec("audio/pcm"); format.setSampleSize(16); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); QAudioDeviceInfo device(QAudioDeviceInfo::defaultOutputDevice()); #else format.setSampleFormat(QAudioFormat::Int16); QAudioDevice device = QMediaDevices::defaultAudioOutput(); #endif if (!device.isNull()) { if (!device.isFormatSupported(format)) { qWarning(logCW()) << "Default format not supported, using preferred"; format = device.preferredFormat(); } #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) output.reset(new QAudioOutput(device,format)); #else output.reset(new QAudioSink(device,format)); #endif #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) qInfo(logCW()) << QString("Sidetone Output: %0 (volume: %1 rate: %2 size: %3 type: %4)") .arg(device.deviceName()).arg(volume).arg(format.sampleRate()).arg(format.sampleSize()).arg(format.sampleType()); #else qInfo(logCW()) << QString("Sidetone Output: %0 (volume: %1 rate: %2 type: %3") .arg(device.description()).arg(volume).arg(format.sampleRate()).arg(format.sampleFormat()); #endif } this->start(); // Start QIODevice } void cwSidetone::send(QString text) { text=text.simplified(); for (int pos=0; pos < text.size(); pos++) { QChar ch = text.at(pos).toUpper(); QString currentChar; if (ch == NULL) { currentChar = cwTable[' ']; } else if (this->cwTable.contains(ch)) { currentChar = cwTable[ch]; } else { currentChar=cwTable['?']; } generateMorse(currentChar); } if (output->state() == QAudio::StoppedState) { output->start(this); } else if (output->state() == QAudio::IdleState) { output->suspend(); output->resume(); } return; } void cwSidetone::start() { open(QIODevice::ReadOnly); } void cwSidetone::stop() { close(); } qint64 cwSidetone::readData(char *data, qint64 len) { QMutexLocker locker(&mutex); const qint64 total = qMin(((qint64)buffer.size()), len); memcpy(data, buffer.constData(), total); buffer.remove(0,total); if (buffer.size() == 0) { emit finished(); } return total; } qint64 cwSidetone::writeData(const char *data, qint64 len) { Q_UNUSED(data); Q_UNUSED(len); return 0; } qint64 cwSidetone::bytesAvailable() const { return buffer.size() + QIODevice::bytesAvailable(); } void cwSidetone::generateMorse(QString morse) { int dit = int(double(SIDETONE_MULTIPLIER / this->speed * SIDETONE_MULTIPLIER)); int dah = int(double(dit * this->ratio)); QMutexLocker locker(&mutex); for (int i=0;ifrequency)); } else if (c == '.') { buffer.append(generateData(dit,this->frequency)); } else // Space char { buffer.append(generateData(dit,0)); } if (i(data.data()); int sampleIndex = 0; while (length) { const qreal x = (qSin(2 * M_PI * freq * qreal(sampleIndex % sampleRate) / sampleRate)) * qreal(volume/100.0); for (int i=0; i(ptr) = static_cast((1.0 + x) / 2 * 255); else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::SignedInt) *reinterpret_cast(ptr) = static_cast(x * std::numeric_limits::max()); else if (format.sampleSize() == 32 && format.sampleType() == QAudioFormat::SignedInt) *reinterpret_cast(ptr) = static_cast(x * std::numeric_limits::max()); else if (format.sampleType() == QAudioFormat::Float) *reinterpret_cast(ptr) = x; else qWarning(logCW()) << QString("Unsupported sample size: %0 type: %1").arg(format.sampleSize()).arg(format.sampleType()); #else if (format.sampleFormat() == QAudioFormat::UInt8) *reinterpret_cast(ptr) = static_cast((1.0 + x) / 2 * 255); else if (format.sampleFormat() == QAudioFormat::Int16) *reinterpret_cast(ptr) = static_cast(x * std::numeric_limits::max()); else if (format.sampleFormat() == QAudioFormat::Int32) *reinterpret_cast(ptr) = static_cast(x * std::numeric_limits::max()); else if (format.sampleFormat() == QAudioFormat::Float) *reinterpret_cast(ptr) = x; else qWarning(logCW()) << QString("Unsupported sample format: %0").arg(format.sampleFormat()); #endif ptr += channelBytes; length -= channelBytes; } ++sampleIndex; } return data; } void cwSidetone::setSpeed(unsigned char speed) { this->speed = (int)speed; } void cwSidetone::setFrequency(unsigned char frequency) { this->frequency = round((((600.0 / 255.0) * frequency) + 300) / 5.0) * 5.0; } void cwSidetone::setRatio(unsigned char ratio) { this->ratio = (double)ratio/10.0; } void cwSidetone::setLevel(int level) { volume = level; } void cwSidetone::stopSending() { QMutexLocker locker(&mutex); buffer.clear(); emit finished(); }