fixed segfault bug
This commit is contained in:
		
							parent
							
								
									37ec0f7a6d
								
							
						
					
					
						commit
						a78093c6b8
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -9,3 +9,4 @@
 | 
			
		||||
.externalNativeBuild
 | 
			
		||||
.cxx
 | 
			
		||||
local.properties
 | 
			
		||||
/app/release/
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
package net.sergeych.karabass
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.media.AudioAttributes
 | 
			
		||||
import android.media.AudioFormat
 | 
			
		||||
@ -40,6 +39,7 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
    @Volatile private var audioTrack: AudioTrack? = null
 | 
			
		||||
    @Volatile private var isPlaying = false
 | 
			
		||||
    @Volatile private var shouldStop = false
 | 
			
		||||
    @Volatile private var isReleased = false
 | 
			
		||||
    private var currentThread: Thread? = null
 | 
			
		||||
 | 
			
		||||
    // Синхронизированные переменные для потокобезопасности
 | 
			
		||||
@ -55,23 +55,24 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
    private var envelopeState = 0 // 0: idle, 1: attack, 2: release
 | 
			
		||||
 | 
			
		||||
    // Параметры огибающей
 | 
			
		||||
    private val attackSamples = (0.02 * SAMPLE_RATE).toInt()  // Увеличил до 20ms для более плавного старта
 | 
			
		||||
    private val releaseSamples = (0.2 * SAMPLE_RATE).toInt()  // 200ms релиз
 | 
			
		||||
    private val attackSamples = (0.02 * SAMPLE_RATE).toInt()
 | 
			
		||||
    private val releaseSamples = (0.2 * SAMPLE_RATE).toInt()
 | 
			
		||||
 | 
			
		||||
    // Минимальный буфер для low-latency
 | 
			
		||||
    private val bufferSize = 1024 // Увеличил буфер для стабильности
 | 
			
		||||
    private val bufferSize = 1024
 | 
			
		||||
 | 
			
		||||
    // Для управления выводом
 | 
			
		||||
    private var lastBufferWritten = false
 | 
			
		||||
    private var silenceBuffersWritten = 0
 | 
			
		||||
    private val requiredSilenceBuffers = 3 // Дополнительные буферы тишины после затухания
 | 
			
		||||
    private val requiredSilenceBuffers = 3
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        initializeAudioTrack()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initializeAudioTrack() {
 | 
			
		||||
    private fun initializeAudioTrack(): Boolean {
 | 
			
		||||
        synchronized(lock) {
 | 
			
		||||
            if (isReleased) return false
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                // Освобождаем старый AudioTrack если есть
 | 
			
		||||
                audioTrack?.let { track ->
 | 
			
		||||
@ -81,11 +82,11 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                        }
 | 
			
		||||
                        track.release()
 | 
			
		||||
                    } catch (e: Exception) {
 | 
			
		||||
                        e.printStackTrace()
 | 
			
		||||
                        // Игнорируем ошибки при освобождении
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                audioTrack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
			
		||||
                val newTrack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
			
		||||
                    val attributes = AudioAttributes.Builder()
 | 
			
		||||
                        .setUsage(AudioAttributes.USAGE_MEDIA)
 | 
			
		||||
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
 | 
			
		||||
@ -100,7 +101,7 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                    AudioTrack.Builder()
 | 
			
		||||
                        .setAudioAttributes(attributes)
 | 
			
		||||
                        .setAudioFormat(format)
 | 
			
		||||
                        .setBufferSizeInBytes(bufferSize * 4) // Увеличил размер буфера
 | 
			
		||||
                        .setBufferSizeInBytes(bufferSize * 4)
 | 
			
		||||
                        .build()
 | 
			
		||||
                } else {
 | 
			
		||||
                    @Suppress("DEPRECATION")
 | 
			
		||||
@ -109,22 +110,29 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                        SAMPLE_RATE,
 | 
			
		||||
                        AudioFormat.CHANNEL_OUT_MONO,
 | 
			
		||||
                        AudioFormat.ENCODING_PCM_16BIT,
 | 
			
		||||
                        bufferSize * 4, // Увеличил размер буфера
 | 
			
		||||
                        bufferSize * 4,
 | 
			
		||||
                        AudioTrack.MODE_STREAM
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                audioTrack?.play()
 | 
			
		||||
                if (newTrack.state == AudioTrack.STATE_INITIALIZED) {
 | 
			
		||||
                    audioTrack = newTrack
 | 
			
		||||
                    newTrack.play()
 | 
			
		||||
                    return true
 | 
			
		||||
                } else {
 | 
			
		||||
                    newTrack.release()
 | 
			
		||||
                    return false
 | 
			
		||||
                }
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                e.printStackTrace()
 | 
			
		||||
                android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
 | 
			
		||||
                    initializeAudioTrack()
 | 
			
		||||
                }, 100)
 | 
			
		||||
                return false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun startNote(frequency: Float) {
 | 
			
		||||
        if (isReleased) return
 | 
			
		||||
 | 
			
		||||
        synchronized(lock) {
 | 
			
		||||
            targetFrequency = frequency
 | 
			
		||||
 | 
			
		||||
@ -135,7 +143,6 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                envelopeSamples = 0
 | 
			
		||||
                envelopeValue = 0.0
 | 
			
		||||
                currentFrequency = frequency
 | 
			
		||||
                lastBufferWritten = false
 | 
			
		||||
                silenceBuffersWritten = 0
 | 
			
		||||
 | 
			
		||||
                // Сбрасываем фазу при начале новой ноты для предотвращения щелчков
 | 
			
		||||
@ -152,6 +159,8 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun stopNote() {
 | 
			
		||||
        if (isReleased) return
 | 
			
		||||
 | 
			
		||||
        synchronized(lock) {
 | 
			
		||||
            if (isNoteOn) {
 | 
			
		||||
                isNoteOn = false
 | 
			
		||||
@ -162,29 +171,39 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun changeNote(frequency: Float) {
 | 
			
		||||
        if (isReleased) return
 | 
			
		||||
 | 
			
		||||
        synchronized(lock) {
 | 
			
		||||
            targetFrequency = frequency
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun startAudioGeneration() {
 | 
			
		||||
        if (isPlaying) return
 | 
			
		||||
        if (isPlaying || isReleased) return
 | 
			
		||||
 | 
			
		||||
        shouldStop = false
 | 
			
		||||
        isPlaying = true
 | 
			
		||||
        lastBufferWritten = false
 | 
			
		||||
        silenceBuffersWritten = 0
 | 
			
		||||
 | 
			
		||||
        // Проверяем и при необходимости пересоздаем AudioTrack
 | 
			
		||||
        val track = audioTrack
 | 
			
		||||
        if (track == null || track.state != AudioTrack.STATE_INITIALIZED) {
 | 
			
		||||
            initializeAudioTrack()
 | 
			
		||||
            if (!initializeAudioTrack()) {
 | 
			
		||||
                isPlaying = false
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        currentThread = Thread {
 | 
			
		||||
            val buffer = ShortArray(bufferSize)
 | 
			
		||||
            var currentAudioTrack: AudioTrack? = null
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                while (!shouldStop && isPlaying) {
 | 
			
		||||
                // Получаем ссылку на AudioTrack один раз в начале
 | 
			
		||||
                synchronized(lock) {
 | 
			
		||||
                    currentAudioTrack = audioTrack
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                while (!shouldStop && isPlaying && !isReleased) {
 | 
			
		||||
                    // Заполняем буфер
 | 
			
		||||
                    var allSamplesZero = true
 | 
			
		||||
                    for (i in buffer.indices) {
 | 
			
		||||
@ -196,7 +215,7 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Безопасная запись в AudioTrack
 | 
			
		||||
                    val track = audioTrack
 | 
			
		||||
                    val track = currentAudioTrack
 | 
			
		||||
                    if (track?.state == AudioTrack.STATE_INITIALIZED && track.playState == AudioTrack.PLAYSTATE_PLAYING) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            track.write(buffer, 0, buffer.size)
 | 
			
		||||
@ -204,18 +223,29 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                            // Если это последний буфер с данными, запоминаем это
 | 
			
		||||
                            if (allSamplesZero && envelopeState == 0) {
 | 
			
		||||
                                silenceBuffersWritten++
 | 
			
		||||
                                if (silenceBuffersWritten >= requiredSilenceBuffers) {
 | 
			
		||||
                                    lastBufferWritten = true
 | 
			
		||||
                                }
 | 
			
		||||
                            } else {
 | 
			
		||||
                                silenceBuffersWritten = 0
 | 
			
		||||
                                lastBufferWritten = false
 | 
			
		||||
                            }
 | 
			
		||||
                        } catch (e: Exception) {
 | 
			
		||||
                            if (!shouldStop) {
 | 
			
		||||
                                e.printStackTrace()
 | 
			
		||||
                                android.os.Handler(android.os.Looper.getMainLooper()).post {
 | 
			
		||||
                                    initializeAudioTrack()
 | 
			
		||||
                            if (!shouldStop && !isReleased) {
 | 
			
		||||
                                // Пытаемся восстановить AudioTrack
 | 
			
		||||
                                synchronized(lock) {
 | 
			
		||||
                                    if (initializeAudioTrack()) {
 | 
			
		||||
                                        currentAudioTrack = audioTrack
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        break
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // AudioTrack не готов
 | 
			
		||||
                        if (!shouldStop && !isReleased) {
 | 
			
		||||
                            synchronized(lock) {
 | 
			
		||||
                                if (initializeAudioTrack()) {
 | 
			
		||||
                                    currentAudioTrack = audioTrack
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    break
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
@ -225,12 +255,11 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                    // 1. Огибающая завершила релиз
 | 
			
		||||
                    // 2. Мы записали несколько буферов тишины для гарантии
 | 
			
		||||
                    // 3. Нет активной ноты
 | 
			
		||||
                    if (lastBufferWritten) {
 | 
			
		||||
                    if (silenceBuffersWritten >= requiredSilenceBuffers) {
 | 
			
		||||
                        synchronized(lock) {
 | 
			
		||||
                            if (!isNoteOn && envelopeState == 0) {
 | 
			
		||||
                                // Даем дополнительное время на вывод последних буферов
 | 
			
		||||
                                Thread.sleep(50)
 | 
			
		||||
                                isPlaying = false
 | 
			
		||||
                                break
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
@ -247,6 +276,8 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
 | 
			
		||||
    private fun generateSample(): Double {
 | 
			
		||||
        synchronized(lock) {
 | 
			
		||||
            if (isReleased) return 0.0
 | 
			
		||||
 | 
			
		||||
            // Плавное изменение частоты для легато
 | 
			
		||||
            if (abs(currentFrequency - targetFrequency) > 0.1) {
 | 
			
		||||
                currentFrequency += (targetFrequency - currentFrequency) * 0.2f
 | 
			
		||||
@ -318,21 +349,13 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
        // Смешиваем с весами, характерными для тубы
 | 
			
		||||
        val mixed = fundamental * 0.7 + overtone2 * 0.5 + overtone3 * 0.3 + overtone4 * 0.2 + overtone5 * 0.15
 | 
			
		||||
 | 
			
		||||
        val result = tanh(mixed)
 | 
			
		||||
 | 
			
		||||
// Очень мягкое ограничение на резкие переходы
 | 
			
		||||
        return if (abs(result) > 0.8) {
 | 
			
		||||
            result * 0.99 // Слегка уменьшаем пики
 | 
			
		||||
        } else {
 | 
			
		||||
            result
 | 
			
		||||
        }
 | 
			
		||||
        // Очень мягкое ограничение для теплого звука
 | 
			
		||||
//        return tanh(mixed)
 | 
			
		||||
        return tanh(mixed)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun isActive(): Boolean {
 | 
			
		||||
        synchronized(lock) {
 | 
			
		||||
            return isNoteOn || envelopeState != 0
 | 
			
		||||
            return !isReleased && (isNoteOn || envelopeState != 0)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -343,20 +366,14 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun release() {
 | 
			
		||||
        isReleased = true
 | 
			
		||||
        shouldStop = true
 | 
			
		||||
        isPlaying = false
 | 
			
		||||
 | 
			
		||||
        // Даем время на завершение релиза и вывод всех буферов
 | 
			
		||||
        try {
 | 
			
		||||
            Thread.sleep(300)
 | 
			
		||||
        } catch (e: InterruptedException) {
 | 
			
		||||
            // Игнорируем
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Безопасная остановка потока
 | 
			
		||||
        // Останавливаем поток генерации
 | 
			
		||||
        currentThread?.let { thread ->
 | 
			
		||||
            try {
 | 
			
		||||
                thread.join(200)
 | 
			
		||||
                thread.join(500) // Увеличил время ожидания
 | 
			
		||||
            } catch (e: InterruptedException) {
 | 
			
		||||
                thread.interrupt()
 | 
			
		||||
            }
 | 
			
		||||
@ -375,7 +392,7 @@ class AdvancedTubaSynth(private val context: Context) {
 | 
			
		||||
                }
 | 
			
		||||
                track.release()
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                e.printStackTrace()
 | 
			
		||||
                // Игнорируем ошибки при освобождении
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        audioTrack = null
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
[versions]
 | 
			
		||||
agp = "8.13.0"
 | 
			
		||||
kotlin = "2.0.21"
 | 
			
		||||
kotlin = "2.2.20"
 | 
			
		||||
coreKtx = "1.10.1"
 | 
			
		||||
junit = "4.13.2"
 | 
			
		||||
junitVersion = "1.1.5"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user