Wavfile class

This commit is contained in:
furrtek 2016-12-07 20:49:42 +01:00
parent e56fa0f479
commit 1db138c27a
20 changed files with 640 additions and 312 deletions

View File

@ -176,7 +176,7 @@ set(CPPSRC
ui_touch_calibration.cpp
ui_whipcalc.cpp
ui_xylos.cpp
# ui_closecall.cpp
ui_closecall.cpp
# ui_loadmodule.cpp
recent_entries.cpp
receiver_model.cpp
@ -196,6 +196,8 @@ set(CPPSRC
sd_card.cpp
time.cpp
file.cpp
filewriter.cpp
wavfile.cpp
log_file.cpp
${COMMON}/png_writer.cpp
capture_thread.cpp

View File

@ -2886,6 +2886,30 @@ file.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/file.cpp.s
.PHONY : file.cpp.s
filewriter.obj: filewriter.cpp.obj
.PHONY : filewriter.obj
# target to build an object file
filewriter.cpp.obj:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/filewriter.cpp.obj
.PHONY : filewriter.cpp.obj
filewriter.i: filewriter.cpp.i
.PHONY : filewriter.i
# target to preprocess a source file
filewriter.cpp.i:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/filewriter.cpp.i
.PHONY : filewriter.cpp.i
filewriter.s: filewriter.cpp.s
.PHONY : filewriter.s
# target to generate assembly for a file
filewriter.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/filewriter.cpp.s
.PHONY : filewriter.cpp.s
hackrf_cpld_data.obj: hackrf_cpld_data.cpp.obj
.PHONY : hackrf_cpld_data.obj
@ -3774,6 +3798,30 @@ ui_channel.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_channel.cpp.s
.PHONY : ui_channel.cpp.s
ui_closecall.obj: ui_closecall.cpp.obj
.PHONY : ui_closecall.obj
# target to build an object file
ui_closecall.cpp.obj:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_closecall.cpp.obj
.PHONY : ui_closecall.cpp.obj
ui_closecall.i: ui_closecall.cpp.i
.PHONY : ui_closecall.i
# target to preprocess a source file
ui_closecall.cpp.i:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_closecall.cpp.i
.PHONY : ui_closecall.cpp.i
ui_closecall.s: ui_closecall.cpp.s
.PHONY : ui_closecall.s
# target to generate assembly for a file
ui_closecall.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_closecall.cpp.s
.PHONY : ui_closecall.cpp.s
ui_debug.obj: ui_debug.cpp.obj
.PHONY : ui_debug.obj
@ -4350,6 +4398,30 @@ ui_xylos.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_xylos.cpp.s
.PHONY : ui_xylos.cpp.s
wavfile.obj: wavfile.cpp.obj
.PHONY : wavfile.obj
# target to build an object file
wavfile.cpp.obj:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/wavfile.cpp.obj
.PHONY : wavfile.cpp.obj
wavfile.i: wavfile.cpp.i
.PHONY : wavfile.i
# target to preprocess a source file
wavfile.cpp.i:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/wavfile.cpp.i
.PHONY : wavfile.cpp.i
wavfile.s: wavfile.cpp.s
.PHONY : wavfile.s
# target to generate assembly for a file
wavfile.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/wavfile.cpp.s
.PHONY : wavfile.cpp.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@ -4705,6 +4777,9 @@ help:
@echo "... file.obj"
@echo "... file.i"
@echo "... file.s"
@echo "... filewriter.obj"
@echo "... filewriter.i"
@echo "... filewriter.s"
@echo "... hackrf_cpld_data.obj"
@echo "... hackrf_cpld_data.i"
@echo "... hackrf_cpld_data.s"
@ -4816,6 +4891,9 @@ help:
@echo "... ui_channel.obj"
@echo "... ui_channel.i"
@echo "... ui_channel.s"
@echo "... ui_closecall.obj"
@echo "... ui_closecall.i"
@echo "... ui_closecall.s"
@echo "... ui_debug.obj"
@echo "... ui_debug.i"
@echo "... ui_debug.s"
@ -4888,6 +4966,9 @@ help:
@echo "... ui_xylos.obj"
@echo "... ui_xylos.i"
@echo "... ui_xylos.s"
@echo "... wavfile.obj"
@echo "... wavfile.i"
@echo "... wavfile.s"
.PHONY : help

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "filewriter.hpp"
#include "portapack.hpp"
using namespace portapack;
#include <cstdint>
namespace ui {
Optional<File::Error> FileWriter::create(const std::string& filename) {
return file.create(filename);
}
File::Result<size_t> FileWriter::write(const void* const buffer, const size_t bytes) {
auto write_result = file.write(buffer, bytes) ;
if( write_result.is_ok() ) {
bytes_written += write_result.value();
}
return write_result;
}
} /* namespace ui */

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __FILEWRITER_H__
#define __FILEWRITER_H__
#include "ui_widget.hpp"
#include "capture_thread.hpp"
#include "signal.hpp"
#include "file.hpp"
#include <cstddef>
#include <string>
#include <memory>
namespace ui {
class FileWriter : public Writer {
public:
FileWriter() = default;
FileWriter(const FileWriter&) = delete;
FileWriter& operator=(const FileWriter&) = delete;
FileWriter(FileWriter&& file) = delete;
FileWriter& operator=(FileWriter&&) = delete;
Optional<File::Error> create(const std::string& filename);
File::Result<size_t> write(const void* const buffer, const size_t bytes) override;
protected:
File file;
uint64_t bytes_written { 0 };
};
} /* namespace ui */
#endif/*__FILEWRITER_H__*/

View File

@ -23,10 +23,12 @@
// Bitmaps generated with:
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
//TODO: CTCSS file/module
//TEST: Imperial in whipcalc
//TEST: Numbers
//TEST: Jammer
//TEST: RDS
//TEST: Morse coder/beacon
//BUG (fixed ?): Soundboard crashes on exit if no wav files on sd card
//BUG (fixed ?): No audio in about when shown second time

View File

@ -59,7 +59,7 @@ void AboutView::on_show() {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
baseband::set_audiotx_data(32, 15, false, 0);
baseband::set_audiotx_data(32, 50, false, 0);
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
}

View File

@ -33,8 +33,6 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "hackrf_hal.hpp"
#include <cstring>
#include <stdio.h>
#include <algorithm>
@ -428,14 +426,8 @@ CloseCallView::CloseCallView(
signal_token_tick_second = time::signal_tick_second += [this]() {
this->on_tick_second();
};
receiver_model.set_baseband_configuration({
.mode = toUType(ReceiverModel::Mode::CloseCall),
.sampling_rate = CC_SLICE_WIDTH,
.decimation_factor = 1,
});
receiver_model.set_baseband_bandwidth(CC_SLICE_WIDTH);
//receiver_model.enable();
receiver_model.enable();
}
} /* namespace ui */

View File

@ -37,7 +37,7 @@
#include "ui_numbers.hpp"
#include "ui_whipcalc.hpp"
//#include "ui_closecall.hpp" // DEBUG
#include "ui_closecall.hpp"
#include "ui_freqman.hpp"
#include "ui_nuoptix.hpp"
#include "ui_soundboard.hpp"
@ -271,10 +271,11 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
/* TransmitterCodedMenuView ******************************************************/
TransmitterCodedMenuView::TransmitterCodedMenuView(NavigationView& nav) {
add_items<7>({ {
add_items<8>({ {
{ "ADS-B Mode S", ui::Color::yellow(), [&nav](){ nav.push<ADSBTxView>(); } },
{ "BHT Epar", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "BHT Xylos", ui::Color::yellow(), [&nav](){ nav.push<XylosView>(); } },
{ "BHT Xylos", ui::Color::green(), [&nav](){ nav.push<XylosView>(); } },
{ "Morse beacon", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Nuoptix DTMF timecode", ui::Color::green(), [&nav](){ nav.push<NuoptixView>(); } },
{ "OOK remote encoders", ui::Color::green(), [&nav](){ nav.push<EncodersView>(); } },
{ "RDS", ui::Color::orange(), [&nav](){ nav.push<RDSView>(); } },
@ -288,7 +289,7 @@ TransmitterCodedMenuView::TransmitterCodedMenuView(NavigationView& nav) {
TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
add_items<4>({ {
{ "Soundboard", ui::Color::green(), [&nav](){ nav.push<SoundBoardView>(); } },
{ "Numbers station", ui::Color::yellow(), [&nav](){ nav.push<NumbersStationView>(); } },
{ "Numbers station", ui::Color::green(), [&nav](){ nav.push<NumbersStationView>(); } },
{ "Microphone", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Whistle", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
} });
@ -298,8 +299,9 @@ TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
/* UtilitiesView *****************************************************************/
UtilitiesView::UtilitiesView(NavigationView& nav) {
add_items<2>({ {
{ "Whip antenna calculator", ui::Color::green(), [&nav](){ nav.push<WhipCalcView>(); } },
add_items<3>({ {
{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push<FreqManView>(); } },
{ "Whip antenna length", ui::Color::green(), [&nav](){ nav.push<WhipCalcView>(); } },
{ "Notepad", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
} });
on_left = [&nav](){ nav.pop(); };
@ -313,14 +315,13 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
{ "Capture", ui::Color::cyan(), [&nav](){ nav.push<CaptureAppView>(); } },
{ "Code transmitters", ui::Color::green(), [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
{ "Audio transmitters", ui::Color::green(), [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
//{ "Close Call RX", ui::Color::cyan(), [&nav](){ nav.push<CloseCallView>(); } },
{ "Close Call", ui::Color::orange(), [&nav](){ nav.push<CloseCallView>(); } },
{ "Jammer", ui::Color::orange(), [&nav](){ nav.push<JammerView>(); } },
{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push<FreqManView>(); } },
{ "Utilities", ui::Color::purple(), [&nav](){ nav.push<UtilitiesView>(); } },
//{ "Analyze", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Setup", ui::Color::white(), [&nav](){ nav.push<SetupMenuView>(); } },
//{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } },
{ "HackRF", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
{ "HackRF mode", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
{ "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } }
} });
}

View File

@ -22,8 +22,6 @@
#include "ui_numbers.hpp"
#include "ch.h"
#include "ff.h"
#include "portapack.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
@ -34,17 +32,15 @@
// TODO: Total transmission time (all durations / 44100)
using namespace portapack;
using namespace hackrf::one;
namespace ui {
void NumbersStationView::focus() {
button_exit.focus();
if (file_error) {
if (file_error)
nav_.display_modal("No files", "Missing files in /numbers/", ABORT, nullptr);
}
}
NumbersStationView::~NumbersStationView() {
transmitter_model.disable();
@ -58,62 +54,56 @@ void NumbersStationView::on_tuning_frequency_changed(rf::Frequency f) {
void NumbersStationView::prepare_audio() {
uint8_t code;
if (cnt >= sample_duration) {
if (sample_counter >= sample_duration) {
if (segment == ANNOUNCE) {
if (!announce_loop) {
segment = MESSAGE;
} else {
auto error = file.open("/numbers/announce.wav");
if (error.is_valid()) return;
reader->open("/numbers/announce.wav");
sample_duration = sound_sizes[10];
file.seek(44); // Skip header
announce_loop--;
}
}
if (segment == MESSAGE) {
if (id_test == 10)
code = symfield_code.value(code_index);
if (code_index == 25)
transmitter_model.disable();
code = symfield_code.value(id_test);
if (code >= 10) {
memset(audio_buffer, 0, 1024);
if (code == 10) {
pause = 11025; // p: 0.25s @ 44100Hz
memset(audio_buffer, 0, 1024);
} else if (code == 11) {
pause = 33075; // P: 0.75s @ 44100Hz
memset(audio_buffer, 0, 1024);
} else if (code == 12) {
transmitter_model.disable();
}
} else {
auto error = file.open("/numbers/" + file_names[code] + ".wav");
if (error.is_valid()) return;
reader->open("/numbers/" + file_names[code] + ".wav");
sample_duration = sound_sizes[code];
file.seek(44); // Skip header
}
id_test++;
code_index++;
}
cnt = 0;
sample_counter = 0;
}
if (!pause) {
size_t bytes_read = file.read(audio_buffer, 1024).value();
size_t bytes_read = reader->read(audio_buffer, 1024);
// Unsigned to signed, pretty stupid :/
for (size_t n = 0; n < bytes_read; n++)
audio_buffer[n] -= 0x80;
for (size_t n = bytes_read; n < 1024; n++)
audio_buffer[n] = 0;
cnt += 1024;
sample_counter += 1024;
} else {
if (pause >= 1024) {
pause -= 1024;
} else {
cnt = sample_duration;
sample_counter = sample_duration;
pause = 0;
}
}
@ -122,12 +112,10 @@ void NumbersStationView::prepare_audio() {
}
void NumbersStationView::start_tx() {
uint32_t divider;
sample_duration = sound_sizes[10]; // Announce
sample_counter = sample_duration;
sample_duration = sound_sizes[0];
cnt = sample_duration;
id_test = 0;
code_index = 0;
announce_loop = 2;
segment = ANNOUNCE;
@ -144,23 +132,25 @@ void NumbersStationView::start_tx() {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
divider = (1536000 / 44100) - 1;
baseband::set_audiotx_data(
divider,
(1536000 / 44100) - 1,
number_bw.value(),
false,
0
);
}
// TODO: Copied from soundboard, make globally available
uint16_t NumbersStationView::fb_to_uint16(const std::string& fb) {
return (fb[1] << 8) + fb[0];
}
void NumbersStationView::on_tick_second() {
if (check_armed.value()) {
armed_blink = not armed_blink;
uint32_t NumbersStationView::fb_to_uint32(const std::string& fb) {
return (fb[3] << 24) + (fb[2] << 16) + (fb[1] << 8) + fb[0];
if (armed_blink)
check_armed.set_style(&style_red);
else
check_armed.set_style(&style());
check_armed.set_dirty();
}
}
NumbersStationView::NumbersStationView(
@ -168,31 +158,17 @@ NumbersStationView::NumbersStationView(
) : nav_ (nav)
{
uint8_t c;
uint8_t y, m, d, dayofweek;
size_t size;
//uint8_t y, m, d, dayofweek;
char file_buffer[32];
reader = std::make_unique<WAVFileReader>();
c = 0;
for (auto& file_name : file_names) {
auto error = file.open("/numbers/" + file_name + ".wav");
if (!error.is_valid()) {
file.seek(22);
file.read(file_buffer, 2);
// Is file mono ?
if (fb_to_uint16(file_buffer) == 1) {
file.seek(40);
file.read(file_buffer, 4);
size = fb_to_uint32(file_buffer);
if (!size) break;
sound_sizes[c] = size;
if (reader->open("/numbers/" + file_name + ".wav")) {
if ((reader->channels() == 1) && (reader->sample_rate() == 44100) && (reader->bits_per_sample() == 8)) {
sound_sizes[c] = reader->data_size();
c++;
} else {
break;
}
} else {
break;
}
}
@ -204,12 +180,15 @@ NumbersStationView::NumbersStationView(
&text_title,
&field_frequency,
&number_bw,
&text_code,
&symfield_code,
&button_tx,
&check_armed,
&button_tx_now,
&button_exit
} });
number_bw.set_value(120);
number_bw.set_value(75);
check_armed.set_value(false);
field_frequency.set_value(transmitter_model.tuning_frequency());
field_frequency.set_step(50000);
@ -225,6 +204,18 @@ NumbersStationView::NumbersStationView(
};
};
check_armed.on_select = [this](Checkbox&) {
if (check_armed.value()) {
armed_blink = false;
signal_token_tick_second = time::signal_tick_second += [this]() {
this->on_tick_second();
};
} else {
check_armed.set_style(&style());
time::signal_tick_second -= signal_token_tick_second;
}
};
// DEBUG
symfield_code.set_value(0, 10);
symfield_code.set_value(1, 3);
@ -236,13 +227,12 @@ NumbersStationView::NumbersStationView(
symfield_code.set_value(7, 7);
symfield_code.set_value(8, 8);
symfield_code.set_value(9, 0);
transmitter_model.set_tuning_frequency(103300000); // 103.3MHz
for (c = 0; c < 10; c++)
symfield_code.set_symbol_list(c, "0123456789pP");
symfield_code.set_value(10, 12); // End
for (c = 0; c < 25; c++)
symfield_code.set_symbol_list(c, "0123456789pPE");
/*
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
@ -254,8 +244,9 @@ NumbersStationView::NumbersStationView(
dayofweek = (y + y/4 - y/100 + y/400 + month_table[m-1] + d) % 7;
text_title.set(day_of_week[dayofweek]);
*/
button_tx.on_select = [this, &nav](Button&){
button_tx_now.on_select = [this, &nav](Button&){
this->start_tx();
};

View File

@ -28,10 +28,12 @@
#include "ui_receiver.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "time.hpp"
#include "clock_manager.hpp"
#include "message.hpp"
#include "baseband_api.hpp"
#include "file.hpp"
#include "utility.hpp"
#include "message.hpp"
#include "wavfile.hpp"
namespace ui {
@ -61,6 +63,7 @@ private:
NavigationView& nav_;
segments segment;
bool armed = false;
bool file_error = false;
uint32_t sound_sizes[11];
@ -78,21 +81,23 @@ private:
{ "announce" }
};
const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
// const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
// const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
File file;
uint8_t id_test, announce_loop;
uint32_t cnt;
std::unique_ptr<WAVFileReader> reader;
uint8_t code_index, announce_loop;
uint32_t sample_counter;
uint32_t sample_duration;
int8_t audio_buffer[1024];
uint32_t pause = 0;
bool armed_blink;
SignalToken signal_token_tick_second;
void on_tick_second();
void on_tuning_frequency_changed(rf::Frequency f);
void prepare_audio();
void start_tx();
uint16_t fb_to_uint16(const std::string& fb);
uint32_t fb_to_uint32(const std::string& fb);
// Schedule: save on sd card
// For each day of the week, max 8 messages ?
@ -105,7 +110,7 @@ private:
// Frequency list and sequence
Text text_title {
{ 1 * 8, 1 * 16, 11, 16 },
{ 1 * 8, 8 * 16, 11, 16 },
"Schedule:"
};
@ -113,22 +118,31 @@ private:
{ 1 * 8, 4 },
};
NumberField number_bw {
{ 12 * 8, 2 * 16 },
{ 12 * 8, 4 },
3,
{1, 150},
1,
' '
};
Text text_code {
{ 20, 4 * 16, 5 * 8, 16 },
"Code:"
};
SymField symfield_code {
{ 1*8, 3 * 16 },
10,
{ 20, 5 * 16 },
25,
false
};
Button button_tx {
{ 21 * 8, 13 * 16, 64, 32 },
"TX !"
Checkbox check_armed {
{ 2 * 8, 13 * 16 },
5,
"Armed"
};
Button button_tx_now {
{ 18 * 8, 13 * 16, 10 * 8, 32 },
"TX now"
};
Button button_exit {
{ 21 * 8, 16 * 16, 64, 32 },

View File

@ -26,7 +26,6 @@
#include "portapack_shared_memory.hpp"
using namespace portapack;
#include "file.hpp"
#include "time.hpp"
#include "string_format.hpp"
@ -34,134 +33,6 @@ using namespace portapack;
#include <cstdint>
class FileWriter : public Writer {
public:
FileWriter() = default;
FileWriter(const FileWriter&) = delete;
FileWriter& operator=(const FileWriter&) = delete;
FileWriter(FileWriter&& file) = delete;
FileWriter& operator=(FileWriter&&) = delete;
Optional<File::Error> create(const std::string& filename) {
return file.create(filename);
}
File::Result<size_t> write(const void* const buffer, const size_t bytes) override {
auto write_result = file.write(buffer, bytes) ;
if( write_result.is_ok() ) {
bytes_written += write_result.value();
}
return write_result;
}
protected:
File file;
uint64_t bytes_written { 0 };
};
using RawFileWriter = FileWriter;
class WAVFileWriter : public FileWriter {
public:
WAVFileWriter(
size_t sampling_rate
) : header { sampling_rate }
{
}
WAVFileWriter(const WAVFileWriter&) = delete;
WAVFileWriter& operator=(const WAVFileWriter&) = delete;
WAVFileWriter(WAVFileWriter&&) = delete;
WAVFileWriter& operator=(WAVFileWriter&&) = delete;
~WAVFileWriter() {
update_header();
}
Optional<File::Error> create(
const std::string& filename
) {
const auto create_error = FileWriter::create(filename);
if( create_error.is_valid() ) {
return create_error;
} else {
return update_header();
}
}
private:
struct fmt_pcm_t {
constexpr fmt_pcm_t(
const uint32_t sampling_rate
) : nSamplesPerSec { sampling_rate },
nAvgBytesPerSec { nSamplesPerSec * nBlockAlign }
{
}
private:
const uint8_t ckID[4] { 'f', 'm', 't', ' ' };
const uint32_t cksize { 16 };
const uint16_t wFormatTag { 0x0001 };
const uint16_t nChannels { 1 };
const uint32_t nSamplesPerSec;
const uint32_t nAvgBytesPerSec;
const uint16_t nBlockAlign { 2 };
const uint16_t wBitsPerSample { 16 };
};
struct data_t {
void set_size(const uint32_t value) {
cksize = value;
}
private:
const uint8_t ckID[4] { 'd', 'a', 't', 'a' };
uint32_t cksize { 0 };
};
struct header_t {
constexpr header_t(
const uint32_t sampling_rate
) : fmt { sampling_rate }
{
}
void set_data_size(const uint32_t value) {
data.set_size(value);
cksize = sizeof(header_t) + value - 8;
}
private:
const uint8_t riff_id[4] { 'R', 'I', 'F', 'F' };
uint32_t cksize { 0 };
const uint8_t wave_id[4] { 'W', 'A', 'V', 'E' };
fmt_pcm_t fmt;
data_t data;
};
header_t header;
Optional<File::Error> update_header() {
header.set_data_size(bytes_written);
const auto seek_0_result = file.seek(0);
if( seek_0_result.is_error() ) {
return seek_0_result.error();
}
const auto old_position = seek_0_result.value();
const auto write_result = file.write(&header, sizeof(header));
if( write_result.is_error() ) {
return write_result.error();
}
const auto seek_old_result = file.seek(old_position);
if( seek_old_result.is_error() ) {
return seek_old_result.error();
}
return { };
}
};
namespace ui {
RecordView::RecordView(
@ -294,7 +165,7 @@ void RecordView::start() {
return;
}
auto p = std::make_unique<RawFileWriter>();
auto p = std::make_unique<FileWriter>();
auto create_error = p->create(
filename_stem + ".C16"
);

View File

@ -26,7 +26,7 @@
#include "capture_thread.hpp"
#include "signal.hpp"
#include "wavfile.hpp"
#include "bitmap.hpp"
#include <cstddef>

View File

@ -24,7 +24,6 @@
#include "ui_soundboard.hpp"
#include "ch.h"
#include "lfsr_random.hpp"
#include "ui_alphanum.hpp"
#include "portapack.hpp"
@ -56,15 +55,15 @@ void SoundBoardView::do_random() {
void SoundBoardView::prepare_audio() {
if (cnt >= sample_duration) {
if (sample_counter >= sample_duration) {
if (tx_mode == NORMAL) {
if (!check_loop.value()) {
pbar.set_value(0);
transmitter_model.disable();
return;
} else {
file.seek(44);
cnt = 0;
reader->rewind();
sample_counter = 0;
}
} else {
pbar.set_value(0);
@ -73,15 +72,17 @@ void SoundBoardView::prepare_audio() {
}
}
pbar.set_value(cnt);
pbar.set_value(sample_counter);
size_t bytes_read = file.read(audio_buffer, 1024).value();
size_t bytes_read = reader->read(audio_buffer, 1024);
// Unsigned to signed, pretty stupid :/
for (size_t n = 0; n < bytes_read; n++)
audio_buffer[n] -= 0x80;
for (size_t n = bytes_read; n < 1024; n++)
audio_buffer[n] = 0;
cnt += 1024;
sample_counter += 1024;
baseband::set_fifo_data(audio_buffer);
}
@ -105,16 +106,14 @@ void SoundBoardView::play_sound(uint16_t id) {
if (sounds[id].size == 0) return;
auto error = file.open("/wav/" + sounds[id].filename);
if (error.is_valid()) return;
if (!reader->open("/wav/" + sounds[id].filename)) return;
sample_duration = sounds[id].sample_duration;
pbar.set_max(sample_duration);
pbar.set_value(0);
cnt = 0;
file.seek(44); // Skip header
sample_counter = 0;
prepare_audio();
@ -177,7 +176,6 @@ void SoundBoardView::refresh_buttons(uint16_t id) {
}
void SoundBoardView::change_page(Button& button, const KeyEvent key) {
// Stupid way to find out if the button is on the sides
if (button.screen_pos().x < 32) {
if ((key == KeyEvent::Left) && (page > 0)) {
@ -193,14 +191,6 @@ void SoundBoardView::change_page(Button& button, const KeyEvent key) {
}
}
uint16_t SoundBoardView::fb_to_uint16(const std::string& fb) {
return (fb[1] << 8) + fb[0];
}
uint32_t SoundBoardView::fb_to_uint32(const std::string& fb) {
return (fb[3] << 24) + (fb[2] << 16) + (fb[1] << 8) + fb[0];
}
void SoundBoardView::on_ctcss_changed(uint32_t v) {
_ctcss_freq = v;
}
@ -210,59 +200,36 @@ SoundBoardView::SoundBoardView(
) : nav_ (nav)
{
std::vector<std::string> file_list;
std::string file_name;
uint32_t size;
uint8_t c;
char file_buffer[32];
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
reader = std::make_unique<WAVFileReader>();
file_list = scan_root_files("/wav", ".WAV");
c = 0;
for (auto& file_name : file_list) {
auto error = file.open("/wav/" + file_name);
if (!error.is_valid()) {
file.seek(22);
file.read(file_buffer, 2);
// Is file mono ?
if (fb_to_uint16(file_buffer) == 1) {
file.seek(40);
file.read(file_buffer, 4);
size = fb_to_uint32(file_buffer);
sounds[c].size = size;
file.seek(24);
file.read(file_buffer, 4);
sounds[c].sample_rate = fb_to_uint32(file_buffer);
file.seek(34);
file.read(file_buffer, 2);
if (fb_to_uint16(file_buffer) > 8) {
if (reader->open("/wav/" + file_name)) {
if (reader->channels() == 1) {
sounds[c].size = reader->data_size();
sounds[c].sample_duration = reader->data_size() / (reader->bits_per_sample() / 8);
sounds[c].sample_rate = reader->sample_rate();
if (reader->bits_per_sample() > 8)
sounds[c].sixteenbit = true;
size /= 2;
} else
else
sounds[c].sixteenbit = false;
sounds[c].ms_duration = (size * 1000) / sounds[c].sample_rate;
sounds[c].sample_duration = size;
sounds[c].ms_duration = reader->ms_duration();
sounds[c].filename = file_name;
sounds[c].shortname = remove_filename_extension(file_name);
c++;
if (c == 100) break; // Limit to 100 files
if (c == 105) break; // Limit to 105 files (5 pages)
}
}
}
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
max_sound = c;
max_page = max_sound / 21; // 21 buttons per page
max_page = max_sound / 21; // 3 * 7 = 21 buttons per page
add_children({ {
&field_frequency,

View File

@ -29,8 +29,9 @@
#include "baseband_api.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "utility.hpp"
#include "message.hpp"
#include "file.hpp"
#include "wavfile.hpp"
namespace ui {
@ -63,15 +64,15 @@ private:
uint32_t ms_duration = 0;
};
uint32_t cnt;
uint32_t sample_counter;
uint32_t sample_duration;
uint8_t page = 0;
File file;
uint16_t lfsr_v = 0x1337;
sound sounds[100];
std::unique_ptr<WAVFileReader> reader;
sound sounds[105];
uint8_t max_sound;
uint8_t max_page;
@ -112,8 +113,6 @@ private:
void play_sound(uint16_t id);
void prepare_audio();
void on_ctcss_changed(uint32_t v);
uint16_t fb_to_uint16(const std::string& fb);
uint32_t fb_to_uint32(const std::string& fb);
Text text_duration {
{ 16, 236, 5 * 8, 16 }

View File

@ -0,0 +1,135 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "wavfile.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "file.hpp"
#include "time.hpp"
#include "utility.hpp"
#include <cstdint>
namespace ui {
int WAVFileReader::open(const std::string& filename) {
if (filename == last_filename) {
rewind();
return 1; // Already open
}
auto error = file.open(filename);
if (!error.is_valid()) {
file.read((void*)&header, sizeof(header));
// TODO: Check validity here
last_filename = filename;
data_start = header.fmt.cksize + 28; // Skip "data" and cksize
data_size_ = header.data.cksize;
sample_rate_ = header.fmt.nSamplesPerSec;
bytes_per_sample = header.fmt.wBitsPerSample / 8;
rewind();
return 1;
} else {
return 0;
}
}
size_t WAVFileReader::read(void * const data, const size_t bytes_to_read) {
return file.read(data, bytes_to_read).value();
}
void WAVFileReader::rewind() {
file.seek(data_start);
}
uint32_t WAVFileReader::ms_duration() {
return ((data_size_ * 1000) / sample_rate_) / bytes_per_sample;
}
int WAVFileReader::seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples) {
auto result = file.seek(data_start + ((((minutes * 60) + seconds) * sample_rate_) + samples) * bytes_per_sample);
if (result.is_error())
return 0;
return 1;
}
uint16_t WAVFileReader::channels() {
return header.fmt.nChannels;
}
uint32_t WAVFileReader::sample_rate() {
return sample_rate_;
}
uint32_t WAVFileReader::data_size() {
return data_size_;
}
uint16_t WAVFileReader::bits_per_sample() {
return header.fmt.wBitsPerSample;
}
WAVFileWriter::~WAVFileWriter() {
update_header();
}
Optional<File::Error> WAVFileWriter::create(
const std::string& filename
) {
const auto create_error = FileWriter::create(filename);
if( create_error.is_valid() ) {
return create_error;
} else {
return update_header();
}
}
Optional<File::Error> WAVFileWriter::update_header() {
header.set_data_size(bytes_written);
const auto seek_0_result = file.seek(0);
if( seek_0_result.is_error() ) {
return seek_0_result.error();
}
const auto old_position = seek_0_result.value();
const auto write_result = file.write(&header, sizeof(header));
if( write_result.is_error() ) {
return write_result.error();
}
const auto seek_old_result = file.seek(old_position);
if( seek_old_result.is_error() ) {
return seek_old_result.error();
}
return { };
}
} /* namespace ui */

View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __WAVFILE_H__
#define __WAVFILE_H__
#include "filewriter.hpp"
#include <cstddef>
#include <string>
#include <memory>
namespace ui {
class WAVFileReader {
public:
WAVFileReader() = default;
WAVFileReader(const WAVFileReader&) = delete;
WAVFileReader& operator=(const WAVFileReader&) = delete;
WAVFileReader(WAVFileReader&&) = delete;
WAVFileReader& operator=(WAVFileReader&&) = delete;
virtual ~WAVFileReader() = default;
int open(const std::string& filename);
void rewind();
uint32_t ms_duration();
size_t read(void * const data, const size_t bytes_to_read);
int seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples);
uint16_t channels();
uint32_t sample_rate();
uint32_t data_size();
uint16_t bits_per_sample();
private:
File file;
uint32_t data_start;
uint32_t bytes_per_sample;
uint32_t data_size_;
uint32_t sample_rate_;
std::string last_filename = "";
struct fmt_pcm_t {
uint8_t ckID[4]; // fmt
uint32_t cksize;
uint16_t wFormatTag;
uint16_t nChannels;
uint32_t nSamplesPerSec;
uint32_t nAvgBytesPerSec;
uint16_t nBlockAlign;
uint16_t wBitsPerSample;
};
struct data_t {
uint8_t ckID[4]; // data
uint32_t cksize;
};
struct header_t {
uint8_t riff_id[4]; // RIFF
uint32_t cksize;
uint8_t wave_id[4]; // WAVE
fmt_pcm_t fmt;
data_t data;
};
header_t header;
};
class WAVFileWriter : public FileWriter {
public:
WAVFileWriter(
size_t sampling_rate
) : header { sampling_rate }
{
}
WAVFileWriter(const WAVFileWriter&) = delete;
WAVFileWriter& operator=(const WAVFileWriter&) = delete;
WAVFileWriter(WAVFileWriter&&) = delete;
WAVFileWriter& operator=(WAVFileWriter&&) = delete;
~WAVFileWriter();
Optional<File::Error> create(const std::string& filename);
private:
struct fmt_pcm_t {
constexpr fmt_pcm_t(
const uint32_t sampling_rate
) : nSamplesPerSec { sampling_rate },
nAvgBytesPerSec { nSamplesPerSec * nBlockAlign }
{
}
private:
const uint8_t ckID[4] { 'f', 'm', 't', ' ' };
const uint32_t cksize { 16 };
const uint16_t wFormatTag { 0x0001 };
const uint16_t nChannels { 1 };
const uint32_t nSamplesPerSec;
const uint32_t nAvgBytesPerSec;
const uint16_t nBlockAlign { 2 };
const uint16_t wBitsPerSample { 16 };
};
struct data_t {
void set_size(const uint32_t value) {
cksize = value;
}
private:
const uint8_t ckID[4] { 'd', 'a', 't', 'a' };
uint32_t cksize { 0 };
};
struct header_t {
constexpr header_t(
const uint32_t sampling_rate
) : fmt { sampling_rate }
{
}
void set_data_size(const uint32_t value) {
data.set_size(value);
cksize = sizeof(header_t) + value - 8;
}
private:
const uint8_t riff_id[4] { 'R', 'I', 'F', 'F' };
uint32_t cksize { 0 };
const uint8_t wave_id[4] { 'W', 'A', 'V', 'E' };
fmt_pcm_t fmt;
data_t data;
};
header_t header;
Optional<File::Error> update_header();
};
} /* namespace ui */
#endif/*__WAVFILE_H__*/

View File

@ -564,9 +564,9 @@ void Checkbox::set_text(const std::string value) {
set_dirty();
}
std::string Checkbox::text() const {
/*std::string Checkbox::text() const {
return text_;
}
}*/
void Checkbox::set_value(const bool value) {
value_ = value;

View File

@ -263,7 +263,7 @@ public:
}
void set_text(const std::string value);
std::string text() const;
// std::string text() const;
void set_value(const bool value);
bool value() const;

View File

@ -31,6 +31,14 @@
#define LOCATE_IN_RAM __attribute__((section(".ramtext")))
inline uint16_t fb_to_uint16(const std::string& fb) {
return (fb[1] << 8) + fb[0];
}
inline uint32_t fb_to_uint32(const std::string& fb) {
return (fb[3] << 24) + (fb[2] << 16) + (fb[1] << 8) + fb[0];
}
constexpr size_t operator "" _KiB(unsigned long long v) {
return v * 1024;
}

Binary file not shown.