diff --git a/README.md b/README.md
index 49137e0..628672d 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,8 @@ Features:
- Drag'n'Drop. Drag'n'Drop tracks right to the Bloody Player window to add tracks to the tracklist. You can also drag'n'drop folders to add tracks from it.
- Tracklists. Save the current tracklist and open it later.
- SFX. Add sound effects or load your VST plugin.
-- Tracklist management. Move tracks in the tracklist or delete some of them by right-clicking on the track or using hotkeys.
-- Search. Use Ctrl + F to open the search window to search for the desired track in the tracklist.
- Oscillogram. Click on the horizontal oscillogram displaying amplitude from time to change the current track's position.
+- Repeat section. Click the right mouse button on the oscillogram to set the left bound for the repetition, click the right mouse button again to set the right bound and make a repetition section in which music will repeat.
+- Tracklist management. Move tracks in the tracklist or delete some of them by right-clicking on the track or using hotkeys.
+- Search. Use Ctrl + F to open the search window to search for the desired track in the tracklist.
- "Repeat Track" / "Random Track". Use buttons under the volume slider to set "Repeat Track" / "Random Track" functions.
diff --git a/TODO.txt b/TODO.txt
index 44ccdfc..8a3c505 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,8 +1,8 @@
-- Speed up the process of adding the tracks.
+- Add a little (non-modal window) "Tutorial" (with pictures) on first startup: tell user to drag'n'drop, Ctrl + F, RMB to create repeat section, RMB on tracks, FX.
+And if user not finished th tutorial then show it again on next start. Or, if user checked "don't show again" don't show it again.
+- Speed up the process of adding the tracks.
- When adding music, we need to add it in the same order in which it was in the folder.
-- Do something with color banding.
- Seems like with 'speed (by pitch)' effect we also need to increase mid-high frequencies on like 3-4 dB and maybe do the same thing with the low and the mid-low.
-- Add bounds in which track will repeat by clicking RMB on the oscillogram. Track's volume in the transition from end bound to start bound will change?
- After the tracklist was cleared volume returns to the default value of 75%, we don't want that, don't change master volume when tracklist is clear and allow to change it if tracklist is clear.
- Add winsock2 and make 'Check for Updates' button in menu (see https://stackoverflow.com/questions/39931347/simple-http-get-with-winsock and https://github.com/VioletGiraffe/github-releases-autoupdater).
- With winsock2 create a server and if another audio file is being opened a new instance of Player will check if one is running and if true then this new track will be sent to currently running instance.
diff --git a/ide/BloodyPlayer.pro.user b/ide/BloodyPlayer.pro.user
index 5d584e0..4a95323 100644
--- a/ide/BloodyPlayer.pro.user
+++ b/ide/BloodyPlayer.pro.user
@@ -1,6 +1,6 @@
-
+
EnvironmentId
diff --git a/src/Controller/controller.cpp b/src/Controller/controller.cpp
index abe3c16..69f1ea1 100644
--- a/src/Controller/controller.cpp
+++ b/src/Controller/controller.cpp
@@ -42,6 +42,11 @@ void Controller::setTrackPos(unsigned int graphPos)
pAudioService->setTrackPos(graphPos);
}
+void Controller::setRepeatPoint(unsigned int graphPos)
+{
+ pAudioService->setRepeatPoint(graphPos);
+}
+
std::string Controller::getBloodyVersion()
{
return pAudioService->getBloodyVersion();
diff --git a/src/Controller/controller.h b/src/Controller/controller.h
index ce74095..2ffd1ff 100644
--- a/src/Controller/controller.h
+++ b/src/Controller/controller.h
@@ -82,6 +82,7 @@ class Controller
void addTracks (std::vector paths);
void setVolume (float fNewVolume);
void setTrackPos (unsigned int graphPos);
+ void setRepeatPoint (unsigned int graphPos);
// Get
diff --git a/src/Model/AudioService/audioservice.cpp b/src/Model/AudioService/audioservice.cpp
index dba0153..b7673c8 100644
--- a/src/Model/AudioService/audioservice.cpp
+++ b/src/Model/AudioService/audioservice.cpp
@@ -21,11 +21,13 @@ AudioService::AudioService(MainWindow* pMainWindow)
{
sBloodyVersion = "1.16.1";
+
this->pMainWindow = pMainWindow;
pSystem = nullptr;
pRndGen = new std::mt19937_64( std::random_device{}() );
iCurrentlyDrawingTrackIndex = new size_t(0);
+
bMonitorTracks = false;
bIsSomeTrackPlaying = false;
bRepeatTrack = false;
@@ -33,6 +35,10 @@ AudioService::AudioService(MainWindow* pMainWindow)
bDrawing = false;
bCurrentTrackPaused = false;
+
+ cRepeatSectionState = 0;
+
+
// FX
pPitch = nullptr;
pPitchForTime = nullptr;
@@ -46,6 +52,7 @@ AudioService::AudioService(MainWindow* pMainWindow)
fCurrentSpeedByPitch = 1.0f;
fCurrentSpeedByTime = 1.0f;
+
FMODinit();
}
@@ -469,6 +476,13 @@ void AudioService::playTrack(size_t iTrackIndex, bool bDontLockMutex)
vTracksHistory.erase( vTracksHistory.begin() );
}
}
+
+ if (cRepeatSectionState != 0)
+ {
+ pMainWindow->eraseRepeatSection();
+
+ cRepeatSectionState = 0;
+ }
}
else
{
@@ -503,12 +517,26 @@ void AudioService::setTrackPos(unsigned int graphPos)
// track->getMaxValueOnGraph() - 100%
// graphPos - x%
- // cast to unsigned long long to avoid overflow
double fPosMult = graphPos / static_cast(tracks[iCurrentlyPlayingTrackIndex]->getMaxValueOnGraph());
+ // cast to avoid overflow
unsigned int iPosInMS = static_cast(tracks[iCurrentlyPlayingTrackIndex]->getLengthInMS() * fPosMult);
- if ( tracks[iCurrentlyPlayingTrackIndex]->setPositionInMS(iPosInMS) )
+
+ if (cRepeatSectionState == 2)
{
- pMainWindow->setCurrentPos(fPosMult, tracks[iCurrentlyPlayingTrackIndex]->getCurrentTime());
+ if ( (iPosInMS > iFirstRepeatTimePos) && (iPosInMS < iSecondRepeatTimePos) )
+ {
+ if ( tracks[iCurrentlyPlayingTrackIndex]->setPositionInMS(iPosInMS) )
+ {
+ pMainWindow->setCurrentPos(fPosMult, tracks[iCurrentlyPlayingTrackIndex]->getCurrentTime());
+ }
+ }
+ }
+ else
+ {
+ if ( tracks[iCurrentlyPlayingTrackIndex]->setPositionInMS(iPosInMS) )
+ {
+ pMainWindow->setCurrentPos(fPosMult, tracks[iCurrentlyPlayingTrackIndex]->getCurrentTime());
+ }
}
}
@@ -522,6 +550,70 @@ void AudioService::setTrackPos(unsigned int graphPos)
mtxTracksVec.unlock();
}
+void AudioService::setRepeatPoint(unsigned int graphPos)
+{
+ mtxTracksVec.lock();
+
+ if ( (tracks.size() > 0) && (bIsSomeTrackPlaying || bCurrentTrackPaused) )
+ {
+ // track->getMaxValueOnGraph() - 100%
+ // graphPos - x%
+
+ double fPosMult = graphPos / static_cast(tracks[iCurrentlyPlayingTrackIndex]->getMaxValueOnGraph());
+
+ if (cRepeatSectionState == 0)
+ {
+ // Set the first point
+ pMainWindow->setRepeatPoint(true, fPosMult);
+
+ cRepeatSectionState = 1;
+
+ unsigned int iPosInMS = static_cast(tracks[iCurrentlyPlayingTrackIndex]->getLengthInMS() * fPosMult);
+ iFirstRepeatTimePos = iPosInMS;
+
+
+ // Set track pos
+ if ( tracks[iCurrentlyPlayingTrackIndex]->getPositionInMS() < iFirstRepeatTimePos )
+ {
+ if ( tracks[iCurrentlyPlayingTrackIndex]->setPositionInMS(iFirstRepeatTimePos) )
+ {
+ pMainWindow->setCurrentPos(fPosMult, tracks[iCurrentlyPlayingTrackIndex]->getCurrentTime());
+ }
+ }
+ }
+ else if (cRepeatSectionState == 1)
+ {
+ // One point already on graph, set the second one
+ pMainWindow->setRepeatPoint(false, fPosMult);
+
+ // Done
+ cRepeatSectionState = 2;
+
+ unsigned int iPosInMS = static_cast(tracks[iCurrentlyPlayingTrackIndex]->getLengthInMS() * fPosMult);
+ iSecondRepeatTimePos = iPosInMS;
+
+
+ // Set track pos
+ if ( tracks[iCurrentlyPlayingTrackIndex]->getPositionInMS() > iSecondRepeatTimePos )
+ {
+ if ( tracks[iCurrentlyPlayingTrackIndex]->setPositionInMS(iFirstRepeatTimePos) )
+ {
+ pMainWindow->setCurrentPos(fPosMult, tracks[iCurrentlyPlayingTrackIndex]->getCurrentTime());
+ }
+ }
+ }
+ else
+ {
+ // User pressed RMB again, erase section
+ pMainWindow->eraseRepeatSection();
+
+ cRepeatSectionState = 0;
+ }
+ }
+
+ mtxTracksVec.unlock();
+}
+
std::string AudioService::getBloodyVersion()
{
return sBloodyVersion;
@@ -1345,6 +1437,29 @@ void AudioService::monitorTrack()
}
else
{
+ if (cRepeatSectionState == 2)
+ {
+ if ( tracks[iCurrentlyPlayingTrackIndex]->getPositionInMS() > iSecondRepeatTimePos - MAX_TIME_ERROR_MS )
+ {
+ for (float i = fCurrentVolume; i >= 0.0f; i-= 0.01f)
+ {
+ tracks[iCurrentlyPlayingTrackIndex]->setVolume(i);
+
+ std::this_thread::sleep_for (std::chrono::milliseconds(2));
+ }
+
+ tracks[iCurrentlyPlayingTrackIndex]->setPositionInMS (iFirstRepeatTimePos);
+
+ for (float i = 0; i <= fCurrentVolume; i+= 0.01f)
+ {
+ tracks[iCurrentlyPlayingTrackIndex]->setVolume(i);
+
+ std::this_thread::sleep_for (std::chrono::milliseconds(2));
+ }
+ }
+ }
+
+
// track->getLengthInMS() - 1.0
// track->getPositionInMS() - x
diff --git a/src/Model/AudioService/audioservice.h b/src/Model/AudioService/audioservice.h
index 3ce0b05..71c0557 100644
--- a/src/Model/AudioService/audioservice.h
+++ b/src/Model/AudioService/audioservice.h
@@ -89,6 +89,7 @@ class AudioService
void addTracks (std::vector paths);
void setVolume (float fNewVolume);
void setTrackPos (unsigned int graphPos);
+ void setRepeatPoint (unsigned int graphPos);
// Get
@@ -155,6 +156,24 @@ class AudioService
bool bDrawing;
+ // Search
+ std::vector vSearchResult;
+ size_t iCurrentPosInSearchVec;
+ bool bFirstSearchAfterKeyChange;
+
+
+ // Tracks
+ std::vector