mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-24 14:59:24 -05:00
Wavfile class
This commit is contained in:
parent
e56fa0f479
commit
1db138c27a
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
43
firmware/application/filewriter.cpp
Normal file
43
firmware/application/filewriter.cpp
Normal 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 */
|
57
firmware/application/filewriter.hpp
Normal file
57
firmware/application/filewriter.hpp
Normal 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__*/
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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,9 +299,10 @@ TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
|
||||
/* UtilitiesView *****************************************************************/
|
||||
|
||||
UtilitiesView::UtilitiesView(NavigationView& nav) {
|
||||
add_items<2>({ {
|
||||
{ "Whip antenna calculator", ui::Color::green(), [&nav](){ nav.push<WhipCalcView>(); } },
|
||||
{ "Notepad", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
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>(); } }
|
||||
} });
|
||||
}
|
||||
|
@ -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,16 +32,14 @@
|
||||
// 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() {
|
||||
@ -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) {
|
||||
pause = 11025; // p: 0.25s @ 44100Hz
|
||||
memset(audio_buffer, 0, 1024);
|
||||
} else if (code == 11) {
|
||||
pause = 33075; // P: 0.75s @ 44100Hz
|
||||
if (code >= 10) {
|
||||
memset(audio_buffer, 0, 1024);
|
||||
if (code == 10) {
|
||||
pause = 11025; // p: 0.25s @ 44100Hz
|
||||
} else if (code == 11) {
|
||||
pause = 33075; // P: 0.75s @ 44100Hz
|
||||
} 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++;
|
||||
}
|
||||
|
||||
cnt = 0;
|
||||
code_index++;
|
||||
}
|
||||
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[0];
|
||||
cnt = sample_duration;
|
||||
sample_duration = sound_sizes[10]; // Announce
|
||||
sample_counter = 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];
|
||||
}
|
||||
|
||||
uint32_t NumbersStationView::fb_to_uint32(const std::string& fb) {
|
||||
return (fb[3] << 24) + (fb[2] << 16) + (fb[1] << 8) + fb[0];
|
||||
void NumbersStationView::on_tick_second() {
|
||||
if (check_armed.value()) {
|
||||
armed_blink = not armed_blink;
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
|
@ -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];
|
||||
|
||||
@ -77,22 +80,24 @@ private:
|
||||
{ "9" },
|
||||
{ "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" };
|
||||
|
||||
File file;
|
||||
uint8_t id_test, announce_loop;
|
||||
uint32_t cnt;
|
||||
// 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" };
|
||||
|
||||
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 },
|
||||
|
@ -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"
|
||||
);
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include "capture_thread.hpp"
|
||||
#include "signal.hpp"
|
||||
|
||||
#include "wavfile.hpp"
|
||||
#include "bitmap.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
@ -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];
|
||||
reader = std::make_unique<WAVFileReader>();
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
|
||||
|
||||
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,
|
||||
|
@ -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 }
|
||||
|
135
firmware/application/wavfile.cpp
Normal file
135
firmware/application/wavfile.cpp
Normal 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 */
|
165
firmware/application/wavfile.hpp
Normal file
165
firmware/application/wavfile.hpp
Normal 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__*/
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
Loading…
Reference in New Issue
Block a user