Start midi file processing.
This commit is contained in:
parent
59c6161fdb
commit
36826fa1d4
22 changed files with 528 additions and 44 deletions
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# MediaTool
|
||||||
|
Hobby project - the idea is to build a set of multimedia tools 'from scratch' in C++ for fun/practice.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake $PATH_TO_SOURCE
|
||||||
|
make
|
||||||
|
apps/$NAME_OF_APP
|
||||||
|
```
|
|
@ -19,9 +19,9 @@ target_link_libraries(sample_console PUBLIC console core network
|
||||||
add_executable(xml_practice xml-practice.cpp)
|
add_executable(xml_practice xml-practice.cpp)
|
||||||
target_include_directories(xml_practice PUBLIC
|
target_include_directories(xml_practice PUBLIC
|
||||||
"${PROJECT_SOURCE_DIR}/src/core"
|
"${PROJECT_SOURCE_DIR}/src/core"
|
||||||
"${PROJECT_SOURCE_DIR}/src/core/xml"
|
"${PROJECT_SOURCE_DIR}/src/web/xml"
|
||||||
)
|
)
|
||||||
target_link_libraries(xml_practice PUBLIC core)
|
target_link_libraries(xml_practice PUBLIC core web)
|
||||||
|
|
||||||
# Markdown practice
|
# Markdown practice
|
||||||
add_executable(markdown_practice markdown-practice.cpp)
|
add_executable(markdown_practice markdown-practice.cpp)
|
||||||
|
|
|
@ -1,16 +1,37 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "CommandLineArgs.h"
|
||||||
#include "MainApplication.h"
|
#include "MainApplication.h"
|
||||||
|
|
||||||
int main()
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
CommandLineArgs command_line_args;
|
||||||
|
command_line_args.Process(argc, argv);
|
||||||
|
|
||||||
|
std::string program_type;
|
||||||
|
if(command_line_args.GetNumberOfArgs() > 1)
|
||||||
|
{
|
||||||
|
program_type = command_line_args.GetArg(1);
|
||||||
|
}
|
||||||
|
|
||||||
// Start the main app
|
// Start the main app
|
||||||
auto main_app = MainApplication::Create();
|
auto main_app = MainApplication::Create();
|
||||||
main_app->Initialize(std::filesystem::current_path());
|
main_app->Initialize(std::filesystem::current_path());
|
||||||
|
|
||||||
//main_app->RunServer();
|
if(program_type == "server")
|
||||||
main_app->PlayAudio();
|
{
|
||||||
|
main_app->RunServer();
|
||||||
|
}
|
||||||
|
else if(program_type == "audio")
|
||||||
|
{
|
||||||
|
main_app->PlayAudio();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Unknown program type" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
main_app->ShutDown();
|
main_app->ShutDown();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
list(APPEND audio_LIB_INCLUDES
|
list(APPEND audio_LIB_INCLUDES
|
||||||
AudioDevice.cpp
|
AudioDevice.cpp
|
||||||
AudioManager.cpp
|
AudioManager.cpp
|
||||||
audio_interfaces/AlsaInterface.cpp)
|
audio_interfaces/AlsaInterface.cpp
|
||||||
|
midi/MidiReader.cpp
|
||||||
|
midi/MidiTrack.cpp
|
||||||
|
midi/MidiDocument.cpp
|
||||||
|
midi/MidiEvent.cpp
|
||||||
|
midi/MetaMidiEvent.cpp)
|
||||||
|
|
||||||
|
|
||||||
add_library(audio SHARED ${audio_LIB_INCLUDES})
|
add_library(audio SHARED ${audio_LIB_INCLUDES})
|
||||||
target_include_directories(audio PUBLIC
|
target_include_directories(audio PUBLIC
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/core/loggers"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/audio_interfaces"
|
"${CMAKE_CURRENT_SOURCE_DIR}/audio_interfaces"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/midi"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(audio PUBLIC asound)
|
target_link_libraries(audio PUBLIC core asound)
|
|
@ -1,6 +1,5 @@
|
||||||
#include "AlsaInterface.h"
|
#include "AlsaInterface.h"
|
||||||
|
#include "FileLogger.h"
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
AlsaInterface::AlsaInterface()
|
AlsaInterface::AlsaInterface()
|
||||||
:mHandle(),
|
:mHandle(),
|
||||||
|
@ -22,17 +21,18 @@ std::shared_ptr<AlsaInterface> AlsaInterface::Create()
|
||||||
|
|
||||||
void AlsaInterface::OpenDevice(AudioDevicePtr device)
|
void AlsaInterface::OpenDevice(AudioDevicePtr device)
|
||||||
{
|
{
|
||||||
|
MLOG_INFO("Opening Device");
|
||||||
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
|
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
|
||||||
if (snd_pcm_open(&mHandle, device->GetName().c_str(), stream, 0) < 0)
|
if (snd_pcm_open(&mHandle, device->GetName().c_str(), stream, 0) < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Error opening PCM device: " << device->GetName() << std::endl;
|
MLOG_ERROR("Error opening PCM device: " + device->GetName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_pcm_hw_params_alloca(&mHardwareParams);
|
snd_pcm_hw_params_alloca(&mHardwareParams);
|
||||||
if (snd_pcm_hw_params_any(mHandle, mHardwareParams) < 0)
|
if (snd_pcm_hw_params_any(mHandle, mHardwareParams) < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Can not configure this PCM device.\n" << std::endl;
|
MLOG_ERROR("Can not configure this PCM device.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ void AlsaInterface::OpenDevice(AudioDevicePtr device)
|
||||||
/* Apply HW parameter settings to */
|
/* Apply HW parameter settings to */
|
||||||
/* PCM device and prepare device */
|
/* PCM device and prepare device */
|
||||||
if (snd_pcm_hw_params(mHandle, mHardwareParams) < 0) {
|
if (snd_pcm_hw_params(mHandle, mHardwareParams) < 0) {
|
||||||
std::cerr << "Error setting HW params." << std::endl;
|
MLOG_ERROR("Error setting HW params.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ void AlsaInterface::OpenDevice(AudioDevicePtr device)
|
||||||
void AlsaInterface::SetAccessType(AudioDevicePtr device)
|
void AlsaInterface::SetAccessType(AudioDevicePtr device)
|
||||||
{
|
{
|
||||||
if (snd_pcm_hw_params_set_access(mHandle, mHardwareParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
|
if (snd_pcm_hw_params_set_access(mHandle, mHardwareParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
|
||||||
std::cerr << "Error setting device access." << std::endl;
|
MLOG_ERROR("Error setting device access.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ void AlsaInterface::SetSampleFormat(AudioDevicePtr device)
|
||||||
{
|
{
|
||||||
/* Set sample format */
|
/* Set sample format */
|
||||||
if (snd_pcm_hw_params_set_format(mHandle, mHardwareParams, SND_PCM_FORMAT_S16_LE) < 0) {
|
if (snd_pcm_hw_params_set_format(mHandle, mHardwareParams, SND_PCM_FORMAT_S16_LE) < 0) {
|
||||||
std::cerr << "Error setting format. " << std::endl;
|
MLOG_ERROR("Error setting format. ");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,11 +74,11 @@ void AlsaInterface::SetSampleRate(AudioDevicePtr device)
|
||||||
unsigned exact_rate = rate;
|
unsigned exact_rate = rate;
|
||||||
if (snd_pcm_hw_params_set_rate_near(mHandle, mHardwareParams, &exact_rate, 0) < 0)
|
if (snd_pcm_hw_params_set_rate_near(mHandle, mHardwareParams, &exact_rate, 0) < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Error setting rate. " << std::endl;
|
MLOG_ERROR("Error setting rate. ");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rate != exact_rate) {
|
if (rate != exact_rate) {
|
||||||
std::cerr << "The rate is not supported by your hardware." << std::endl;
|
MLOG_ERROR("The rate is not supported by your hardware.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ void AlsaInterface::SetPeriod(AudioDevicePtr device)
|
||||||
/* Set number of periods. Periods used to be called fragments. */
|
/* Set number of periods. Periods used to be called fragments. */
|
||||||
if (snd_pcm_hw_params_set_periods(mHandle, mHardwareParams, device->GetPeriod(), 0) < 0)
|
if (snd_pcm_hw_params_set_periods(mHandle, mHardwareParams, device->GetPeriod(), 0) < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Error setting periods. " << std::endl;
|
MLOG_ERROR("Error setting periods. ");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ void AlsaInterface::SetBufferSize(AudioDevicePtr device)
|
||||||
/* latency = periodsize * periods / (rate * bytes_per_frame) */
|
/* latency = periodsize * periods / (rate * bytes_per_frame) */
|
||||||
if (snd_pcm_hw_params_set_buffer_size(mHandle, mHardwareParams, (mPeriodSize * periods)>>2) < 0)
|
if (snd_pcm_hw_params_set_buffer_size(mHandle, mHardwareParams, (mPeriodSize * periods)>>2) < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Error setting buffersize. " << std::endl;
|
MLOG_ERROR("Error setting buffersize. ");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,13 +111,14 @@ void AlsaInterface::SetChannelNumber(AudioDevicePtr device)
|
||||||
/* Set number of channels */
|
/* Set number of channels */
|
||||||
if (snd_pcm_hw_params_set_channels(mHandle, mHardwareParams, device->GetNumChannels()) < 0)
|
if (snd_pcm_hw_params_set_channels(mHandle, mHardwareParams, device->GetNumChannels()) < 0)
|
||||||
{
|
{
|
||||||
std::cout << "Error setting channels" << std::endl;
|
MLOG_ERROR("Error setting channels");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlsaInterface::Play(AudioDevicePtr device)
|
void AlsaInterface::Play(AudioDevicePtr device)
|
||||||
{
|
{
|
||||||
|
MLOG_INFO("Playing audio");
|
||||||
int num_frames = 10;
|
int num_frames = 10;
|
||||||
unsigned char *data = (unsigned char *)malloc(mPeriodSize);
|
unsigned char *data = (unsigned char *)malloc(mPeriodSize);
|
||||||
int frames = mPeriodSize >> 2;
|
int frames = mPeriodSize >> 2;
|
||||||
|
@ -135,7 +136,7 @@ void AlsaInterface::Play(AudioDevicePtr device)
|
||||||
while ((snd_pcm_writei(mHandle, data, frames)) < 0)
|
while ((snd_pcm_writei(mHandle, data, frames)) < 0)
|
||||||
{
|
{
|
||||||
snd_pcm_prepare(mHandle);
|
snd_pcm_prepare(mHandle);
|
||||||
fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>\n");
|
MLOG_ERROR("<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
src/audio/midi/MetaMidiEvent.cpp
Normal file
7
src/audio/midi/MetaMidiEvent.cpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include "MetaMidiEvent.h"
|
||||||
|
|
||||||
|
MetaMidiEvent::MetaMidiEvent()
|
||||||
|
: MidiEvent()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
26
src/audio/midi/MetaMidiEvent.h
Normal file
26
src/audio/midi/MetaMidiEvent.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "MidiEvent.h"
|
||||||
|
|
||||||
|
class MetaMidiEvent : public MidiEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
UNSET,
|
||||||
|
TRACK_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
Type mType = Type::UNSET;
|
||||||
|
std::string mLabel;
|
||||||
|
MetaMidiEvent();
|
||||||
|
|
||||||
|
static bool IsTrackName(char c)
|
||||||
|
{
|
||||||
|
return (c & META_TRACK_NAME) == META_TRACK_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int META_TRACK_NAME = 0x3; // 0000 0011
|
||||||
|
};
|
6
src/audio/midi/MidiDocument.cpp
Normal file
6
src/audio/midi/MidiDocument.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "MidiDocument.h"
|
||||||
|
|
||||||
|
MidiDocument::MidiDocument()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
16
src/audio/midi/MidiDocument.h
Normal file
16
src/audio/midi/MidiDocument.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "MidiTrack.h"
|
||||||
|
|
||||||
|
class MidiDocument
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int mFormatType = 0;
|
||||||
|
int mExpectedTracks = 0;
|
||||||
|
bool mUseFps = true;
|
||||||
|
int mFps = 0;
|
||||||
|
int ticksPerFrame = 0;
|
||||||
|
std::vector<MidiTrack> mTracks;
|
||||||
|
MidiDocument();
|
||||||
|
};
|
6
src/audio/midi/MidiEvent.cpp
Normal file
6
src/audio/midi/MidiEvent.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "MidiEvent.h"
|
||||||
|
|
||||||
|
MidiEvent::MidiEvent()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
17
src/audio/midi/MidiEvent.h
Normal file
17
src/audio/midi/MidiEvent.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class MidiEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
int mTimeDelta = 0;
|
||||||
|
MidiEvent();
|
||||||
|
|
||||||
|
static bool IsMetaEvent(char c)
|
||||||
|
{
|
||||||
|
return (c & META_EVENT) == META_EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const int META_EVENT = 0xFF; // 1111 1111
|
||||||
|
};
|
211
src/audio/midi/MidiReader.cpp
Normal file
211
src/audio/midi/MidiReader.cpp
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
#include "MidiReader.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <bitset>
|
||||||
|
#include "MidiDocument.h"
|
||||||
|
#include "ByteUtils.h"
|
||||||
|
#include "MidiTrack.h"
|
||||||
|
|
||||||
|
MidiReader::MidiReader()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetBytes(std::ifstream& file, char* buffer, unsigned number)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
for(unsigned idx=0; idx<number; idx++)
|
||||||
|
{
|
||||||
|
if(file.get(c))
|
||||||
|
{
|
||||||
|
buffer[idx] = c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetWord(std::ifstream& file, char* buffer)
|
||||||
|
{
|
||||||
|
return GetBytes(file, buffer, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetDWord(std::ifstream& file, char* buffer)
|
||||||
|
{
|
||||||
|
return GetBytes(file, buffer, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MidiReader::ProcessHeader(std::ifstream& file)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
unsigned bufferSize = ByteUtils::BYTES_PER_DWORD;
|
||||||
|
char buffer[bufferSize];
|
||||||
|
|
||||||
|
if(!GetDWord(file, buffer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ByteUtils::CompareDWords(buffer, HeaderLabel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!GetDWord(file, buffer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = ByteUtils::ToDWord(buffer);
|
||||||
|
|
||||||
|
if(!GetWord(file, buffer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mDocument.mFormatType = ByteUtils::ToWord(buffer);
|
||||||
|
|
||||||
|
if(!GetWord(file, buffer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mDocument.mExpectedTracks = ByteUtils::ToWord(buffer);
|
||||||
|
|
||||||
|
if(!GetWord(file, buffer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int time_division;
|
||||||
|
std::memcpy(&time_division, buffer, sizeof(int));
|
||||||
|
|
||||||
|
const int TOP_7_BITS = 0x7F00; // 0111 1111 - 0000 0000
|
||||||
|
mDocument.mUseFps = ByteUtils::GetWordFirstBit(time_division);
|
||||||
|
mDocument.mFps = ((~time_division & TOP_7_BITS) >> 8) - 1; // Reverse 2complement of next 7 bits
|
||||||
|
mDocument.ticksPerFrame = ByteUtils::GetWordLastByte(time_division);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiReader::ProcessTimeDelta(std::ifstream& file)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
file.get(c);
|
||||||
|
bool needs_next = c & ByteUtils::BYTE_FIRST_BIT;
|
||||||
|
int time_offset = 0;
|
||||||
|
if(!needs_next)
|
||||||
|
{
|
||||||
|
time_offset = int(c);
|
||||||
|
mTrackByteCount ++;
|
||||||
|
}
|
||||||
|
std::cout << "Next " << needs_next <<std::endl;
|
||||||
|
std::cout << "Offset " << time_offset <<std::endl;
|
||||||
|
return time_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MidiReader::ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
file.get(c);
|
||||||
|
mTrackByteCount ++;
|
||||||
|
if(MetaMidiEvent::IsTrackName(c))
|
||||||
|
{
|
||||||
|
event.mType = MetaMidiEvent::Type::TRACK_NAME;
|
||||||
|
file.get(c);
|
||||||
|
mTrackByteCount ++;
|
||||||
|
int length = int(c);
|
||||||
|
std::string name;
|
||||||
|
std::cout << "Track name: " << length <<std::endl;
|
||||||
|
|
||||||
|
for(unsigned idx=0; idx<length; idx++)
|
||||||
|
{
|
||||||
|
file.get(c);
|
||||||
|
mTrackByteCount ++;
|
||||||
|
name += c;
|
||||||
|
}
|
||||||
|
std::cout << name << std::endl;
|
||||||
|
event.mLabel = "Track name";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "something else" << std::endl;
|
||||||
|
std::cout << "Next " << std::bitset<8>(c) << "|" <<std::endl;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MidiReader::ProcessEvent(std::ifstream& file)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int timeDelta = ProcessTimeDelta(file);
|
||||||
|
|
||||||
|
file.get(c);
|
||||||
|
mTrackByteCount ++;
|
||||||
|
if(MidiEvent::IsMetaEvent(c))
|
||||||
|
{
|
||||||
|
MetaMidiEvent event;
|
||||||
|
event.mTimeDelta = timeDelta;
|
||||||
|
std::cout << "Meta event " <<std::endl;
|
||||||
|
ProcessMetaEvent(file, event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int first_byte = 0xF0;
|
||||||
|
int event_type = (c & first_byte) >> 4;
|
||||||
|
std::cout << "Next " << std::bitset<8>(c) << "|" << event_type <<std::endl;
|
||||||
|
std::cout << event_type << std::endl;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MidiReader::ProcessTrackChunk(std::ifstream& file)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
unsigned bufferSize = ByteUtils::BYTES_PER_DWORD;
|
||||||
|
char buffer[bufferSize];
|
||||||
|
|
||||||
|
if(!GetDWord(file, buffer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ByteUtils::CompareDWords(buffer, TrackChunkLabel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!GetDWord(file, buffer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int chunkSize = ByteUtils::ToDWord(buffer);
|
||||||
|
std::cout << "Chunk size: "<< chunkSize << std::endl;
|
||||||
|
|
||||||
|
mTrackByteCount = 0;
|
||||||
|
MidiTrack track;
|
||||||
|
ProcessEvent(file);
|
||||||
|
std::cout << "Track byte count: "<< mTrackByteCount << std::endl;
|
||||||
|
|
||||||
|
ProcessEvent(file);
|
||||||
|
|
||||||
|
mDocument.mTracks.push_back(track);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiReader::Read(const std::string& path)
|
||||||
|
{
|
||||||
|
std::ifstream file(path);
|
||||||
|
if(!ProcessHeader(file))
|
||||||
|
{
|
||||||
|
std::cout << "Problem processing header" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ProcessTrackChunk(file))
|
||||||
|
{
|
||||||
|
std::cout << "Problem processing track chunk" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
31
src/audio/midi/MidiReader.h
Normal file
31
src/audio/midi/MidiReader.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MidiDocument.h"
|
||||||
|
#include "MetaMidiEvent.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class MidiReader
|
||||||
|
{
|
||||||
|
static constexpr const char TrackChunkLabel[] = "MTrk";
|
||||||
|
static constexpr const char HeaderLabel[] = "MThd";
|
||||||
|
MidiDocument mDocument;
|
||||||
|
unsigned mTrackByteCount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
MidiReader();
|
||||||
|
|
||||||
|
void Read(const std::string& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool ProcessHeader(std::ifstream& file);
|
||||||
|
|
||||||
|
bool ProcessTrackChunk(std::ifstream& file);
|
||||||
|
|
||||||
|
bool ProcessEvent(std::ifstream& file);
|
||||||
|
|
||||||
|
bool ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event);
|
||||||
|
|
||||||
|
int ProcessTimeDelta(std::ifstream& file);
|
||||||
|
};
|
11
src/audio/midi/MidiTrack.cpp
Normal file
11
src/audio/midi/MidiTrack.cpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include "MidiTrack.h"
|
||||||
|
|
||||||
|
MidiTrack::MidiTrack()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiTrack::AddEvent(const MidiEvent& event)
|
||||||
|
{
|
||||||
|
mEvents.push_back(event);
|
||||||
|
}
|
15
src/audio/midi/MidiTrack.h
Normal file
15
src/audio/midi/MidiTrack.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "MidiEvent.h"
|
||||||
|
|
||||||
|
class MidiTrack
|
||||||
|
{
|
||||||
|
std::vector<MidiEvent> mEvents;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
MidiTrack();
|
||||||
|
|
||||||
|
void AddEvent(const MidiEvent& event);
|
||||||
|
};
|
|
@ -9,5 +9,6 @@ target_include_directories(console PUBLIC
|
||||||
"${PROJECT_SOURCE_DIR}/src/network"
|
"${PROJECT_SOURCE_DIR}/src/network"
|
||||||
"${PROJECT_SOURCE_DIR}/src/network/sockets"
|
"${PROJECT_SOURCE_DIR}/src/network/sockets"
|
||||||
"${PROJECT_SOURCE_DIR}/src/audio"
|
"${PROJECT_SOURCE_DIR}/src/audio"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/audio/midi"
|
||||||
"${PROJECT_SOURCE_DIR}/src/audio/audio_interfaces"
|
"${PROJECT_SOURCE_DIR}/src/audio/audio_interfaces"
|
||||||
)
|
)
|
|
@ -1,8 +1,9 @@
|
||||||
#include "MainApplication.h"
|
#include "MainApplication.h"
|
||||||
|
#include "FileLogger.h"
|
||||||
|
#include "MidiReader.h"
|
||||||
|
|
||||||
MainApplication::MainApplication()
|
MainApplication::MainApplication()
|
||||||
:mLogger(),
|
: mDatabaseManager()
|
||||||
mDatabaseManager()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,10 +15,9 @@ MainApplication::~MainApplication()
|
||||||
|
|
||||||
void MainApplication::Initialize(const std::filesystem::path& workDir)
|
void MainApplication::Initialize(const std::filesystem::path& workDir)
|
||||||
{
|
{
|
||||||
mLogger = FileLogger::Create();
|
FileLogger::GetInstance().SetWorkDirectory(workDir.string());
|
||||||
mLogger->SetWorkDirectory(workDir.string());
|
FileLogger::GetInstance().Open();
|
||||||
mLogger->Open();
|
MLOG_INFO("Launched");
|
||||||
mLogger->LogLine("Launched");
|
|
||||||
|
|
||||||
mDatabaseManager = DatabaseManager::Create();
|
mDatabaseManager = DatabaseManager::Create();
|
||||||
mDatabaseManager->CreateDatabase(workDir.string() + "/database.db");
|
mDatabaseManager->CreateDatabase(workDir.string() + "/database.db");
|
||||||
|
@ -34,16 +34,19 @@ void MainApplication::RunServer()
|
||||||
|
|
||||||
void MainApplication::PlayAudio()
|
void MainApplication::PlayAudio()
|
||||||
{
|
{
|
||||||
auto device = AudioDevice::Create();
|
MidiReader reader;
|
||||||
mAudioManager->GetAudioInterface()->OpenDevice(device);
|
reader.Read("/home/james/sample.mid");
|
||||||
mAudioManager->GetAudioInterface()->Play(device);
|
// auto device = AudioDevice::Create();
|
||||||
|
// mAudioManager->GetAudioInterface()->OpenDevice(device);
|
||||||
|
// mAudioManager->GetAudioInterface()->Play(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainApplication::ShutDown()
|
void MainApplication::ShutDown()
|
||||||
{
|
{
|
||||||
mLogger->Close();
|
|
||||||
mDatabaseManager->OnShutDown();
|
mDatabaseManager->OnShutDown();
|
||||||
mNetworkManager->ShutDown();
|
mNetworkManager->ShutDown();
|
||||||
|
MLOG_INFO("Shut down");
|
||||||
|
FileLogger::GetInstance().Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MainApplication> MainApplication::Create()
|
std::shared_ptr<MainApplication> MainApplication::Create()
|
||||||
|
|
|
@ -12,7 +12,6 @@ class MainApplication
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
FileLoggerPtr mLogger;
|
|
||||||
DatabaseManagerPtr mDatabaseManager;
|
DatabaseManagerPtr mDatabaseManager;
|
||||||
NetworkManagerPtr mNetworkManager;
|
NetworkManagerPtr mNetworkManager;
|
||||||
AudioManagerPtr mAudioManager;
|
AudioManagerPtr mAudioManager;
|
||||||
|
|
81
src/core/ByteUtils.h
Normal file
81
src/core/ByteUtils.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
#pragma once
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
class ByteUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Word = int;
|
||||||
|
using DWord = int;
|
||||||
|
|
||||||
|
static int GetWordFirstBit(const Word word)
|
||||||
|
{
|
||||||
|
return word & ByteUtils::WORD_FIRST_BIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int GetWordLastByte(const Word word)
|
||||||
|
{
|
||||||
|
return word & ByteUtils::WORD_LAST_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReverseBuffer(char* buffer, char* reverse, unsigned size)
|
||||||
|
{
|
||||||
|
for(unsigned idx=0; idx<size; idx++)
|
||||||
|
{
|
||||||
|
reverse[idx] = buffer[size - 1 -idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ToInt(char* buffer, unsigned size, bool reverse = true)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
if(reverse)
|
||||||
|
{
|
||||||
|
char reversed[size];
|
||||||
|
ReverseBuffer(buffer, reversed, size);
|
||||||
|
std::memcpy(&result, reversed, sizeof(int));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::memcpy(&result, buffer, sizeof(int));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Word ToWord(char* buffer, bool reverse = true)
|
||||||
|
{
|
||||||
|
return ToInt(buffer, BYTES_PER_WORD, reverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWord ToDWord(char* buffer, bool reverse = true)
|
||||||
|
{
|
||||||
|
return ToInt(buffer, BYTES_PER_DWORD, reverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Compare(char* buffer, const char* tag, unsigned size)
|
||||||
|
{
|
||||||
|
for(unsigned idx=0; idx<size; idx++)
|
||||||
|
{
|
||||||
|
if(tag[idx] != buffer[idx])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CompareDWords(char* buffer, const char* tag)
|
||||||
|
{
|
||||||
|
return Compare(buffer, tag, BYTES_PER_DWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CompareWords(char* buffer, const char* tag)
|
||||||
|
{
|
||||||
|
return Compare(buffer, tag, BYTES_PER_WORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int BYTES_PER_WORD = 2;
|
||||||
|
static const int BYTES_PER_DWORD = 4;
|
||||||
|
static const int BYTE_FIRST_BIT = 0x40; // 1000 0000
|
||||||
|
static const int WORD_FIRST_BIT = 0x8000; // 1000 0000 - 0000 0000
|
||||||
|
static const int WORD_LAST_BYTE = 0x00FF; // 0000 0000 - 1111 1111
|
||||||
|
};
|
|
@ -3,7 +3,8 @@ list(APPEND core_LIB_INCLUDES
|
||||||
Color.cpp
|
Color.cpp
|
||||||
CommandLineArgs.cpp
|
CommandLineArgs.cpp
|
||||||
loggers/FileLogger.cpp
|
loggers/FileLogger.cpp
|
||||||
StringUtils.cpp)
|
StringUtils.cpp
|
||||||
|
http/HttpResponse.cpp)
|
||||||
|
|
||||||
# add the executable
|
# add the executable
|
||||||
add_library(core SHARED ${core_LIB_INCLUDES})
|
add_library(core SHARED ${core_LIB_INCLUDES})
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
#include "FileLogger.h"
|
#include "FileLogger.h"
|
||||||
|
#include <ctime>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
FileLogger::FileLogger()
|
|
||||||
:mWorkDirectory(),
|
|
||||||
mFileName("MT_Log.txt"),
|
|
||||||
mFileStream()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLogger::~FileLogger()
|
FileLogger::~FileLogger()
|
||||||
{
|
{
|
||||||
|
@ -38,7 +33,9 @@ void FileLogger::LogLine(const std::string& line)
|
||||||
mFileStream << line << std::endl;
|
mFileStream << line << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<FileLogger> FileLogger::Create()
|
void FileLogger::LogLine(const std::string& logType, const std::string& line, const std::string& fileName, const std::string& functionName, int lineNumber)
|
||||||
{
|
{
|
||||||
return std::make_shared<FileLogger>();
|
std::time_t t = std::time(nullptr);
|
||||||
|
const std::string cleanedFileName = fileName.substr(fileName.find_last_of("/\\") + 1);
|
||||||
|
mFileStream << logType << "|" << std::put_time(std::gmtime(&t), "%T") << "|" << cleanedFileName << "::" << functionName << "::" << lineNumber << "|" << line << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#define MLOG_INFO(msg) FileLogger::GetInstance().LogLine("Info", msg, __FILE__, __FUNCTION__, __LINE__);
|
||||||
|
#define MLOG_ERROR(msg) FileLogger::GetInstance().LogLine("Error", msg, __FILE__, __FUNCTION__, __LINE__);
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -12,9 +14,24 @@ class FileLogger
|
||||||
|
|
||||||
std::ofstream mFileStream;
|
std::ofstream mFileStream;
|
||||||
|
|
||||||
|
FileLogger()
|
||||||
|
:mWorkDirectory(),
|
||||||
|
mFileName("MT_Log.txt"),
|
||||||
|
mFileStream()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FileLogger();
|
static FileLogger& GetInstance()
|
||||||
|
{
|
||||||
|
static FileLogger instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileLogger(FileLogger const&) = delete;
|
||||||
|
void operator=(FileLogger const&) = delete;
|
||||||
|
|
||||||
~FileLogger();
|
~FileLogger();
|
||||||
|
|
||||||
|
@ -28,7 +45,7 @@ public:
|
||||||
|
|
||||||
void LogLine(const std::string& line);
|
void LogLine(const std::string& line);
|
||||||
|
|
||||||
static std::shared_ptr<FileLogger> Create();
|
void LogLine(const std::string& logType, const std::string& line, const std::string& fileName = "", const std::string& functionName = "", int lineNumber=-1);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue