kopia lustrzana https://github.com/markondej/fm_transmitter
Added instructions for general audio broadcasting (issue #144)
rodzic
245e099f0d
commit
0c1966aa51
29
README.md
29
README.md
|
@ -22,7 +22,7 @@ make
|
|||
```
|
||||
After a successful build you can start transmitting by executing the "fm_transmitter" program:
|
||||
```
|
||||
sudo ./fm_transmitter -f 102.0 acoustic_guitar_duet.wav
|
||||
sudo ./fm_transmitter -f 100.6 acoustic_guitar_duet.wav
|
||||
```
|
||||
Where:
|
||||
* -f frequency - Specifies the frequency in MHz, 100.0 by default if not passed
|
||||
|
@ -33,7 +33,7 @@ Other options:
|
|||
* -b bandwidth - Specifies the bandwidth in kHz, 100 by default
|
||||
* -r - Loops the playback
|
||||
|
||||
After transmission has begun, simply tune an FM receiver to chosen frequency, You should hear the playback.
|
||||
After transmission has begun, simply tune an FM receiver to chosen frequency, you should hear the playback.
|
||||
### Raspberry Pi 4
|
||||
On Raspberry Pi 4 other built-in hardware probably interfers somehow with this software making transmitting not possible on all standard FM broadcasting frequencies. In this case it is recommended to:
|
||||
1. Compile executable with option to use GPIO21 instead of GPIO4 (PIN 40 on GPIO header):
|
||||
|
@ -45,6 +45,22 @@ make GPIO21=1
|
|||
echo "performance"| sudo tee /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
```
|
||||
3. Using lower FM broadcasting frequencies (below 93 MHz) when transmitting.
|
||||
### Use as general audio output device
|
||||
[hydranix](https://github.com/markondej/fm_transmitter/issues/144) has came up with simple method of using transmitter as an general audio output device. In order to achieve this you should load "snd-aloop" module and stream output from loopback device to transmitter application:
|
||||
```
|
||||
sudo modprobe snd-aloop
|
||||
arecord -D hw:1,1,0 -c 1 -d 0 -r 22050 -f S16_LE | sudo ./fm_transmitter -f 100.6 - &
|
||||
```
|
||||
Please keep in mind loopback device should be set default ALSA device (see [this article](https://www.alsa-project.org/wiki/Setting_the_default_device)). Also parameter "-D hw:X,1,0" should be pointing this device (use card number instead of "X").
|
||||
### Microphone support
|
||||
In order to use a microphone live input use the `arecord` command, eg.:
|
||||
```
|
||||
arecord -D hw:1,0 -c 1 -d 0 -r 22050 -f S16_LE | sudo ./fm_transmitter -f 100.6 -
|
||||
```
|
||||
In cases of a performance drop down use ```plughw:1,0``` instead of ```hw:1,0``` like this:
|
||||
```
|
||||
arecord -D plughw:1,0 -c 1 -d 0 -r 22050 -f S16_LE | sudo ./fm_transmitter -f 100.6 -
|
||||
```
|
||||
### Supported audio formats
|
||||
You can transmitt uncompressed WAV (.wav) files directly or read audio data from stdin, eg. using MP3 file:
|
||||
```
|
||||
|
@ -62,15 +78,6 @@ Or you could also use FFMPEG:
|
|||
ffmpeg -i example.webm -f wav -bitexact -acodec pcm_s16le -ar 22050 -ac 1 converted-example.wav
|
||||
sudo ./fm_transmitter -f 100.6 converted-example.wav
|
||||
```
|
||||
### Microphone support
|
||||
In order to use a microphone live input use the `arecord` command, eg.:
|
||||
```
|
||||
arecord -D hw:1,0 -c1 -d 0 -r 22050 -f S16_LE | sudo ./fm_transmitter -f 100.6 -
|
||||
```
|
||||
In cases of a performance drop down use ```plughw:1,0``` instead of ```hw:1,0``` like this:
|
||||
```
|
||||
arecord -D plughw:1,0 -c1 -d 0 -r 22050 -f S16_LE | sudo ./fm_transmitter -f 100.6 -
|
||||
```
|
||||
## Legal note
|
||||
Please keep in mind that transmitting on certain frequencies without special permissions may be illegal in your country.
|
||||
## New features
|
||||
|
|
|
@ -109,9 +109,11 @@ int main(int argc, char** argv)
|
|||
std::cout << "Error: " << catched.what() << std::endl;
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
auto temp = transmitter;
|
||||
transmitter = nullptr;
|
||||
delete temp;
|
||||
if (transmitter != nullptr) {
|
||||
auto temp = transmitter;
|
||||
transmitter = nullptr;
|
||||
delete temp;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
9
makefile
9
makefile
|
@ -1,20 +1,17 @@
|
|||
EXECUTABLE = fm_transmitter
|
||||
VERSION = 0.9.5
|
||||
VERSION = 0.9.6
|
||||
FLAGS = -Wall -O3 -std=c++11
|
||||
TRANSMITTER = -fno-strict-aliasing -I/opt/vc/include
|
||||
ifeq ($(GPIO21), 1)
|
||||
TRANSMITTER += -DGPIO21
|
||||
endif
|
||||
|
||||
all: fm_transmitter.o mailbox.o sample.o wave_reader.o transmitter.o
|
||||
g++ -L/opt/vc/lib -o $(EXECUTABLE) fm_transmitter.o mailbox.o sample.o wave_reader.o transmitter.o -lm -lpthread -lbcm_host
|
||||
all: fm_transmitter.o mailbox.o wave_reader.o transmitter.o
|
||||
g++ -L/opt/vc/lib -o $(EXECUTABLE) fm_transmitter.o mailbox.o wave_reader.o transmitter.o -lm -lpthread -lbcm_host
|
||||
|
||||
mailbox.o: mailbox.c mailbox.h
|
||||
g++ $(FLAGS) -c mailbox.c
|
||||
|
||||
sample.o: sample.cpp sample.hpp
|
||||
g++ $(FLAGS) -c sample.cpp
|
||||
|
||||
wave_reader.o: wave_reader.cpp wave_reader.hpp
|
||||
g++ $(FLAGS) -c wave_reader.cpp
|
||||
|
||||
|
|
60
sample.cpp
60
sample.cpp
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
FM Transmitter - use Raspberry Pi as FM transmitter
|
||||
|
||||
Copyright (c) 2021, Marcin Kondej
|
||||
All rights reserved.
|
||||
|
||||
See https://github.com/markondej/fm_transmitter
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
||||
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "sample.hpp"
|
||||
#include <climits>
|
||||
|
||||
Sample::Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel)
|
||||
: value(0.f)
|
||||
{
|
||||
int sum = 0;
|
||||
int16_t *channelValues = new int16_t[channels];
|
||||
for (unsigned i = 0; i < channels; i++) {
|
||||
switch (bitsPerChannel >> 3) {
|
||||
case 2:
|
||||
channelValues[i] = (data[((i + 1) << 1) - 1] << 8) | data[((i + 1) << 1) - 2];
|
||||
break;
|
||||
case 1:
|
||||
channelValues[i] = (static_cast<int16_t>(data[i]) - 0x80) << 8;
|
||||
break;
|
||||
}
|
||||
sum += channelValues[i];
|
||||
}
|
||||
value = 2 * sum / (static_cast<float>(USHRT_MAX) * channels);
|
||||
delete[] channelValues;
|
||||
}
|
||||
|
||||
float Sample::GetMonoValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
48
sample.hpp
48
sample.hpp
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
FM Transmitter - use Raspberry Pi as FM transmitter
|
||||
|
||||
Copyright (c) 2021, Marcin Kondej
|
||||
All rights reserved.
|
||||
|
||||
See https://github.com/markondej/fm_transmitter
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
||||
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SAMPLE_HPP
|
||||
#define SAMPLE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Sample
|
||||
{
|
||||
public:
|
||||
Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel);
|
||||
float GetMonoValue() const;
|
||||
protected:
|
||||
float value;
|
||||
};
|
||||
|
||||
#endif // SAMPLE_HPP
|
111
transmitter.cpp
111
transmitter.cpp
|
@ -353,10 +353,8 @@ class DMAController : public Device
|
|||
volatile DMARegisters *dma;
|
||||
};
|
||||
|
||||
bool Transmitter::transmitting = false;
|
||||
|
||||
Transmitter::Transmitter()
|
||||
: output(nullptr), stopped(true)
|
||||
: output(nullptr), stop(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -368,11 +366,7 @@ Transmitter::~Transmitter() {
|
|||
|
||||
void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool preserveCarrier)
|
||||
{
|
||||
if (transmitting) {
|
||||
throw std::runtime_error("Cannot transmit, transmitter already in use");
|
||||
}
|
||||
transmitting = true;
|
||||
stopped = false;
|
||||
stop = false;
|
||||
|
||||
WaveHeader header = reader.GetHeader();
|
||||
unsigned bufferSize = static_cast<unsigned>(static_cast<unsigned long long>(header.sampleRate) * BUFFER_TIME / 1000000);
|
||||
|
@ -389,7 +383,6 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth,
|
|||
delete output;
|
||||
output = nullptr;
|
||||
}
|
||||
transmitting = false;
|
||||
};
|
||||
try {
|
||||
if (dmaChannel != 0xff) {
|
||||
|
@ -406,36 +399,43 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth,
|
|||
|
||||
void Transmitter::Stop()
|
||||
{
|
||||
stopped = true;
|
||||
stop = true;
|
||||
}
|
||||
|
||||
void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange)
|
||||
{
|
||||
std::vector<Sample> samples = reader.GetSamples(bufferSize, stopped);
|
||||
std::vector<Sample> samples = reader.GetSamples(bufferSize, stop);
|
||||
if (samples.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned sampleOffset = 0;
|
||||
bool eof = samples.size() < bufferSize;
|
||||
std::thread transmitterThread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples);
|
||||
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 = [&]() {
|
||||
stopped = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(access);
|
||||
txStop = true;
|
||||
}
|
||||
transmitterThread.join();
|
||||
samples.clear();
|
||||
stop = true;
|
||||
};
|
||||
try {
|
||||
while (!eof && !stopped) {
|
||||
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, stopped);
|
||||
samples = reader.GetSamples(bufferSize, stop);
|
||||
if (samples.empty()) {
|
||||
break;
|
||||
}
|
||||
|
@ -459,7 +459,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, stopped);
|
||||
std::vector<Sample> samples = reader.GetSamples(bufferSize, stop);
|
||||
if (samples.empty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -508,12 +508,12 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
|
|||
while (dma.GetControllBlockAddress() != 0x00000000) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
stopped = true;
|
||||
samples.clear();
|
||||
stop = true;
|
||||
};
|
||||
try {
|
||||
while (!eof && !stopped) {
|
||||
samples = reader.GetSamples(bufferSize, stopped);
|
||||
while (!eof && !stop) {
|
||||
samples = reader.GetSamples(bufferSize, stop);
|
||||
if (!samples.size()) {
|
||||
break;
|
||||
}
|
||||
|
@ -535,47 +535,52 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
|
|||
finally();
|
||||
}
|
||||
|
||||
void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples)
|
||||
void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop)
|
||||
{
|
||||
Peripherals &peripherals = Peripherals::GetInstance();
|
||||
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;
|
||||
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;
|
||||
|
||||
while (true) {
|
||||
std::vector<Sample> loadedSamples;
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instance->access);
|
||||
if (instance->stopped) {
|
||||
return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
loadedSamples = std::move(*samples);
|
||||
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
|
||||
if (!loadedSamples.empty()) {
|
||||
*sampleOffset = (current - playbackStart) * sampleRate / 1000000;
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
};
|
||||
|
||||
uint64_t start = current;
|
||||
unsigned offset = (current - start) * sampleRate / 1000000;
|
||||
|
||||
while (true) {
|
||||
if (offset >= loadedSamples.size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
};
|
||||
|
||||
uint64_t start = current;
|
||||
unsigned offset = (current - start) * sampleRate / 1000000;
|
||||
|
||||
while (true) {
|
||||
if (offset >= loadedSamples.size()) {
|
||||
break;
|
||||
}
|
||||
unsigned prevOffset = offset;
|
||||
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;
|
||||
unsigned prevOffset = offset;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
std::lock_guard<std::mutex> lock(instance->access);
|
||||
*stop = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef TRANSMITTER_HPP
|
||||
#define TRANSMITTER_HPP
|
||||
#pragma once
|
||||
|
||||
#include "wave_reader.hpp"
|
||||
#include <mutex>
|
||||
|
@ -52,12 +51,9 @@ class Transmitter
|
|||
private:
|
||||
void TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange);
|
||||
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);
|
||||
static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop);
|
||||
|
||||
static bool transmitting;
|
||||
ClockOutput *output;
|
||||
std::mutex access;
|
||||
bool stopped;
|
||||
bool stop;
|
||||
};
|
||||
|
||||
#endif // TRANSMITTER_HPP
|
||||
|
|
|
@ -38,6 +38,32 @@
|
|||
#include <chrono>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <climits>
|
||||
|
||||
Sample::Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel)
|
||||
: value(0.f)
|
||||
{
|
||||
int sum = 0;
|
||||
int16_t *channelValues = new int16_t[channels];
|
||||
for (unsigned i = 0; i < channels; i++) {
|
||||
switch (bitsPerChannel >> 3) {
|
||||
case 2:
|
||||
channelValues[i] = (data[((i + 1) << 1) - 1] << 8) | data[((i + 1) << 1) - 2];
|
||||
break;
|
||||
case 1:
|
||||
channelValues[i] = (static_cast<int16_t>(data[i]) - 0x80) << 8;
|
||||
break;
|
||||
}
|
||||
sum += channelValues[i];
|
||||
}
|
||||
value = 2 * sum / (static_cast<float>(USHRT_MAX) * channels);
|
||||
delete[] channelValues;
|
||||
}
|
||||
|
||||
float Sample::GetMonoValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
WaveReader::WaveReader(const std::string &filename, bool &stop) :
|
||||
filename(filename), headerOffset(0), currentDataOffset(0)
|
||||
|
|
|
@ -31,10 +31,9 @@
|
|||
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef WAVE_READER_HPP
|
||||
#define WAVE_READER_HPP
|
||||
#pragma once
|
||||
|
||||
#include "sample.hpp"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -57,6 +56,15 @@ struct WaveHeader
|
|||
uint32_t subchunk2Size;
|
||||
};
|
||||
|
||||
class Sample
|
||||
{
|
||||
public:
|
||||
Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel);
|
||||
float GetMonoValue() const;
|
||||
protected:
|
||||
float value;
|
||||
};
|
||||
|
||||
class WaveReader
|
||||
{
|
||||
public:
|
||||
|
@ -77,5 +85,3 @@ class WaveReader
|
|||
unsigned dataOffset, headerOffset, currentDataOffset;
|
||||
int fileDescriptor;
|
||||
};
|
||||
|
||||
#endif // WAVE_READER_HPP
|
||||
|
|
Ładowanie…
Reference in New Issue