Skip to content

Commit

Permalink
Fixed sound bug, added new sound config and script for release
Browse files Browse the repository at this point in the history
  • Loading branch information
milosobral committed Jun 7, 2023
1 parent 07861cd commit 7a5a807
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,5 @@ venv.bak/
.mypy_cache/
.dmypy.json
dmypy.json

config_files.txt
301 changes: 301 additions & 0 deletions portiloop/notebooks/test_sounds.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import alsaaudio\n",
"import time\n",
"from multiprocessing import Process\n",
"from threading import Thread\n",
"from pathlib import Path\n",
"from threading import Thread, Lock\n",
"import wave"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['null',\n",
" 'jack',\n",
" 'pulse',\n",
" 'custom',\n",
" 'default',\n",
" 'sysdefault:CARD=excelsiorcard',\n",
" 'dmix:CARD=excelsiorcard,DEV=0',\n",
" 'dmix:CARD=excelsiorcard,DEV=3',\n",
" 'dsnoop:CARD=excelsiorcard,DEV=0',\n",
" 'dsnoop:CARD=excelsiorcard,DEV=3',\n",
" 'hw:CARD=excelsiorcard,DEV=0',\n",
" 'hw:CARD=excelsiorcard,DEV=3',\n",
" 'plughw:CARD=excelsiorcard,DEV=0',\n",
" 'plughw:CARD=excelsiorcard,DEV=3',\n",
" 'usbstream:CARD=excelsiorcard']"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"alsaaudio.pcms()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"class Dummy:\n",
" def __getattr__(self, attr):\n",
" return lambda *args, **kwargs: None"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"class SleepSpindleRealTimeStimulator():\n",
" def __init__(self, soundname=None, lsl_streamer=Dummy(), stimulation_delay=0.0, inter_stim_delay=0.0):\n",
" \"\"\"\n",
" params: \n",
" stimulation_delay (float): simple delay between a detection and a stimulation\n",
" inter_stim_delay (float): time to wait between a stimulation and the next detection \n",
" \"\"\"\n",
" if soundname is None:\n",
" self.soundname = 'stimulus.wav' # CHANGE HERE TO THE SOUND THAT YOU WANT. ONLY ADD THE FILE NAME, NOT THE ENTIRE PATH\n",
" else:\n",
" self.soundname = soundname\n",
"# self._sound = Path(\".\").parent.parent / 'sounds' / self.soundname\n",
" self._sound = f\"../sounds/{self.soundname}\"\n",
" self._thread = None\n",
" self._lock = Lock()\n",
" self.last_detected_ts = time.time()\n",
" self.wait_t = 0.4 # 400 ms\n",
" self.delayer = None\n",
" self.lsl_streamer = lsl_streamer\n",
"\n",
" # Initialize Alsa stuff\n",
" # Open WAV file and set PCM device\n",
" with wave.open(str(self._sound), 'rb') as f: \n",
" device = 'custom'\n",
" \n",
" self.duration = f.getnframes() / float(f.getframerate())\n",
" \n",
" format = None\n",
"\n",
" # 8bit is unsigned in wav files\n",
" if f.getsampwidth() == 1:\n",
" format = alsaaudio.PCM_FORMAT_U8\n",
" # Otherwise we assume signed data, little endian\n",
" elif f.getsampwidth() == 2:\n",
" format = alsaaudio.PCM_FORMAT_S16_LE\n",
" elif f.getsampwidth() == 3:\n",
" format = alsaaudio.PCM_FORMAT_S24_3LE\n",
" elif f.getsampwidth() == 4:\n",
" format = alsaaudio.PCM_FORMAT_S32_LE\n",
" else:\n",
" raise ValueError('Unsupported format')\n",
"\n",
" self.periodsize = f.getframerate() // 8\n",
"\n",
" try:\n",
" self.pcm = alsaaudio.PCM(channels=f.getnchannels(), rate=f.getframerate(), format=format, periodsize=self.periodsize, device=device)\n",
" except alsaaudio.ALSAAudioError as e:\n",
"# print(\"WARNING: Could not open ALSA device as it is already playing a sound. To test stimulation, stop recording and try again.\")\n",
" self.pcm = Dummy()\n",
"\n",
" raise e\n",
" \n",
" # Store data in list to avoid reopening the file\n",
" self.wav_list = []\n",
" while True:\n",
" data = f.readframes(self.periodsize) \n",
" if data:\n",
" self.wav_list.append(data)\n",
" else: \n",
" break\n",
" \n",
" print(f\"DEBUG: Stimulator will play sound {self.soundname}, duration: {self.duration:.3f} seconds\")\n",
"\n",
"\n",
" def play_sound(self):\n",
" '''\n",
" Open the wav file and play a sound\n",
" '''\n",
" print(len(self.wav_list[0]))\n",
" for data in self.wav_list:\n",
" self.pcm.write(data) \n",
" \n",
" # Added this to make sure the thread does not stop before the sound is done playing\n",
" time.sleep(self.duration)\n",
" \n",
" def stimulate(self, detection_signal):\n",
" for sig in detection_signal:\n",
" # We detect a stimulation\n",
" if sig:\n",
" # Record time of stimulation\n",
" ts = time.time()\n",
" \n",
" # Check if time since last stimulation is long enough\n",
" if ts - self.last_detected_ts > self.wait_t:\n",
" if not isinstance(self.delayer, Dummy):\n",
" # If we have a delayer, notify it\n",
" self.delayer.detected()\n",
" # Send the LSL marer for the fast stimulation \n",
" self.send_stimulation(\"FAST_STIM\", False)\n",
" else:\n",
" self.send_stimulation(\"STIM\", True)\n",
"\n",
" self.last_detected_ts = ts\n",
"\n",
" def send_stimulation(self, lsl_text, sound):\n",
" # Send lsl stimulation\n",
" self.lsl_streamer.push_marker(lsl_text)\n",
" # Send sound to patient\n",
" if sound:\n",
" with self._lock:\n",
" if self._thread is None: \n",
" self._thread = Thread(target=self._t_sound, daemon=True)\n",
" self._thread.start()\n",
"\n",
" \n",
" def _t_sound(self):\n",
" self.play_sound()\n",
" with self._lock:\n",
" self._thread = None\n",
" \n",
" def test_stimulus(self):\n",
" with self._lock:\n",
" if self._thread is None:\n",
" self._thread = Thread(target=self._t_sound, daemon=True)\n",
" self._thread.start()\n",
"\n",
" def add_delayer(self, delayer):\n",
" self.delayer = delayer\n",
" self.delayer.stimulate = lambda: self.send_stimulation(\"DELAY_STIM\", True)\n",
"\n",
" def __del__(self):\n",
" print(\"DEBUG: releasing PCM\")\n",
" del self.pcm"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"sound = 'stimulus.wav'"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def sound_process():\n",
"\n",
" stimulator = SleepSpindleRealTimeStimulator(soundname=sound)\n",
" stimulator.play_sound()\n",
" time.sleep(1)\n",
" stimulator.play_sound()\n",
" del stimulator\n",
" print(\"Done\")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"DEBUG: Stimulator will play sound stimulus.wav, duration: 1.588 seconds\n",
"22048\n",
"22048\n",
"DEBUG: releasing PCM\n",
"Done\n"
]
}
],
"source": [
"sound_process()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"DEBUG: Stimulator will play sound stimulus.wav, duration: 1.588 seconds\n",
"22048\n",
"22048\n",
"DEBUG: releasing PCM\n",
"Done\n"
]
}
],
"source": [
"sound_proc = Process(target=sound_process)\n",
"sound_proc.start()\n",
"sound_proc.join()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"sound_proc.join()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
10 changes: 4 additions & 6 deletions portiloop/notebooks/tests.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e24b5e02465d4d10af827cceaa9eebf9",
"model_id": "2e7a7c5d244f46899bc564c1986d59e9",
"version_major": 2,
"version_minor": 0
},
Expand All @@ -25,11 +25,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
"['bias', 'disabled', 'disabled', 'disabled']\n",
"indigo-bunny-portiloop\n",
"DEBUG:/home/mendel/portiloop-software/portiloop/sounds/stimul_15ms.wav\n",
"PID capture: 4194. Kill this process if program crashes before end of execution.\n",
"Saving metadata to /home/mendel/workspace/edf_recording/recording_metadata.json\n"
"PID start process: 5121. Kill this process if program crashes before end of execution.\n",
"PID capture: 5133. Kill this process if program crashes before end of execution.\n",
"Average frequency: 249.95013153215078 Hz for 1754 samples\n"
]
}
],
Expand Down
36 changes: 36 additions & 0 deletions portiloop/setup_files/asound.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pcm.!default {
type plug
slave.pcm "softvol"
}

pcm.dmixer {
type dmix
ipc_key 1024
slave {
pcm "hw:0,0" # Replace with your sound card device
period_time 0
period_size 1024
buffer_size 8192
rate 48000
}
bindings {
0 0
1 1
}
}

ctl.dmixer {
type hw
card 0 # Replace with your sound card device
}

pcm.softvol {
type softvol
slave {
pcm "dmixer"
}
control {
name "SoftMaster"
card 0 # Replace with your sound card device
}
}
19 changes: 19 additions & 0 deletions portiloop/setup_files/get_setup_files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

output_file="config_files.txt"

# List of files to concatenate
file_list=("/etc/asound.conf" "/etc/systemd/system/create_ap.service" "/etc/systemd/system/jupyter.service" "/etc/systemd/system/setup_tables.service" "/usr/local/bin/create_ap0.sh" "/etc/hostapd/hostapd.conf" "/etc/dnsmasq.conf" "/usr/local/bin/setup_tables.sh")

# Create an empty output file
echo -n > "$output_file"

# Loop through each file
for file_name in "${file_list[@]}"; do
echo "File: $(basename "$file_name")" >> "$output_file" # Add file name
echo "---------------------------------------------------" >> "$output_file"
cat "$file_name" >> "$output_file" # Concatenate file content
echo >> "$output_file"
done

echo "Concatenation completed. Output file: $output_file"
3 changes: 3 additions & 0 deletions portiloop/sounds/AA_100SE.wav
Git LFS file not shown
3 changes: 3 additions & 0 deletions portiloop/sounds/shortest_1_100ms (1).wav
Git LFS file not shown
Loading

0 comments on commit 7a5a807

Please sign in to comment.