mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Audio input, Audio optimisation
This commit is contained in:
+4
-1
@@ -29,7 +29,10 @@ Connector::Connector(uint16_t videoPadding)
|
||||
|
||||
Connector::~Connector()
|
||||
{
|
||||
stop();
|
||||
_active = false;
|
||||
|
||||
if (_write_thread.joinable())
|
||||
_write_thread.join();
|
||||
|
||||
if (_cipher)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef SRC_HELPER_IAUDIO_SENDER
|
||||
#define SRC_HELPER_IAUDIO_SENDER
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class IAudioSender {
|
||||
public:
|
||||
virtual ~IAudioSender() = default;
|
||||
virtual void sendAudio(uint8_t* data, uint32_t length) = 0;
|
||||
};
|
||||
|
||||
#endif /* SRC_HELPER_IAUDIO_SENDER */
|
||||
@@ -35,6 +35,9 @@
|
||||
#define CMD_VERSION 204
|
||||
#define CMD_ENCRYPTION 240
|
||||
|
||||
#define AUDIO_BUFFER_SIZE 2560
|
||||
#define AUDIO_BUFFER_OFFSET 12
|
||||
|
||||
struct ProtocolCmdEntry
|
||||
{
|
||||
int cmd;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#include "interface.h"
|
||||
#include "resource/background.h"
|
||||
#include "resource/font.h"
|
||||
#include "resource/colors.h"
|
||||
#include "resource/colours.h"
|
||||
#include "settings.h"
|
||||
#include "helper/protocol_const.h"
|
||||
#include <iostream>
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ extern "C"
|
||||
|
||||
#define FRAME_DELAY_INACTIVE 200
|
||||
|
||||
static const char *title = "Fast Car Play v0.4";
|
||||
static const char *title = "Fast Car Play v0.5";
|
||||
static SDL_Window *window = nullptr;
|
||||
static SDL_Renderer *renderer = nullptr;
|
||||
Uint32 evtStatus = (Uint32)-1;
|
||||
|
||||
+48
-25
@@ -24,6 +24,8 @@ PcmAudio::PcmAudio(const char *name)
|
||||
_faded = false;
|
||||
_volume = 1.0;
|
||||
_fadeVolume = Settings::audioFade;
|
||||
_config.channels = 0;
|
||||
_config.rate = 0;
|
||||
if (_fadeVolume < 0)
|
||||
_fadeVolume = 0;
|
||||
if (_fadeVolume > 1)
|
||||
@@ -64,17 +66,31 @@ void PcmAudio::callback(void *userdata, Uint8 *stream, int len)
|
||||
PcmAudio *self = static_cast<PcmAudio *>(userdata);
|
||||
const Message *segment = self->_data->peek();
|
||||
if (segment && self->_offset == 0 && getConfig(segment->getInt(OFFSET_AUDIO_FORMAT)) != self->_config)
|
||||
{
|
||||
self->_reconfig = true;
|
||||
self->_cv.notify_one();
|
||||
|
||||
bool underflow;
|
||||
if (self->_data->has(self->_underflowSize))
|
||||
{
|
||||
underflow = false;
|
||||
self->_underflowCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
underflow = true;
|
||||
}
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
if (segment == nullptr || self->_reconfig)
|
||||
if (segment == nullptr || self->_reconfig || underflow)
|
||||
{
|
||||
std::fill_n(stream, len, 0);
|
||||
self->_faded = self->_fade;
|
||||
self->_volume = self->_faded ? Settings::audioFade : 1;
|
||||
if (underflow && self->_underflowCount++ < 20)
|
||||
{
|
||||
self->_data->clear();
|
||||
return;
|
||||
}
|
||||
self->_reconfig = true;
|
||||
self->_cv.notify_one();
|
||||
return;
|
||||
@@ -155,33 +171,40 @@ void PcmAudio::runner()
|
||||
if (!segment)
|
||||
continue;
|
||||
|
||||
_config = getConfig(segment->getInt(OFFSET_AUDIO_FORMAT));
|
||||
if (device != 0)
|
||||
ChannelConfig config = getConfig(segment->getInt(OFFSET_AUDIO_FORMAT));
|
||||
if (_config != config)
|
||||
{
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
SDL_CloseAudioDevice(device);
|
||||
device = 0;
|
||||
if (device != 0)
|
||||
{
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
SDL_CloseAudioDevice(device);
|
||||
device = 0;
|
||||
}
|
||||
|
||||
// Configure new spec
|
||||
SDL_zero(spec);
|
||||
spec.freq = config.rate;
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.channels = config.channels;
|
||||
spec.samples = 4096;
|
||||
spec.callback = callback;
|
||||
spec.userdata = this;
|
||||
|
||||
device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0);
|
||||
if (device == 0)
|
||||
{
|
||||
std::cerr << _name << " Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
_config = config;
|
||||
}
|
||||
|
||||
// Configure new spec
|
||||
SDL_zero(spec);
|
||||
spec.freq = _config.rate;
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.channels = _config.channels;
|
||||
spec.samples = 4096;
|
||||
spec.callback = callback;
|
||||
spec.userdata = this;
|
||||
|
||||
_reconfig = false;
|
||||
_offset = 0;
|
||||
_reconfig = false;
|
||||
_underflowCount = 0;
|
||||
_underflowSize = spec.channels == 1 ? Settings::audioDelayCall : Settings::audioDelay;
|
||||
|
||||
device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0);
|
||||
if (device == 0)
|
||||
{
|
||||
std::cerr << _name << " Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
std::cout << _name << " Start playing " << _config.rate << "kHz " << (_config.channels == 2 ? "stereo" : "mono") << std::endl;
|
||||
if (_fader)
|
||||
|
||||
@@ -58,6 +58,8 @@ private:
|
||||
bool _faded;
|
||||
float _volume;
|
||||
float _fadeVolume;
|
||||
int _underflowCount;
|
||||
int _underflowSize;
|
||||
|
||||
std::thread _thread;
|
||||
std::mutex _mtx;
|
||||
|
||||
+37
-1
@@ -11,11 +11,11 @@ Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t paddi
|
||||
videoData(Settings::videoQueue),
|
||||
audioStreamMain(Settings::audioQueue),
|
||||
audioStreamAux(Settings::audioQueue),
|
||||
_recorder(Settings::audioQueue),
|
||||
_width(width),
|
||||
_height(height),
|
||||
_fps(fps),
|
||||
_phoneConnected(false)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
@@ -163,6 +163,14 @@ void Protocol::sendMove(float dx, float dy)
|
||||
send(CMD_TOUCH, false, buf, 16);
|
||||
}
|
||||
|
||||
void Protocol::sendAudio(uint8_t *data, uint32_t length)
|
||||
{
|
||||
write_uint32_le(data, 5);
|
||||
write_uint32_le(data + 4, 0);
|
||||
write_uint32_le(data + 8, 3);
|
||||
send(CMD_AUDIO_DATA, false, data, length);
|
||||
}
|
||||
|
||||
void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
// filename is assumed null‑terminated, so strlen + 1 to include the '\0'
|
||||
@@ -248,6 +256,9 @@ void Protocol::onPhone(bool connected)
|
||||
|
||||
std::cout << (connected ? "[Protocol] Phone connected" : "[Protocol] Phone disconnected") << std::endl;
|
||||
|
||||
if (!connected)
|
||||
_recorder.stop();
|
||||
|
||||
pushEvent(_evtPhoneId, connected ? 1 : 0);
|
||||
|
||||
if (connected && Settings::onConnect.value.length() > 1)
|
||||
@@ -257,11 +268,36 @@ void Protocol::onPhone(bool connected)
|
||||
execute(Settings::onDisconnect.value.c_str());
|
||||
}
|
||||
|
||||
void Protocol::onControl(int cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case 1:
|
||||
_recorder.start(this);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_recorder.stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
{
|
||||
bool dispose = true;
|
||||
switch (cmd)
|
||||
{
|
||||
|
||||
case CMD_CONTROL:
|
||||
if (length == 4)
|
||||
{
|
||||
int cmd = 0;
|
||||
memcpy(&cmd, data, sizeof(int));
|
||||
onControl(cmd);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CMD_PLUGGED:
|
||||
onPhone(true);
|
||||
break;
|
||||
|
||||
+8
-4
@@ -3,11 +3,12 @@
|
||||
|
||||
#include "struct/atomic_queue.h"
|
||||
#include "struct/message.h"
|
||||
|
||||
#include "helper/iaudio_sender.h"
|
||||
#include "settings.h"
|
||||
#include "connector.h"
|
||||
#include "recorder.h"
|
||||
|
||||
class Protocol : private Connector
|
||||
class Protocol : private Connector, public IAudioSender
|
||||
{
|
||||
|
||||
public:
|
||||
@@ -26,6 +27,7 @@ public:
|
||||
void sendFile(const char *filename, int value);
|
||||
void sendClick(float x, float y, bool down);
|
||||
void sendMove(float dx, float dy);
|
||||
void sendAudio(uint8_t *data, uint32_t length) override;
|
||||
|
||||
AtomicQueue<Message> videoData;
|
||||
AtomicQueue<Message> audioStreamMain;
|
||||
@@ -40,15 +42,17 @@ private:
|
||||
|
||||
void onStatus(uint8_t status) override;
|
||||
void onDevice(bool connected) override;
|
||||
void onControl(int cmd);
|
||||
void onData(uint32_t cmd, uint32_t length, uint8_t *data) override;
|
||||
void onPhone(bool connected);
|
||||
|
||||
static const char *cmdString(int cmd);
|
||||
static const char *cmdString(int cmd);
|
||||
|
||||
Recorder _recorder;
|
||||
uint16_t _width;
|
||||
uint16_t _height;
|
||||
uint16_t _fps;
|
||||
bool _phoneConnected;
|
||||
bool _phoneConnected;
|
||||
|
||||
uint32_t _evtStatusId = (uint32_t)-1;
|
||||
uint32_t _evtPhoneId = (uint32_t)-1;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#include "recorder.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "helper/protocol_const.h"
|
||||
#include "helper/functions.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
Recorder::Recorder(uint16_t buffSize)
|
||||
: _sender(nullptr), _active(false), _device(0), _data(buffSize)
|
||||
{
|
||||
}
|
||||
|
||||
Recorder::~Recorder()
|
||||
{
|
||||
stop();
|
||||
if (_thread.joinable())
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
void Recorder::start(IAudioSender *sender)
|
||||
{
|
||||
if (_active)
|
||||
return;
|
||||
|
||||
if (_thread.joinable())
|
||||
_thread.join();
|
||||
|
||||
_sender = sender;
|
||||
_active = true;
|
||||
_thread = std::thread(&Recorder::runner, this);
|
||||
}
|
||||
|
||||
void Recorder::stop()
|
||||
{
|
||||
if (!_active)
|
||||
return;
|
||||
_active = false;
|
||||
_data.notify();
|
||||
}
|
||||
|
||||
void Recorder::AudioCallback(void *userdata, Uint8 *stream, int len)
|
||||
{
|
||||
Recorder *self = static_cast<Recorder *>(userdata);
|
||||
std::unique_ptr<AudioChunk> frame(new AudioChunk(AUDIO_BUFFER_OFFSET + len));
|
||||
std::memcpy(frame.get()->data + AUDIO_BUFFER_OFFSET, stream, len);
|
||||
self->_data.pushDiscard(std::move(frame));
|
||||
}
|
||||
|
||||
void Recorder::runner()
|
||||
{
|
||||
setThreadName("recorder");
|
||||
|
||||
SDL_AudioDeviceID device = 0;
|
||||
SDL_AudioSpec spec;
|
||||
|
||||
SDL_zero(spec);
|
||||
spec.freq = 16000;
|
||||
spec.format = AUDIO_S16LSB;
|
||||
spec.channels = 1;
|
||||
spec.samples = AUDIO_BUFFER_SIZE / 2; // = 2560 bytes (1280 samples * 2 bytes)
|
||||
spec.callback = AudioCallback;
|
||||
spec.userdata = this;
|
||||
|
||||
device = SDL_OpenAudioDevice(nullptr, SDL_TRUE, &spec, nullptr, 0);
|
||||
if (device == 0)
|
||||
{
|
||||
std::cerr << "[Recording] Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
|
||||
while (_active)
|
||||
{
|
||||
std::unique_ptr<AudioChunk> buffer = _data.pop();
|
||||
if (buffer && _sender)
|
||||
_sender->sendAudio(buffer.get()->data, buffer.get()->size);
|
||||
else if (_active)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
SDL_CloseAudioDevice(device);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef SRC_RECORDER
|
||||
#define SRC_RECORDER
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "helper/iaudio_sender.h"
|
||||
#include "struct/atomic_queue.h"
|
||||
|
||||
class AudioChunk
|
||||
{
|
||||
public:
|
||||
AudioChunk(uint16_t size)
|
||||
: data(new uint8_t[size]), size(size)
|
||||
{
|
||||
}
|
||||
|
||||
~AudioChunk()
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
// Deleted copy constructor/assignment
|
||||
AudioChunk(const AudioChunk &) = delete;
|
||||
AudioChunk &operator=(const AudioChunk &) = delete;
|
||||
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
class Recorder
|
||||
{
|
||||
public:
|
||||
Recorder(uint16_t buffSize);
|
||||
~Recorder();
|
||||
|
||||
void start(IAudioSender *sender);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
static void AudioCallback(void *userdata, Uint8 *stream, int len);
|
||||
void runner();
|
||||
|
||||
IAudioSender *_sender;
|
||||
std::atomic<bool> _active;
|
||||
std::thread _thread;
|
||||
SDL_AudioDeviceID _device;
|
||||
AtomicQueue<AudioChunk> _data;
|
||||
};
|
||||
|
||||
#endif /* SRC_RECORDER */
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
static inline Setting<int> videoQueue{"video-buffer-size", 32};
|
||||
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
|
||||
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
|
||||
static inline Setting<int> audioDelayCall{"audio-buffer-wait-call", 8};
|
||||
static inline Setting<float> audioFade{"audio-fade", 0.3};
|
||||
static inline Setting<std::string> audioDriver{"audio-driver", ""};
|
||||
static inline Setting<std::string> onConnect{"on-connect-script", ""};
|
||||
|
||||
@@ -68,6 +68,11 @@ public:
|
||||
return item;
|
||||
}
|
||||
|
||||
bool has(uint8_t count)
|
||||
{
|
||||
return _count >= count;
|
||||
}
|
||||
|
||||
bool wait(atomic<bool> &waitFlag, uint8_t count = 0)
|
||||
{
|
||||
unique_lock<std::mutex> lock(_mtx);
|
||||
|
||||
Reference in New Issue
Block a user