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