Calculate tone lengths based on user preferences
This commit is contained in:
parent
cf89b9b4f3
commit
4216961b5b
3 changed files with 63 additions and 82 deletions
5
TODO
5
TODO
|
@ -1,7 +1,4 @@
|
|||
Basic:
|
||||
* Correct tone length calculation
|
||||
* Settings
|
||||
|
||||
* Koch lesson select
|
||||
* Run a koch lesson
|
||||
* Koch result analysis screen
|
||||
|
@ -12,6 +9,8 @@ Polish:
|
|||
* Visual styling
|
||||
* Settings info text
|
||||
* Extract UI strings into resource file
|
||||
* Play sample sounds when settings change
|
||||
* More Settings
|
||||
|
||||
Bonus:
|
||||
* Morse machine
|
||||
|
|
|
@ -6,9 +6,9 @@ import android.media.AudioTrack
|
|||
import android.media.MediaDataSource
|
||||
import android.view.KeyEvent
|
||||
import java.util.concurrent.ArrayBlockingQueue
|
||||
import java.util.concurrent.BlockingQueue
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sin
|
||||
|
||||
enum class SoundTypes {
|
||||
|
@ -23,44 +23,44 @@ fun StringToSoundSequence(s : String) : List<SoundTypes> {
|
|||
|
||||
val first = when(s[0]) {
|
||||
' ' -> listOf(SoundTypes.WORD_SPACE)
|
||||
'A' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'B' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'C' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'D' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'E' -> listOf(SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'F' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'G' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'H' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'I' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'J' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'K' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'L' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'M' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'N' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'O' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'P' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'Q' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'R' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'S' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'T' -> listOf(SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'U' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'V' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'W' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'X' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'Y' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'Z' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'0' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'1' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'2' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'3' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'4' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'5' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'6' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'7' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'8' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'9' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'.' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.WORD_SPACE)
|
||||
'?' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.WORD_SPACE)
|
||||
'A' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'B' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'C' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'D' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'E' -> listOf(SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'F' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'G' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'H' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'I' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'J' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'K' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'L' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'M' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'N' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'O' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'P' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'Q' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'R' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'S' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'T' -> listOf(SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'U' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'V' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'W' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'X' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'Y' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'Z' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'0' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'1' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'2' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'3' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'4' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'5' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'6' -> listOf(SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'7' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'8' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'9' -> listOf(SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
'.' -> listOf(SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.LETTER_SPACE)
|
||||
'?' -> listOf(SoundTypes.DIT, SoundTypes.DIT, SoundTypes.DAH, SoundTypes.DAH, SoundTypes.DIT, SoundTypes.DIT, SoundTypes.LETTER_SPACE)
|
||||
else -> { listOf() }
|
||||
}
|
||||
|
||||
|
@ -113,60 +113,42 @@ fun KeycodeToSoundSequence(keycode : Int) : List<SoundTypes> {
|
|||
|
||||
}
|
||||
|
||||
|
||||
class DitDahGenerator : MediaDataSource {
|
||||
constructor() : super()
|
||||
|
||||
override fun close() {
|
||||
}
|
||||
override fun getSize() : Long {
|
||||
return 80000;
|
||||
}
|
||||
|
||||
override fun readAt(position: Long, buffer: ByteArray?, offset: Int, size: Int): Int {
|
||||
if(position > 10000000)
|
||||
return -1;
|
||||
/*
|
||||
for(i in 0..size) {
|
||||
val xt = sin((i + offset) / 44e3f) * 255.0f;
|
||||
buffer?.set(i + offset, xt.toByte());
|
||||
}
|
||||
*/
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DitDahGeneratorSettings
|
||||
{
|
||||
var toneFrequency = 650 //TODO These values are duplicated in the settings fragment
|
||||
var WordsPerMinute = 20;
|
||||
//TODO These values are duplicated in the settings fragment
|
||||
var toneFrequency = 650
|
||||
var wordsPerMinute = 20
|
||||
var farnsworthWordsPerMinute = 20
|
||||
}
|
||||
|
||||
class DitDahSoundStream {
|
||||
constructor(config : DitDahGeneratorSettings) {
|
||||
// Farnsworth timing calculations: https://morsecode.world/international/timing.html
|
||||
//TODO: Compute these properly
|
||||
val ditLengthSeconds = 0.15f;
|
||||
val dahLengthSeconds = ditLengthSeconds * 3.0f;
|
||||
|
||||
val ditLengthInSamples = (ditLengthSeconds * mAudioSampleRate).toInt();
|
||||
val dahLengthInSamples = (dahLengthSeconds * mAudioSampleRate).toInt();
|
||||
val interCharacterSpacingInSamples = ditLengthInSamples;
|
||||
val t_dit = 60.0f / (50.0f * config.wordsPerMinute)
|
||||
val t_fdit = ((60.0f / min(config.farnsworthWordsPerMinute, config.wordsPerMinute)) - 31.0f * t_dit) / 19.0f
|
||||
|
||||
mDitSound = ShortArray(ditLengthInSamples + interCharacterSpacingInSamples);
|
||||
mDahSound = ShortArray(dahLengthInSamples + interCharacterSpacingInSamples);
|
||||
mWordSpacingSound = ShortArray((dahLengthSeconds * mAudioSampleRate).toInt());
|
||||
mCharacterSpacingSound = ShortArray((ditLengthSeconds * mAudioSampleRate).toInt());
|
||||
val ditLengthInSamples = (t_dit * mAudioSampleRate).toInt()
|
||||
val dahLengthInSamples = (t_dit * 3 * mAudioSampleRate).toInt()
|
||||
val intraCharacterSpacingInSamples = (t_dit * mAudioSampleRate).toInt()
|
||||
val interCharacterSpacingInSamples = (3 * t_fdit * mAudioSampleRate).toInt()
|
||||
val wordSpacingInSamples = (7 * t_fdit * mAudioSampleRate).toInt()
|
||||
|
||||
mDitSound = ShortArray(ditLengthInSamples + intraCharacterSpacingInSamples)
|
||||
mDahSound = ShortArray(dahLengthInSamples + intraCharacterSpacingInSamples)
|
||||
|
||||
// These two subtract the intra-character spacing, because they'll already have been played by the most recent sound
|
||||
mWordSpacingSound = ShortArray(wordSpacingInSamples - intraCharacterSpacingInSamples);
|
||||
mCharacterSpacingSound = ShortArray(interCharacterSpacingInSamples - intraCharacterSpacingInSamples);
|
||||
|
||||
//TODO: Ramp up? Make sound nicer? Seems to have clipping - just in the emulator?
|
||||
val invSampleRate = 1.0 / mAudioSampleRate.toFloat()
|
||||
for(i in 0 until ditLengthInSamples) {
|
||||
mDitSound[i] = (0x7fff.toFloat() * sin( 2.0f * PI * i * config.toneFrequency * invSampleRate)).toShort();
|
||||
mDitSound[i] = (0x7fff.toFloat() * sin( 2.0f * PI * i * config.toneFrequency * invSampleRate)).toShort()
|
||||
}
|
||||
|
||||
for(i in 0 until dahLengthInSamples) {
|
||||
mDahSound[i] = (0x7fff.toFloat() * sin(2.0f * PI * i * config.toneFrequency * invSampleRate) ).toShort();
|
||||
mDahSound[i] = (0x7fff.toFloat() * sin(2.0f * PI * i * config.toneFrequency * invSampleRate) ).toShort()
|
||||
}
|
||||
|
||||
thread() { makeSoundsWorkerThreadFunc() }
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/Sounder"
|
||||
android:id="@+id/sounder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="openSounder"
|
||||
android:text="Button" />
|
||||
android:text="Sounder" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button2"
|
||||
|
|
Loading…
Reference in a new issue