some part are working
This commit is contained in:
commit
bbd19fc2a8
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/wfr.iml" filepath="$PROJECT_DIR$/.idea/wfr.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
2
.idea/wfr.iml
generated
Normal file
2
.idea/wfr.iml
generated
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
21
CMakeLists.txt
Normal file
21
CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
SET(APP_NAME wfr)
|
||||||
|
|
||||||
|
project(${APP_NAME})
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
add_executable(${APP_NAME} main.cpp src/ALSADevice.cpp src/ALSADevice.h src/MemoryPool.cpp src/MemoryPool.h utils/Semaphore.h utils/Semaphore.cpp src/SampleWriter.h src/Timestamp.h
|
||||||
|
#src/SampleReceiver.h
|
||||||
|
src/audio_types.h)
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
if (THREADS_FOUND)
|
||||||
|
target_link_libraries(${APP_NAME} Threads::Threads)
|
||||||
|
endif (THREADS_FOUND)
|
||||||
|
|
||||||
|
find_package(ALSA REQUIRED)
|
||||||
|
if (ALSA_FOUND)
|
||||||
|
include_directories(${ALSA_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${APP_NAME} ${ALSA_LIBRARIES})
|
||||||
|
endif (ALSA_FOUND)
|
39
MATLAB/read_syncdata.m
Normal file
39
MATLAB/read_syncdata.m
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
% settings
|
||||||
|
DATASET_DIR = 'test';
|
||||||
|
TS_FILE = 'ts';
|
||||||
|
SAMPLE_FILE_PREFIX = 'ch_';
|
||||||
|
FILE_EXT = 'dat';
|
||||||
|
CHANNELS = 2;
|
||||||
|
|
||||||
|
% computed values
|
||||||
|
FN_TS = strcat(DATASET_DIR, '/', TS_FILE, '.', FILE_EXT);
|
||||||
|
|
||||||
|
for ch=0:1:(CHANNELS-1)
|
||||||
|
FN_SAMPLE{ch+1} = strcat(DATASET_DIR, '/', SAMPLE_FILE_PREFIX, num2str(ch), '.', FILE_EXT);
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
% load timestamps
|
||||||
|
f = fopen(FN_TS ,'r');
|
||||||
|
ts = fread(f,[2,Inf],'uint32');
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
% take first element as t0
|
||||||
|
ts(1,:) = ts(1,:) - ts(1,1);
|
||||||
|
|
||||||
|
% create fractional timestamp
|
||||||
|
ts = ts(1,:) + ts(2,:) / 1E+09;
|
||||||
|
|
||||||
|
% get sample count
|
||||||
|
sample_cnt = length(ts);
|
||||||
|
|
||||||
|
% load samples
|
||||||
|
s = zeros(CHANNELS, sample_cnt);
|
||||||
|
for ch=1:CHANNELS
|
||||||
|
f = fopen(FN_SAMPLE{ch},'r');
|
||||||
|
d = fread(f, [1,Inf],'int16');
|
||||||
|
s(ch,:) = d;
|
||||||
|
fclose(f);
|
||||||
|
end
|
||||||
|
|
||||||
|
plot(ts(1,1:100), s(1,1:100))
|
5
MATLAB/readdump.m
Normal file
5
MATLAB/readdump.m
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
f = fopen('wf.dat','r');
|
||||||
|
d = fread(f,[2, 44100],'int16');
|
||||||
|
s = d(1,1:1000);
|
||||||
|
plot(s);
|
||||||
|
fclose(f);
|
197
main.cpp
Normal file
197
main.cpp
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/* Use the newer ALSA API */
|
||||||
|
#define ALSA_PCM_NEW_HW_PARAMS_API
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include "src/ALSADevice.h"
|
||||||
|
#include "src/SampleWriter.h"
|
||||||
|
#include "src/audio_types.h"
|
||||||
|
|
||||||
|
//int main() {
|
||||||
|
// long loops;
|
||||||
|
// int rc;
|
||||||
|
// int size;
|
||||||
|
// snd_pcm_t *handle;
|
||||||
|
// snd_pcm_hw_params_t *params;
|
||||||
|
// unsigned int val;
|
||||||
|
// int dir;
|
||||||
|
// snd_pcm_uframes_t frames;
|
||||||
|
//
|
||||||
|
// /* Open PCM device for playback. */
|
||||||
|
// rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_PLAYBACK, 0);
|
||||||
|
// if (rc < 0) {
|
||||||
|
// fprintf(stderr,"unable to open pcm device: %s\n", snd_strerror(rc));
|
||||||
|
// exit(1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /* Allocate a hardware parameters object. */
|
||||||
|
// snd_pcm_hw_params_alloca(¶ms);
|
||||||
|
//
|
||||||
|
// /* Fill it in with default values. */
|
||||||
|
// snd_pcm_hw_params_any(handle, params);
|
||||||
|
//
|
||||||
|
// /* Set the desired hardware parameters. */
|
||||||
|
//
|
||||||
|
// /* Interleaved mode */
|
||||||
|
// snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
|
//
|
||||||
|
// /* Signed 16-bit little-endian format */
|
||||||
|
// snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
|
||||||
|
//
|
||||||
|
// /* Two channels (stereo) */
|
||||||
|
// snd_pcm_hw_params_set_channels(handle, params, 2);
|
||||||
|
//
|
||||||
|
// /* 44100 bits/second sampling rate (CD quality) */
|
||||||
|
// unsigned sampleRate = 44100;
|
||||||
|
// val = sampleRate;
|
||||||
|
// snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
|
||||||
|
//
|
||||||
|
// /* Set period size to 32 frames. */
|
||||||
|
// frames = 32;
|
||||||
|
// snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
|
||||||
|
//
|
||||||
|
// /* Write the parameters to the driver */
|
||||||
|
// rc = snd_pcm_hw_params(handle, params);
|
||||||
|
// if (rc < 0) {
|
||||||
|
// fprintf(stderr,"unable to set hw parameters: %s\n", snd_strerror(rc));
|
||||||
|
// exit(1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /* Use a buffer large enough to hold one period */
|
||||||
|
// snd_pcm_hw_params_get_period_size(params, &frames, &dir);
|
||||||
|
// size = frames * 4; /* 2 bytes/sample, 2 channels */
|
||||||
|
// int16_t buffer[frames * 2];
|
||||||
|
//
|
||||||
|
// /* We want to loop for 5 seconds */
|
||||||
|
// snd_pcm_hw_params_get_period_time(params, &val, &dir);
|
||||||
|
// /* 5 seconds in microseconds divided by
|
||||||
|
// * period time */
|
||||||
|
//
|
||||||
|
// loops = 2 * 1000000 / val;
|
||||||
|
//
|
||||||
|
// float f = 440;
|
||||||
|
// float df = 2 * M_PI * f / sampleRate;
|
||||||
|
// float amplitude = 0.01;
|
||||||
|
// float phase = 0;
|
||||||
|
//
|
||||||
|
// //FILE* wf = fopen("wf.dat", "wb");
|
||||||
|
//
|
||||||
|
// for (size_t l = 0; l < loops; l++) {
|
||||||
|
// for (size_t i = 0; i < 2 * frames; i += 2) {
|
||||||
|
// buffer[i] = 32767 * amplitude * sin(phase);
|
||||||
|
// buffer[i+1] = buffer[i];
|
||||||
|
// phase += df;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //fwrite(buffer, frames * 4, 1, wf);
|
||||||
|
//
|
||||||
|
// if (phase > 2 * M_PI) {
|
||||||
|
// phase -= 2 * M_PI;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// std::cout << std::to_string(snd_pcm_avail(handle)) << std::endl;
|
||||||
|
//
|
||||||
|
// rc = snd_pcm_writei(handle, buffer, frames);
|
||||||
|
// if (rc == -EPIPE) {
|
||||||
|
// /* EPIPE means underrun */
|
||||||
|
// fprintf(stderr, "underrun occurred\n");
|
||||||
|
// snd_pcm_prepare(handle);
|
||||||
|
// } else if (rc < 0) {
|
||||||
|
// fprintf(stderr, "error from writei: %s\n", snd_strerror(rc));
|
||||||
|
// } else if (rc != (int) frames) {
|
||||||
|
// fprintf(stderr, "short write, write %d frames\n", rc);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //fclose(wf);
|
||||||
|
//
|
||||||
|
// snd_pcm_drain(handle);
|
||||||
|
// snd_pcm_close(handle);
|
||||||
|
// //free(buffer);
|
||||||
|
//
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
#define Fs (48000) // sampling frequency
|
||||||
|
#define F (440) // signal frequency
|
||||||
|
#define K (0.1) // amplitude
|
||||||
|
|
||||||
|
void generate_sine(int16_t *pBuf, uint32_t n) {
|
||||||
|
double y;
|
||||||
|
static double phase = 0, dt = 1.0 / Fs;
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
y = K * 0.5 * (1 + sin(phase));
|
||||||
|
phase += 2 * M_PI * dt * F;
|
||||||
|
|
||||||
|
if (phase > (2 * M_PI)) {
|
||||||
|
phase -= 2 * M_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
pBuf[2 * i + 1] = pBuf[2 * i] = ((int16_t) (y * 0x7FFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pRecvBuf[8096] __attribute__ ((aligned (32)));
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]) {
|
||||||
|
ALSADevice dev(PERIOD_LEN, SAMPLE_RATE);
|
||||||
|
MemoryPool<int16_t> pool(STEREO_BUF_LEN, 1000);
|
||||||
|
//SampleWriter<AudioSampleType> sw("test", 2, STEREO_BUF_LEN / 2);
|
||||||
|
|
||||||
|
//std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
/*for (size_t i = 0; i < 1000; i++) {
|
||||||
|
auto p = pool.alloc();
|
||||||
|
generate_sine(p->pBlock, 324);
|
||||||
|
dev.write(p);
|
||||||
|
//std::cout << pool.avail() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int soc = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(soc, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
|
||||||
|
|
||||||
|
sockaddr_in addr;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
addr.sin_port = htons(atoi(argv[1]));
|
||||||
|
|
||||||
|
bind(soc, (const sockaddr *)&addr, sizeof(addr));
|
||||||
|
|
||||||
|
AudioPayload * pAudioPayload = reinterpret_cast<AudioPayload *>(pRecvBuf);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
//while (sw.getSampleCnt() < 100000) {
|
||||||
|
auto p = pool.alloc();
|
||||||
|
|
||||||
|
recv(soc, pRecvBuf, 8096, 0);
|
||||||
|
|
||||||
|
//std::cout << pAudioPayload->timestamp_s << ' ' << pAudioPayload->timestamp_ns << std::endl;
|
||||||
|
|
||||||
|
Timestamp ts = { pAudioPayload->timestamp_s, pAudioPayload->timestamp_ns };
|
||||||
|
//sw.addSamples(pAudioPayload->pData, ts);
|
||||||
|
|
||||||
|
memcpy(p->pBlock, pAudioPayload->pData, STEREO_BUF_LEN * sizeof(AudioSampleType));
|
||||||
|
|
||||||
|
dev.write(p);
|
||||||
|
|
||||||
|
//std::cout << pool.avail() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(soc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
177
src/ALSADevice.cpp
Normal file
177
src/ALSADevice.cpp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 09. 20..
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
#include "ALSADevice.h"
|
||||||
|
|
||||||
|
ALSADevice::ALSADevice(snd_pcm_uframes_t periodLen, unsigned sampleRate) {
|
||||||
|
mPeriodLen = periodLen;
|
||||||
|
mSampleRate = sampleRate;
|
||||||
|
openDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALSADevice::~ALSADevice() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ALSADevice::openDevice() {
|
||||||
|
/* Open PCM device for playback. */
|
||||||
|
int ret;
|
||||||
|
ret = snd_pcm_open(&pmHandle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Unable to open pcm device: %s\n", snd_strerror(ret));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate a hardware parameters object. */
|
||||||
|
snd_pcm_hw_params_alloca(&pmHwParams);
|
||||||
|
|
||||||
|
/* Fill it in with default values. */
|
||||||
|
snd_pcm_hw_params_any(pmHandle, pmHwParams);
|
||||||
|
|
||||||
|
/* Interleaved mode */
|
||||||
|
snd_pcm_hw_params_set_access(pmHandle, pmHwParams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
|
|
||||||
|
/* Signed 16-bit little-endian format */
|
||||||
|
snd_pcm_hw_params_set_format(pmHandle, pmHwParams, SND_PCM_FORMAT_S16_LE);
|
||||||
|
|
||||||
|
/* Two channels (stereo) */
|
||||||
|
snd_pcm_hw_params_set_channels(pmHandle, pmHwParams, 2);
|
||||||
|
|
||||||
|
/* Set sampling rate */
|
||||||
|
int dir;
|
||||||
|
snd_pcm_hw_params_set_rate_near(pmHandle, pmHwParams, &mSampleRate, &dir);
|
||||||
|
|
||||||
|
/* Set period size to mPeriodLen frames. */
|
||||||
|
snd_pcm_hw_params_set_period_size_near(pmHandle, pmHwParams, &mPeriodLen, &dir);
|
||||||
|
|
||||||
|
/* Write the parameters to the driver */
|
||||||
|
ret = snd_pcm_hw_params(pmHandle, pmHwParams);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(ret));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use a buffer large enough to hold one period */
|
||||||
|
snd_pcm_hw_params_get_period_size(pmHwParams, &mPeriodLen, &dir);
|
||||||
|
|
||||||
|
/* Allocate sample buffer */
|
||||||
|
//pmPeriodBufLeft = std::shared_ptr<int16_t>(new int16_t[mPeriodLen]);
|
||||||
|
//pmPeriodBufRight = std::shared_ptr<int16_t>(new int16_t[mPeriodLen]);
|
||||||
|
|
||||||
|
/* Get buffer duration */
|
||||||
|
snd_pcm_hw_params_get_period_time(pmHwParams, &mPeriodDuration_us, &dir);
|
||||||
|
|
||||||
|
/* Start write thread */
|
||||||
|
mRunning = true;
|
||||||
|
pmWriteThread = std::make_shared<std::thread>(fn_WriteThread, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ALSADevice::fn_WriteThread(ALSADevice *pDev) {
|
||||||
|
//int16_t pPeriodIntvBuf[pDev->mPeriodLen * 2]; // interlaved channel buffer
|
||||||
|
int16_t * pPlayBuf; // buffer to be passed to write routine
|
||||||
|
std::shared_ptr<MemoryChunk<int16_t>> pChunk; // memory chunk storing interleaved sample data
|
||||||
|
|
||||||
|
int16_t pSilence[pDev->mPeriodLen * 2]; // buffer storing a single period-long silence
|
||||||
|
snd_pcm_format_set_silence(SND_PCM_FORMAT_S16_LE, pSilence, pDev->mPeriodLen * 2); // create silence
|
||||||
|
|
||||||
|
snd_pcm_start(pDev->pmHandle);
|
||||||
|
|
||||||
|
unsigned long long iterCnt = 0;
|
||||||
|
bool playbackStopped = true; // indicates wheter playback has been stopped due to frame dropoupts
|
||||||
|
size_t startThreshold = 10; // amount of frames available to play to start writing to decive
|
||||||
|
size_t optimalFillLevel = 6; // optimal amount of frames to be stored in the buffer
|
||||||
|
size_t minimalFramesStored = 3; // minimal amount of frames stored in queue to start playing
|
||||||
|
|
||||||
|
while (pDev->mRunning) {
|
||||||
|
//snd_pcm_sframes_t avail = snd_pcm_avail(pDev->pmHandle); // get available frames on the hardware
|
||||||
|
|
||||||
|
//pDev->mQMtx.lock();
|
||||||
|
|
||||||
|
// wait for data to arrive
|
||||||
|
pDev->mPlaySem.wait();
|
||||||
|
|
||||||
|
// start only if sufficient amount of frames are available
|
||||||
|
if (pDev->mChunkQ.size() < startThreshold && playbackStopped) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
playbackStopped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//pDev->mQMtx.unlock();
|
||||||
|
|
||||||
|
bool firstWriteIteration = true; // első iteráció a lejátszásra
|
||||||
|
|
||||||
|
//std::cout << pDev->mChunkQ.size() << std::endl;
|
||||||
|
|
||||||
|
// while the input queue contains at least a single chunk of sound data...
|
||||||
|
while (pDev->mChunkQ.size() > optimalFillLevel || (firstWriteIteration && pDev->mChunkQ.size() > minimalFramesStored)) {
|
||||||
|
pChunk = pDev->mChunkQ.front(); // get chunk
|
||||||
|
pPlayBuf = pChunk->pBlock; // change play buffer
|
||||||
|
|
||||||
|
// write frames to device
|
||||||
|
snd_pcm_sframes_t ret = snd_pcm_writei(pDev->pmHandle, pPlayBuf, pDev->mPeriodLen);
|
||||||
|
|
||||||
|
// error handling
|
||||||
|
if (ret == -EPIPE) {
|
||||||
|
std::cerr << "Underrun" << std::endl;
|
||||||
|
snd_pcm_prepare(pDev->pmHandle);
|
||||||
|
//playbackStopped = true; // stop playback, gather some frames
|
||||||
|
} else if (ret < 0) {
|
||||||
|
std::cerr << "Error from snd_pcm_writen: " << snd_strerror((int) ret) << std::endl;
|
||||||
|
//playbackStopped = true; // ...
|
||||||
|
} else if (ret != (int) pDev->mPeriodLen) {
|
||||||
|
std::cerr << "Short write, only " << std::to_string(ret) << " frames written!" << std::endl;
|
||||||
|
} else { // everything is OK
|
||||||
|
|
||||||
|
// release chunks since data contained in them have been processed
|
||||||
|
pChunk->pPool->free(pChunk);
|
||||||
|
pChunk = nullptr;
|
||||||
|
|
||||||
|
// pop frames written to device
|
||||||
|
pDev->mChunkQ.pop();
|
||||||
|
|
||||||
|
// first iteration done
|
||||||
|
firstWriteIteration = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment iteration counter
|
||||||
|
iterCnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ALSADevice::write(const std::shared_ptr<MemoryChunk<int16_t>> &pChunk) {
|
||||||
|
// aquire lock on queue manipulation
|
||||||
|
//mQMtx.lock();
|
||||||
|
|
||||||
|
// store chunks into the queues
|
||||||
|
mChunkQ.push(pChunk);
|
||||||
|
|
||||||
|
mPlaySem.post();
|
||||||
|
|
||||||
|
// release lock
|
||||||
|
//mQMtx.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ALSADevice::closeDevice() {
|
||||||
|
mRunning = false; // FIXME...
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
|
||||||
|
float f = 440;
|
||||||
|
float df = 2 * M_PI * f / pDev->mSampleRate;
|
||||||
|
float amplitude = 0.01;
|
||||||
|
float phase = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2 * pDev->mPeriodLen; i += 2) {
|
||||||
|
pSilenceRight.get()[i] = 32767 * amplitude * sin(phase);
|
||||||
|
phase += df;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
38
src/ALSADevice.h
Normal file
38
src/ALSADevice.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 09. 20..
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WFR_ALSADEVICE_H
|
||||||
|
#define WFR_ALSADEVICE_H
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include "MemoryPool.h"
|
||||||
|
#include "../utils/Semaphore.h"
|
||||||
|
|
||||||
|
class ALSADevice {
|
||||||
|
private:
|
||||||
|
snd_pcm_uframes_t mPeriodLen; // alsa period length
|
||||||
|
snd_pcm_t * pmHandle; // handle to alsa device
|
||||||
|
snd_pcm_hw_params_t * pmHwParams; // pointer to hardware parameters
|
||||||
|
unsigned mSampleRate; // hardware sample rate
|
||||||
|
unsigned mPeriodDuration_us; // period duration in microseconds
|
||||||
|
bool mRunning; // flag controlling flow in write thread
|
||||||
|
std::queue<std::shared_ptr<MemoryChunk<int16_t>>> mChunkQ; // queues storing received chunks of sound data
|
||||||
|
//std::mutex mQMtx; // mutex for queue management
|
||||||
|
Semaphore mPlaySem; // semaphore for playing samples
|
||||||
|
private:
|
||||||
|
static void fn_WriteThread(ALSADevice * pDev); // routine function executed in write thread
|
||||||
|
std::shared_ptr<std::thread> pmWriteThread; // thread managing write operations
|
||||||
|
private:
|
||||||
|
void openDevice();
|
||||||
|
void closeDevice();
|
||||||
|
public:
|
||||||
|
ALSADevice(snd_pcm_uframes_t periodLen, unsigned sampleRate); // constr.
|
||||||
|
virtual ~ALSADevice(); // destr.
|
||||||
|
void write(const std::shared_ptr<MemoryChunk<int16_t>> &pChunk); // function receiving a chunk of sound data
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //WFR_ALSADEVICE_H
|
6
src/MemoryPool.cpp
Normal file
6
src/MemoryPool.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 09. 20..
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MemoryPool.h"
|
||||||
|
|
86
src/MemoryPool.h
Normal file
86
src/MemoryPool.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 09. 20..
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WFR_MEMORYPOOL_H
|
||||||
|
#define WFR_MEMORYPOOL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class MemoryPool;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct MemoryChunk{
|
||||||
|
public:
|
||||||
|
T * pBlock; // memory block
|
||||||
|
MemoryPool<T> * pPool; // pool owning chunk
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class MemoryPool {
|
||||||
|
private:
|
||||||
|
size_t mChunkCnt, mChunkSize; // memory chunk size and count
|
||||||
|
size_t mBlockSize; // size of allocated contigous memory block in element count
|
||||||
|
std::shared_ptr<T> pmBlock; // the block
|
||||||
|
std::queue<std::shared_ptr<MemoryChunk<T>>> mFreeChunks;
|
||||||
|
public:
|
||||||
|
void init(size_t chunkSize, size_t count) {
|
||||||
|
if (mChunkSize != 0 || mChunkCnt != 0 || mBlockSize != 0) {
|
||||||
|
throw std::runtime_error("Memory pool already initialized, cannot reinit!"); // memory pool already initialized, exit now
|
||||||
|
}
|
||||||
|
|
||||||
|
// save parameters
|
||||||
|
mChunkSize = chunkSize;
|
||||||
|
mChunkCnt = count;
|
||||||
|
mBlockSize = mChunkSize * mChunkCnt;
|
||||||
|
|
||||||
|
// allocate pool block
|
||||||
|
pmBlock = std::shared_ptr<T>(new T[mBlockSize]);
|
||||||
|
|
||||||
|
// create chunks
|
||||||
|
for (size_t i = 0; i < mChunkCnt; i++) {
|
||||||
|
std::shared_ptr<MemoryChunk<T>> pChunk = std::make_shared<MemoryChunk<T>>();
|
||||||
|
pChunk->pBlock = pmBlock.get() + i*mChunkSize;
|
||||||
|
pChunk->pPool = this;
|
||||||
|
mFreeChunks.push(pChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
// default constr.
|
||||||
|
MemoryPool() : mChunkSize(0), mChunkCnt(0), mBlockSize(0) {};
|
||||||
|
|
||||||
|
// constructor with size parameters
|
||||||
|
MemoryPool(size_t chunkSize, size_t count) : mChunkSize(0), mChunkCnt(0), mBlockSize(0) // constr. (WARNING: chunkSize defines the amount of elements of T to fit in a chunk)
|
||||||
|
{
|
||||||
|
init(chunkSize, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MemoryChunk<T>> alloc() // allocate a chunk of memory
|
||||||
|
{
|
||||||
|
std::shared_ptr<MemoryChunk<T>> pChunk = mFreeChunks.front();
|
||||||
|
mFreeChunks.pop();
|
||||||
|
return pChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool free(const std::shared_ptr<MemoryChunk<T>>& pMemChunk) { // release a chunk of memory
|
||||||
|
if ((pMemChunk->pPool == this) && (pmBlock.get() <= pMemChunk->pBlock) && (pMemChunk->pBlock < pmBlock.get() + mBlockSize)) {
|
||||||
|
mFreeChunks.push(pMemChunk);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t avail() const { // get count of available blocks
|
||||||
|
return mFreeChunks.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //WFR_MEMORYPOOL_H
|
41
src/SampleReceiver.h
Normal file
41
src/SampleReceiver.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 11. 02..
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WFR_SAMPLERECEIVER_H
|
||||||
|
#define WFR_SAMPLERECEIVER_H
|
||||||
|
|
||||||
|
#include "MemoryPool.h"
|
||||||
|
#include "Timestamp.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class SampleReceiver {
|
||||||
|
private:
|
||||||
|
static constexpr size_t MEMORY_BLOCK_COUNT = 16;
|
||||||
|
private:
|
||||||
|
const size_t mChPN; // number of channels PAIRS
|
||||||
|
const size_t mBlockSize; // sample block size
|
||||||
|
MemoryPool<T> mMemPool; // memory pool for buffers
|
||||||
|
private:
|
||||||
|
std::vector<ssize_t> mOldestChkIdx, mNewestChkIdx; // vectors for storing oldest and chunk indices for each pair of channels
|
||||||
|
std::vector<Timestamp> mLastTimestamp; // vector storing last timestamp for each pair of channels
|
||||||
|
public:
|
||||||
|
// constr.
|
||||||
|
SampleReceiver(size_t chPN, size_t blockSize) : mChPN(chPN), mBlockSize(blockSize) {
|
||||||
|
// calculate memory pool parameters [area for timestamps + area for samples]
|
||||||
|
size_t chunkSize = mChPN * mBlockSize * 2 * sizeof(uint32_t) + mChPN * 2 * mBlockSize * sizeof(T);
|
||||||
|
|
||||||
|
// create memory pool
|
||||||
|
mMemPool.init(chunkSize, MEMORY_BLOCK_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive a block of data
|
||||||
|
bool recvBlock(const T* pData, const Timestamp& ts, size_t ch) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //WFR_SAMPLERECEIVER_H
|
195
src/SampleWriter.h
Normal file
195
src/SampleWriter.h
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 11. 01..
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WFR_SAMPLEWRITER_H
|
||||||
|
#define WFR_SAMPLEWRITER_H
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <queue>
|
||||||
|
#include "Timestamp.h"
|
||||||
|
|
||||||
|
#define DIRSEP '/'
|
||||||
|
|
||||||
|
template<typename T> // ch: number of channels, T: sample datatype
|
||||||
|
class SampleWriter {
|
||||||
|
public:
|
||||||
|
static const std::string TS_FILENAME; // filename for timestamp file
|
||||||
|
static const std::string SAMPLE_FILENAME_PREFIX; // filename prefix for sample file
|
||||||
|
static const std::string FILE_EXT; // datafile extensions
|
||||||
|
public:
|
||||||
|
// timestamp record
|
||||||
|
struct TimestampRecord {
|
||||||
|
public:
|
||||||
|
uint32_t ts_s, ts_ns; // timestamp
|
||||||
|
public:
|
||||||
|
TimestampRecord &operator=(const Timestamp &ts) {
|
||||||
|
ts_s = (uint32_t) ts.s;
|
||||||
|
ts_ns = (uint32_t) ts.ns;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const size_t mChN; // number of channel PAIRS, immutable
|
||||||
|
const size_t mBlockSize; // buffer size, immutable
|
||||||
|
private:
|
||||||
|
std::shared_ptr<TimestampRecord> mpTs; // timestamp records
|
||||||
|
std::vector<std::shared_ptr<T>> mpSamples; // sample buffer array
|
||||||
|
|
||||||
|
std::string mTsFileName; // timestamp filename
|
||||||
|
std::ofstream mTsFile; // timestamp file
|
||||||
|
std::vector<std::string> mSampleFileNames; // sample file names
|
||||||
|
std::vector<std::ofstream> mSampleFiles; // sample output files
|
||||||
|
private:
|
||||||
|
Timestamp mTs[2]; // previous and current timestamp
|
||||||
|
private:
|
||||||
|
unsigned long long mSamplesN; // number of samples written until now
|
||||||
|
private:
|
||||||
|
// resize sample and record buffers
|
||||||
|
void prepareBuffers() {
|
||||||
|
// allocate timestamp data buffer
|
||||||
|
mpTs = std::shared_ptr<TimestampRecord>(new TimestampRecord[mBlockSize]);
|
||||||
|
|
||||||
|
// allocate sample data buffers
|
||||||
|
mpSamples.resize(mChN);
|
||||||
|
for (size_t i = 0; i < mChN; i++) {
|
||||||
|
mpSamples[i] = std::shared_ptr<T>(new T[mBlockSize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare dataset environment
|
||||||
|
void prepareDatadir(const std::string &datasetName) {
|
||||||
|
struct stat sb;
|
||||||
|
if (stat(datasetName.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) { // if directory exists then leave it as it is
|
||||||
|
//unlink(datasetName.c_str()); FIXME
|
||||||
|
} else if (mkdir(datasetName.c_str(), 0777) == -1) { // else: create directory for dataset
|
||||||
|
throw std::runtime_error("Could not create directory " + datasetName + "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string datadir_dirsep = datasetName + DIRSEP;
|
||||||
|
|
||||||
|
// generate file names
|
||||||
|
mTsFileName = TS_FILENAME + '.' + FILE_EXT; // timestamp
|
||||||
|
|
||||||
|
// samples
|
||||||
|
mSampleFileNames.resize(mChN);
|
||||||
|
for (size_t i = 0; i < mChN; i++) {
|
||||||
|
mSampleFileNames[i] = SAMPLE_FILENAME_PREFIX + std::to_string(i) + '.' + FILE_EXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open timestamp file
|
||||||
|
mTsFile.open(datadir_dirsep + mTsFileName, std::ios_base::binary);
|
||||||
|
if (!mTsFile.is_open()) {
|
||||||
|
throw std::runtime_error("Could not open timestamp file " + mTsFileName + "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// open sample files
|
||||||
|
mSampleFiles.resize(mChN);
|
||||||
|
for (size_t i = 0; i < mChN; i++) {
|
||||||
|
mSampleFiles[i].open(datadir_dirsep + mSampleFileNames[i], std::ios_base::binary);
|
||||||
|
if (!mSampleFiles[i].is_open()) {
|
||||||
|
throw std::runtime_error("Could not open sample file " + mSampleFileNames[i] + "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close files
|
||||||
|
void closeFiles() {
|
||||||
|
// close file storing timestamp files
|
||||||
|
mTsFile.close();
|
||||||
|
|
||||||
|
// close files stroring samples
|
||||||
|
for (size_t i = 0; i < mChN; i++) {
|
||||||
|
mSampleFiles[i].close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SampleWriter(const std::string &datasetName, size_t chN, size_t bufSize) : mChN(chN), mBlockSize(bufSize), mSamplesN(0) { // constr.
|
||||||
|
prepareBuffers();
|
||||||
|
prepareDatadir(datasetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SampleWriter() {
|
||||||
|
closeFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
// add n samples to file
|
||||||
|
void addSamples(const T *pSamples, const Timestamp &ts) {
|
||||||
|
// shift FIFO and store new timestamp
|
||||||
|
mTs[1] = mTs[0];
|
||||||
|
mTs[0] = ts;
|
||||||
|
|
||||||
|
// if previous timestamp is empty, then don't store samples since time interpolation can not be done
|
||||||
|
if (mTs[1].empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate time step size
|
||||||
|
Timestamp d = (mTs[0] - mTs[1]) / (double) mBlockSize;
|
||||||
|
|
||||||
|
// timestamp for time interpolation
|
||||||
|
Timestamp ts_interp = mTs[1];
|
||||||
|
|
||||||
|
// get channel pointers
|
||||||
|
T *ppCh[mChN];
|
||||||
|
for (size_t i = 0; i < mChN; i++) {
|
||||||
|
ppCh[i] = mpSamples[i].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract samples
|
||||||
|
for (size_t i = 0; i < mBlockSize; i++) {
|
||||||
|
// select record
|
||||||
|
T *pRawSamples = (short *) (pSamples + i * mChN);
|
||||||
|
|
||||||
|
// store sample for a channel
|
||||||
|
for (size_t ch = 0; ch < mChN; ch++) {
|
||||||
|
ppCh[ch][i] = pRawSamples[ch];
|
||||||
|
}
|
||||||
|
|
||||||
|
// step time
|
||||||
|
ts_interp += d;
|
||||||
|
|
||||||
|
// store timestamp
|
||||||
|
mpTs.get()[i] = ts_interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to file newly created records
|
||||||
|
|
||||||
|
// write timestamps
|
||||||
|
mTsFile.write((const char *) mpTs.get(), mBlockSize * sizeof(TimestampRecord));
|
||||||
|
|
||||||
|
// write samples
|
||||||
|
for (size_t ch = 0; ch < mChN; ch++) {
|
||||||
|
mSampleFiles[ch].write((const char *) ppCh[ch], mBlockSize * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment counter
|
||||||
|
mSamplesN += mBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get number of samples written out
|
||||||
|
unsigned long long getSampleCnt() const {
|
||||||
|
return mSamplesN;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// static fields
|
||||||
|
template<typename T>
|
||||||
|
const std::string SampleWriter<T>::TS_FILENAME = "ts";
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const std::string SampleWriter<T>::SAMPLE_FILENAME_PREFIX = "ch_";
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const std::string SampleWriter<T>::FILE_EXT = "dat";
|
||||||
|
|
||||||
|
#endif //WFR_SAMPLEWRITER_H
|
112
src/Timestamp.h
Normal file
112
src/Timestamp.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 11. 01..
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WFR_TIMESTAMP_H
|
||||||
|
#define WFR_TIMESTAMP_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class Timestamp {
|
||||||
|
public:
|
||||||
|
static constexpr uint32_t NANO_PREFIX = 1E+09;
|
||||||
|
public:
|
||||||
|
int64_t s, ns; // time data
|
||||||
|
public:
|
||||||
|
// constr.
|
||||||
|
explicit Timestamp(int64_t s = 0, int64_t ns = 0): s(s), ns(ns) {};
|
||||||
|
|
||||||
|
// initialization with initializer list
|
||||||
|
Timestamp(const std::initializer_list<int64_t>& il) : s(0), ns(0) {
|
||||||
|
this->operator=(il);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignment
|
||||||
|
Timestamp& operator=(const std::initializer_list<int64_t>& il) {
|
||||||
|
if (il.size() >= 2) {
|
||||||
|
s = *il.begin();
|
||||||
|
ns = *(il.begin() + 1);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timestamp& operator=(const Timestamp& other) {
|
||||||
|
s = other.s;
|
||||||
|
ns = other.ns;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is it empty?
|
||||||
|
bool empty() const {
|
||||||
|
return (s == 0) && (ns == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to nanoseconds
|
||||||
|
int64_t to_ns() const {
|
||||||
|
return (int64_t)NANO_PREFIX * s + ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert from nanoseconds
|
||||||
|
void from_ns(int64_t ns) {
|
||||||
|
this->s = ns / (int64_t)NANO_PREFIX;
|
||||||
|
this->ns = (ns - s * NANO_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add timestamps
|
||||||
|
Timestamp& operator+=(const Timestamp& other) {
|
||||||
|
int64_t sum_ns = to_ns() + other.to_ns();
|
||||||
|
from_ns(sum_ns);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiply timestamp
|
||||||
|
Timestamp operator*(double c) const {
|
||||||
|
Timestamp ts;
|
||||||
|
ts.from_ns((int64_t)(c * this->to_ns()));
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// divide timestamp by scalar
|
||||||
|
Timestamp operator/(double d) const {
|
||||||
|
return this->operator*(1 / d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// substract timestamps
|
||||||
|
Timestamp& operator-=(const Timestamp& other) {
|
||||||
|
return this->operator+=(other * (-1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add timestamps
|
||||||
|
Timestamp operator+(const Timestamp& other) const {
|
||||||
|
Timestamp ts;
|
||||||
|
ts.from_ns(this->to_ns() + other.to_ns());
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// substract timestamps
|
||||||
|
Timestamp operator-(const Timestamp& other) const {
|
||||||
|
Timestamp ts = *this + other * (-1);
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// comparison
|
||||||
|
bool operator>(const Timestamp& other) const {
|
||||||
|
return this->to_ns() > other.to_ns();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Timestamp& other) const {
|
||||||
|
return this->to_ns() < other.to_ns();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Timestamp& other) const {
|
||||||
|
return this->to_ns() == other.to_ns();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Timestamp& other) const {
|
||||||
|
return !this->operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //WFR_TIMESTAMP_H
|
22
src/audio_types.h
Normal file
22
src/audio_types.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 11. 02..
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WFR_AUDIO_TYPES_H
|
||||||
|
#define WFR_AUDIO_TYPES_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define STEREO_BUF_LEN (648)
|
||||||
|
#define PERIOD_LEN (STEREO_BUF_LEN / 2)
|
||||||
|
#define SAMPLE_RATE (48000)
|
||||||
|
|
||||||
|
typedef int16_t AudioSampleType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t timestamp_s, timestamp_ns; // timestamp
|
||||||
|
uint32_t sample_cnt; // count of samples in packet
|
||||||
|
AudioSampleType pData[STEREO_BUF_LEN]; // buffer for stereo data
|
||||||
|
} AudioPayload;
|
||||||
|
|
||||||
|
#endif //WFR_AUDIO_TYPES_H
|
25
utils/Semaphore.cpp
Normal file
25
utils/Semaphore.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 09. 12..
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Semaphore.h"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
Semaphore::Semaphore() {
|
||||||
|
sem_init(&mSem, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Semaphore::~Semaphore() {
|
||||||
|
sem_destroy(&mSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Semaphore::post() {
|
||||||
|
sem_post(&mSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Semaphore::wait() {
|
||||||
|
sem_wait(&mSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
26
utils/Semaphore.h
Normal file
26
utils/Semaphore.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Created by epagris on 2021. 09. 12..
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef MULTIPROJECTOR_SEMAPHORE_H
|
||||||
|
#define MULTIPROJECTOR_SEMAPHORE_H
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <semaphore.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
class Semaphore {
|
||||||
|
private:
|
||||||
|
sem_t mSem;
|
||||||
|
public:
|
||||||
|
Semaphore(); // konstr.
|
||||||
|
virtual ~Semaphore(); // destr.
|
||||||
|
void post(); // post
|
||||||
|
void wait(); // wait (blokkoló!)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //MULTIPROJECTOR_SEMAPHORE_H
|
Loading…
x
Reference in New Issue
Block a user