kopia lustrzana https://github.com/markondej/fm_transmitter
commit
331412533e
|
@ -36,7 +36,8 @@
|
|||
#include <csignal>
|
||||
#include <unistd.h>
|
||||
|
||||
bool stop = false;
|
||||
std::mutex mtx;
|
||||
bool enable = true;
|
||||
Transmitter *transmitter = nullptr;
|
||||
|
||||
void sigIntHandler(int sigNum)
|
||||
|
@ -44,7 +45,7 @@ void sigIntHandler(int sigNum)
|
|||
if (transmitter != nullptr) {
|
||||
std::cout << "Stopping..." << std::endl;
|
||||
transmitter->Stop();
|
||||
stop = true;
|
||||
enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,14 +98,14 @@ int main(int argc, char** argv)
|
|||
if ((optind == argc) && loop) {
|
||||
optind = filesOffset;
|
||||
}
|
||||
WaveReader reader(filename != "-" ? filename : std::string(), stop);
|
||||
WaveReader reader(filename != "-" ? filename : std::string(), enable, mtx);
|
||||
WaveHeader header = reader.GetHeader();
|
||||
std::cout << "Playing: " << reader.GetFilename() << ", "
|
||||
<< header.sampleRate << " Hz, "
|
||||
<< header.bitsPerSample << " bits, "
|
||||
<< ((header.channels > 0x01) ? "stereo" : "mono") << std::endl;
|
||||
transmitter->Transmit(reader, frequency, bandwidth, dmaChannel, optind < argc);
|
||||
} while (!stop && (optind < argc));
|
||||
} while (enable && (optind < argc));
|
||||
} catch (std::exception &catched) {
|
||||
std::cout << "Error: " << catched.what() << std::endl;
|
||||
result = EXIT_FAILURE;
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x86">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x86</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x86">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x86</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{f1c523ba-9fe5-413c-8a54-8c7563f63eb6}</ProjectGuid>
|
||||
<Keyword>Linux</Keyword>
|
||||
<RootNamespace>fm_transmitter</RootNamespace>
|
||||
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
|
||||
<ApplicationType>Linux</ApplicationType>
|
||||
<ApplicationTypeRevision>1.0</ApplicationTypeRevision>
|
||||
<TargetLinuxPlatform>Generic</TargetLinuxPlatform>
|
||||
<LinuxProjectType>{D51BCBC9-82E9-4017-911E-C93873C4EA2B}</LinuxProjectType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings" />
|
||||
<ImportGroup Label="Shared" />
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<RemoteLdCommmandTimeout>0</RemoteLdCommmandTimeout>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="fm_transmitter.cpp" />
|
||||
<ClCompile Include="mailbox.cpp" />
|
||||
<ClCompile Include="transmitter.cpp" />
|
||||
<ClCompile Include="wave_reader.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="mailbox.hpp" />
|
||||
<ClInclude Include="transmitter.hpp" />
|
||||
<ClInclude Include="wave_reader.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
<FunctionBinding>false</FunctionBinding>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<FunctionBinding>false</FunctionBinding>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
<FunctionBinding>false</FunctionBinding>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<FunctionBinding>false</FunctionBinding>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
<FunctionBinding>false</FunctionBinding>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<FunctionBinding>false</FunctionBinding>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>/opt/vc/include;%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<CAdditionalWarning>
|
||||
</CAdditionalWarning>
|
||||
<CppAdditionalWarning>
|
||||
</CppAdditionalWarning>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>/opt/vc/lib;%(Link.AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Link>
|
||||
<LibraryDependencies>pthread;bcm_host;%(LibraryDependencies)</LibraryDependencies>
|
||||
<UnresolvedSymbolReferences>false</UnresolvedSymbolReferences>
|
||||
<NoExecStackRequired>false</NoExecStackRequired>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
|
@ -36,7 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
#include "mailbox.hpp"
|
||||
|
||||
#define PAGE_SIZE (4*1024)
|
||||
|
4
makefile
4
makefile
|
@ -9,8 +9,8 @@ endif
|
|||
all: fm_transmitter.o mailbox.o wave_reader.o transmitter.o
|
||||
g++ -o $(EXECUTABLE) fm_transmitter.o mailbox.o wave_reader.o transmitter.o -L/opt/vc/lib -lm -lpthread -lbcm_host
|
||||
|
||||
mailbox.o: mailbox.c mailbox.h
|
||||
g++ $(FLAGS) -c mailbox.c
|
||||
mailbox.o: mailbox.cpp mailbox.hpp
|
||||
g++ $(FLAGS) -c mailbox.cpp
|
||||
|
||||
wave_reader.o: wave_reader.cpp wave_reader.hpp
|
||||
g++ $(FLAGS) -c wave_reader.cpp
|
||||
|
|
202
transmitter.cpp
202
transmitter.cpp
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#include "transmitter.hpp"
|
||||
#include "mailbox.h"
|
||||
#include "mailbox.hpp"
|
||||
#include <bcm_host.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
@ -51,7 +51,6 @@
|
|||
#define BCM2711_PLLD_FREQ 750
|
||||
|
||||
#define GPIO_BASE_OFFSET 0x00200000
|
||||
#define TIMER_BASE_OFFSET 0x00003000
|
||||
|
||||
#define CLK0_BASE_OFFSET 0x00101070
|
||||
#define CLK1_BASE_OFFSET 0x00101078
|
||||
|
@ -100,16 +99,6 @@
|
|||
#define BUFFER_TIME 1000000
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
struct TimerRegisters {
|
||||
uint32_t ctlStatus;
|
||||
uint32_t low;
|
||||
uint32_t high;
|
||||
uint32_t c0;
|
||||
uint32_t c1;
|
||||
uint32_t c2;
|
||||
uint32_t c3;
|
||||
};
|
||||
|
||||
struct ClockRegisters {
|
||||
uint32_t ctl;
|
||||
uint32_t div;
|
||||
|
@ -354,11 +343,15 @@ class DMAController : public Device
|
|||
};
|
||||
|
||||
Transmitter::Transmitter()
|
||||
: output(nullptr), stop(true)
|
||||
: output(nullptr), enable(false)
|
||||
{
|
||||
}
|
||||
|
||||
Transmitter::~Transmitter() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock, [&]() -> bool {
|
||||
return !txThread.joinable() && !enable;
|
||||
});
|
||||
if (output != nullptr) {
|
||||
delete output;
|
||||
}
|
||||
|
@ -366,7 +359,10 @@ Transmitter::~Transmitter() {
|
|||
|
||||
void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool preserveCarrier)
|
||||
{
|
||||
stop = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
enable = true;
|
||||
}
|
||||
|
||||
WaveHeader header = reader.GetHeader();
|
||||
unsigned bufferSize = static_cast<unsigned>(static_cast<unsigned long long>(header.sampleRate) * BUFFER_TIME / 1000000);
|
||||
|
@ -383,6 +379,11 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth,
|
|||
delete output;
|
||||
output = nullptr;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
enable = false;
|
||||
}
|
||||
cv.notify_all();
|
||||
};
|
||||
try {
|
||||
if (dmaChannel != 0xff) {
|
||||
|
@ -399,56 +400,10 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth,
|
|||
|
||||
void Transmitter::Stop()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange)
|
||||
{
|
||||
std::vector<Sample> samples = reader.GetSamples(bufferSize, stop);
|
||||
if (samples.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned sampleOffset = 0;
|
||||
bool eof = samples.size() < bufferSize, txStop = false;
|
||||
std::thread transmitterThread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &txStop);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2));
|
||||
|
||||
auto finally = [&]() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(access);
|
||||
txStop = true;
|
||||
}
|
||||
transmitterThread.join();
|
||||
samples.clear();
|
||||
stop = true;
|
||||
};
|
||||
try {
|
||||
while (!eof && !stop) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(access);
|
||||
if (txStop) {
|
||||
throw std::runtime_error("Transmitter thread has unexpectedly exited");
|
||||
}
|
||||
if (samples.empty()) {
|
||||
if (!reader.SetSampleOffset(sampleOffset + bufferSize)) {
|
||||
break;
|
||||
}
|
||||
samples = reader.GetSamples(bufferSize, stop);
|
||||
if (samples.empty()) {
|
||||
break;
|
||||
}
|
||||
eof = samples.size() < bufferSize;
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2));
|
||||
}
|
||||
} catch (...) {
|
||||
finally();
|
||||
throw;
|
||||
}
|
||||
finally();
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
enable = false;
|
||||
lock.unlock();
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel)
|
||||
|
@ -459,7 +414,7 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
|
|||
|
||||
AllocatedMemory allocated(sizeof(uint32_t) * bufferSize + sizeof(DMAControllBlock) * (2 * bufferSize) + sizeof(uint32_t));
|
||||
|
||||
std::vector<Sample> samples = reader.GetSamples(bufferSize, stop);
|
||||
std::vector<Sample> samples = reader.GetSamples(bufferSize, enable, mtx);
|
||||
if (samples.empty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -509,11 +464,16 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
samples.clear();
|
||||
stop = true;
|
||||
};
|
||||
try {
|
||||
while (!eof && !stop) {
|
||||
samples = reader.GetSamples(bufferSize, stop);
|
||||
while (!eof) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (!enable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
samples = reader.GetSamples(bufferSize, enable, mtx);
|
||||
if (!samples.size()) {
|
||||
break;
|
||||
}
|
||||
|
@ -535,35 +495,87 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
|
|||
finally();
|
||||
}
|
||||
|
||||
void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange)
|
||||
{
|
||||
std::vector<Sample> samples;
|
||||
unsigned sampleOffset = 0;
|
||||
|
||||
bool eof = false, stop = false, start = true;
|
||||
|
||||
txThread = std::thread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &stop);
|
||||
|
||||
auto finally = [&]() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
stop = true;
|
||||
}
|
||||
cv.notify_all();
|
||||
txThread.join();
|
||||
samples.clear();
|
||||
};
|
||||
|
||||
try {
|
||||
while (!eof) {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
if (!start) {
|
||||
cv.wait(lock, [&]() -> bool {
|
||||
return samples.empty() || !enable || stop;
|
||||
});
|
||||
}
|
||||
if (!enable) {
|
||||
break;
|
||||
}
|
||||
if (stop) {
|
||||
throw std::runtime_error("Transmitter thread has unexpectedly exited");
|
||||
}
|
||||
if (samples.empty()) {
|
||||
if (!reader.SetSampleOffset(sampleOffset + (start ? 0 : bufferSize))) {
|
||||
break;
|
||||
}
|
||||
lock.unlock();
|
||||
samples = reader.GetSamples(bufferSize, enable, mtx);
|
||||
lock.lock();
|
||||
if (samples.empty()) {
|
||||
break;
|
||||
}
|
||||
eof = samples.size() < bufferSize;
|
||||
lock.unlock();
|
||||
cv.notify_all();
|
||||
} else {
|
||||
lock.unlock();
|
||||
}
|
||||
start = false;
|
||||
}
|
||||
} catch (...) {
|
||||
finally();
|
||||
throw;
|
||||
}
|
||||
finally();
|
||||
}
|
||||
|
||||
void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop)
|
||||
{
|
||||
try {
|
||||
Peripherals &peripherals = Peripherals::GetInstance();
|
||||
|
||||
volatile TimerRegisters *timer = reinterpret_cast<TimerRegisters *>(peripherals.GetVirtualAddress(TIMER_BASE_OFFSET));
|
||||
uint64_t current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
|
||||
uint64_t playbackStart = current;
|
||||
auto playbackStart = std::chrono::system_clock::now();
|
||||
std::chrono::system_clock::time_point current, start;
|
||||
|
||||
while (true) {
|
||||
std::vector<Sample> loadedSamples;
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instance->access);
|
||||
if (*stop) {
|
||||
return;
|
||||
}
|
||||
loadedSamples = std::move(*samples);
|
||||
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
|
||||
if (!loadedSamples.empty()) {
|
||||
*sampleOffset = (current - playbackStart) * sampleRate / 1000000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
};
|
||||
|
||||
uint64_t start = current;
|
||||
unsigned offset = (current - start) * sampleRate / 1000000;
|
||||
std::unique_lock<std::mutex> lock(instance->mtx);
|
||||
instance->cv.wait(lock, [&]() -> bool {
|
||||
return !samples->empty() || *stop;
|
||||
});
|
||||
if (*stop) {
|
||||
break;
|
||||
}
|
||||
start = current = std::chrono::system_clock::now();
|
||||
*sampleOffset = std::chrono::duration_cast<std::chrono::microseconds>(current - playbackStart).count() * sampleRate / 1000000;
|
||||
loadedSamples = std::move(*samples);
|
||||
lock.unlock();
|
||||
instance->cv.notify_all();
|
||||
|
||||
unsigned offset = 0;
|
||||
|
||||
while (true) {
|
||||
if (offset >= loadedSamples.size()) {
|
||||
|
@ -573,14 +585,16 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output,
|
|||
float value = loadedSamples[offset].GetMonoValue();
|
||||
instance->output->SetDivisor(clockDivisor - static_cast<int>(round(value * divisorRange)));
|
||||
while (offset == prevOffset) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1)); // asm("nop");
|
||||
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));;
|
||||
offset = (current - start) * sampleRate / 1000000;
|
||||
std::this_thread::yield(); // asm("nop");
|
||||
current = std::chrono::system_clock::now();
|
||||
offset = std::chrono::duration_cast<std::chrono::microseconds>(current - start).count() * sampleRate / 1000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
std::lock_guard<std::mutex> lock(instance->access);
|
||||
std::unique_lock<std::mutex> lock(instance->mtx);
|
||||
*stop = true;
|
||||
lock.unlock();
|
||||
instance->cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "wave_reader.hpp"
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
class ClockOutput;
|
||||
|
||||
|
@ -53,7 +54,9 @@ class Transmitter
|
|||
void TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel);
|
||||
static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop);
|
||||
|
||||
std::condition_variable cv;
|
||||
std::thread txThread;
|
||||
ClockOutput *output;
|
||||
std::mutex access;
|
||||
bool stop;
|
||||
std::mutex mtx;
|
||||
bool enable;
|
||||
};
|
||||
|
|
|
@ -65,7 +65,7 @@ float Sample::GetMonoValue() const
|
|||
return value;
|
||||
}
|
||||
|
||||
WaveReader::WaveReader(const std::string &filename, bool &stop) :
|
||||
WaveReader::WaveReader(const std::string &filename, bool &enable, std::mutex &mtx) :
|
||||
filename(filename), headerOffset(0), currentDataOffset(0)
|
||||
{
|
||||
if (!filename.empty()) {
|
||||
|
@ -80,12 +80,12 @@ WaveReader::WaveReader(const std::string &filename, bool &stop) :
|
|||
}
|
||||
|
||||
try {
|
||||
ReadData(sizeof(WaveHeader::chunkID) + sizeof(WaveHeader::chunkSize) + sizeof(WaveHeader::format), true, stop);
|
||||
ReadData(sizeof(WaveHeader::chunkID) + sizeof(WaveHeader::chunkSize) + sizeof(WaveHeader::format), true, enable, mtx);
|
||||
if ((std::string(reinterpret_cast<char *>(header.chunkID), 4) != std::string("RIFF")) || (std::string(reinterpret_cast<char *>(header.format), 4) != std::string("WAVE"))) {
|
||||
throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", WAVE file expected"));
|
||||
}
|
||||
|
||||
ReadData(sizeof(WaveHeader::subchunk1ID) + sizeof(WaveHeader::subchunk1Size), true, stop);
|
||||
ReadData(sizeof(WaveHeader::subchunk1ID) + sizeof(WaveHeader::subchunk1Size), true, enable, mtx);
|
||||
unsigned subchunk1MinSize = sizeof(WaveHeader::audioFormat) + sizeof(WaveHeader::channels) +
|
||||
sizeof(WaveHeader::sampleRate) + sizeof(WaveHeader::byteRate) + sizeof(WaveHeader::blockAlign) +
|
||||
sizeof(WaveHeader::bitsPerSample);
|
||||
|
@ -93,7 +93,7 @@ WaveReader::WaveReader(const std::string &filename, bool &stop) :
|
|||
throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", data corrupted"));
|
||||
}
|
||||
|
||||
ReadData(header.subchunk1Size, true, stop);
|
||||
ReadData(header.subchunk1Size, true, enable, mtx);
|
||||
if ((header.audioFormat != WAVE_FORMAT_PCM) ||
|
||||
(header.byteRate != (header.bitsPerSample >> 3) * header.channels * header.sampleRate) ||
|
||||
(header.blockAlign != (header.bitsPerSample >> 3) * header.channels) ||
|
||||
|
@ -101,7 +101,7 @@ WaveReader::WaveReader(const std::string &filename, bool &stop) :
|
|||
throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", unsupported WAVE format"));
|
||||
}
|
||||
|
||||
ReadData(sizeof(WaveHeader::subchunk2ID) + sizeof(WaveHeader::subchunk2Size), true, stop);
|
||||
ReadData(sizeof(WaveHeader::subchunk2ID) + sizeof(WaveHeader::subchunk2Size), true, enable, mtx);
|
||||
if (std::string(reinterpret_cast<char *>(header.subchunk2ID), 4) != std::string("data")) {
|
||||
throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", data corrupted"));
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ const WaveHeader &WaveReader::GetHeader() const
|
|||
return header;
|
||||
}
|
||||
|
||||
std::vector<Sample> WaveReader::GetSamples(unsigned quantity, bool &stop) {
|
||||
std::vector<Sample> WaveReader::GetSamples(unsigned quantity, bool &enable, std::mutex &mtx) {
|
||||
unsigned bytesPerSample = (header.bitsPerSample >> 3) * header.channels;
|
||||
unsigned bytesToRead = quantity * bytesPerSample;
|
||||
unsigned bytesLeft = header.subchunk2Size - currentDataOffset;
|
||||
|
@ -143,7 +143,7 @@ std::vector<Sample> WaveReader::GetSamples(unsigned quantity, bool &stop) {
|
|||
quantity = bytesToRead / bytesPerSample;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data = std::move(ReadData(bytesToRead, false, stop));
|
||||
std::vector<uint8_t> data = std::move(ReadData(bytesToRead, false, enable, mtx));
|
||||
if (data.size() < bytesToRead) {
|
||||
quantity = data.size() / bytesPerSample;
|
||||
}
|
||||
|
@ -166,12 +166,22 @@ bool WaveReader::SetSampleOffset(unsigned offset) {
|
|||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WaveReader::ReadData(unsigned bytesToRead, bool headerBytes, bool &stop)
|
||||
std::vector<uint8_t> WaveReader::ReadData(unsigned bytesToRead, bool headerBytes, bool &enable, std::mutex &mtx)
|
||||
{
|
||||
unsigned bytesRead = 0;
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(bytesToRead);
|
||||
while ((bytesRead < bytesToRead) && !stop) {
|
||||
timeval timeout = {
|
||||
.tv_sec = 1,
|
||||
};
|
||||
fd_set fds;
|
||||
while (bytesRead < bytesToRead) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (!enable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int bytes = read(fileDescriptor, &data[bytesRead], bytesToRead - bytesRead);
|
||||
if (((bytes == -1) && ((fileDescriptor != STDIN_FILENO) || (errno != EAGAIN))) ||
|
||||
((static_cast<unsigned>(bytes) < bytesToRead) && headerBytes && (fileDescriptor != STDIN_FILENO))) {
|
||||
|
@ -185,20 +195,31 @@ std::vector<uint8_t> WaveReader::ReadData(unsigned bytesToRead, bool headerBytes
|
|||
data.resize(bytesRead);
|
||||
break;
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout);
|
||||
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||
FD_CLR(STDIN_FILENO, &fds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (headerBytes) {
|
||||
if (stop) {
|
||||
throw std::runtime_error("Cannot obtain header, program interrupted");
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (!enable) {
|
||||
throw std::runtime_error("Cannot obtain header, program interrupted");
|
||||
}
|
||||
}
|
||||
std::memcpy(&(reinterpret_cast<uint8_t *>(&header))[headerOffset], data.data(), bytesRead);
|
||||
headerOffset += bytesRead;
|
||||
} else {
|
||||
if (stop) {
|
||||
data.resize(bytesRead);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (!enable) {
|
||||
data.resize(bytesRead);
|
||||
}
|
||||
}
|
||||
currentDataOffset += bytesRead;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#define WAVE_FORMAT_PCM 0x0001
|
||||
|
||||
|
@ -68,17 +69,17 @@ protected:
|
|||
class WaveReader
|
||||
{
|
||||
public:
|
||||
WaveReader(const std::string &filename, bool &stop);
|
||||
WaveReader(const std::string &filename, bool &enable, std::mutex &mtx);
|
||||
virtual ~WaveReader();
|
||||
WaveReader(const WaveReader &) = delete;
|
||||
WaveReader(WaveReader &&) = delete;
|
||||
WaveReader &operator=(const WaveReader &) = delete;
|
||||
std::string GetFilename() const;
|
||||
const WaveHeader &GetHeader() const;
|
||||
std::vector<Sample> GetSamples(unsigned quantity, bool &stop);
|
||||
std::vector<Sample> GetSamples(unsigned quantity, bool &enable, std::mutex &mtx);
|
||||
bool SetSampleOffset(unsigned offset);
|
||||
private:
|
||||
std::vector<uint8_t> ReadData(unsigned bytesToRead, bool headerBytes, bool &stop);
|
||||
std::vector<uint8_t> ReadData(unsigned bytesToRead, bool headerBytes, bool &enable, std::mutex &mtx);
|
||||
|
||||
std::string filename;
|
||||
WaveHeader header;
|
||||
|
|
Ładowanie…
Reference in New Issue