A map of Sheffield from 1795, originally published by William Fairbank and Son in September 1796. You can see all the familiar city place names as farms or villages.

See more detail: https://www.g7smy.co.uk/history/sheffield1795/
A map of Sheffield from 1795, originally published by William Fairbank and Son in September 1796. You can see all the familiar city place names as farms or villages.
See more detail: https://www.g7smy.co.uk/history/sheffield1795/
Colliery Road in Sheffield Brightside is an odd throughway that passes under the Midland Mainline Railway providing a 290m (317 yards) route between the junction of Brightside Lane and Weedon Street to Holywell Road. It is a narrow single track road and the the Brightside Lane end has been cut into the hillside to accommodate the railway junction and sidings above. Nowadays access is limited to pedestrians and cyclists as at 2.32m (7′ 6″) high the bridges are far too low to be of any use for general traffic, the road was closed to motor vehicles in around 2016 and is now part of the Lower Don Valley Cycle Route1. In this short essay I am looking a little into its history, the modifications that have changed the road to accommodate the railway and to speculate why it exists at all.
Two rail bridges run over the road, one for the Midland Mainline, originally the Sheffield to Rotherham Railway, constructed in 1838/39, this bridge is referred to in Drake’s Road Book published in 1840 where he says “We now cross a private road by a beautiful stone bridge – one which is infact the only ornamented bridge upon the line, and also the only one that is graced with parapets”2. The other is now disused, it was part of Brightside Junction giving access to the various steel works as well as a line towards Catcliffe and the south as part of the Sheffield District Railway. As well as the two we now see, sometime between 1880 and 1955 there was also a third single track bridge on the north western side used for the gas and chemical works.
By 1892 Brightside Junction had been added to the railway providing access to the steel works in that part of the Don Valley, at that time Colliery Road was moved to start from near near Naseby Street and run alongside the south-eastern side of the tracks for a short while before ducking under the line, this was to accommodate the embankment built for the junction and the goods line into Brightside Steel works. It wasn’t until 1896 when construction started on the Sheffield District Railway that the road was moved to go between the new railway and the existing goods line, this is when the roads route was fixed to that we see today.
In the 1930’s on the north-eastern side of the tracks Holywell Road was extended from Upwell Street alongside the Gas Works to connect to Limpsfield Road and replacing Burslem Street as part of an expansion of housing into the area. Before then this part of Wincobank was mostly farmland with some industry alongside the railway, most notably the 1906 map (CCLXXXIX.SW) shows the Grimesthorpe Gas Works taking up a large amount of land between Upwell Street and Colliery Road. Looking at the 1926 aerial photo below, the exit of Colliery Road from under the railway can be seen on the far left of the image. Today this land is used by a mail order distribution centre as well as some smaller wholesalers and trade suppliers.
Brightside Colliery was also situated on the north-eastern side, the mine was worked from 1855 to 1886 and the shafts were located at the end of Colliery Road, the colliery was owned by Unwin & Shaw Co. and the pit had two shafts, No. 1 from 1855 to 1871 and No.2 from 1868 to 1886, they worked the Parkgate seam3. From 1883 until around 1955 the site was occupied by the Brightside Chemical Works which was a part of the gas works dealing with sulphur produced from gas manufacture [Sheffield History]. Nowadays the land is used by a self storage facility and a motor vehicle repair company.
The road must of had a high cost of construction, the changes that needed to be made to have it run under the railway including cutting it into the hillside at the Brightside Lane seems a lot of effort has gone into maintaining a private a road that gives such poor access.
A reason for this could be that the colliery supplied coal to the works and foundries on the south-eastern side of the railway, this would justify a bridge for the Sheffield to Rotherham Railway but by the time Brightside Junction was built the mine had been closed for around five years so why go to all the trouble to modify the road? Or is Colliery Road a legacy of the landowner insisting they have access from their farm at The Grange down to Brightside Lane? I can imagine a track existing before the railway and it being kept as a right of way as a route to Brightside village and the bridge across the Don which had been there in one form or another since before 16504.
By comparing the 1854 and 1892 maps above we can see similarities between the buildings being shown, the map also shows there is no roadway from the colliery site into Brightside as a field is in the way. The coming of the railway would have cut access off from Naseby Street and maybe the landowner did not want to sell part of his field, or set the price too high, to permit access to the nearest bridge over the line on Jenkin Road and forcing further development of Colliery Road, the bridge and its parapets.
1838/39 | Sheffield to Rotherham Railway constructed (Midland Mainline) |
1838 | Brightside Station opens |
1855-1871 | Brightside Colliery – Shaft No.1 in operation |
1868-1886 | Brightside Colliery – Shaft No.2 in operation |
c.1855-c.1955 | Grimesthorpe Gas Works in operation |
1883-c.1955 | Brightside Chemical works – part of Grimesthorpe Gas Works |
c.1890 | Brightside Junction added – for the steel works |
1896 | Sheffield District Railway construction starts |
My photos of Colliery Road on flickr
Ordnance Survey maps at the National Library of Scotland:
I have written this Weight Ratio Calculator, it splits a total weight by a ratio to give the weights needed to make up a mixture it can be used for any two-part solution such as epoxy resin or silicone rubber used for moulds.
Link: https://www.g7smy.co.uk/ratio
In this video I am adapting a sewing machine pedal to provide variable speed to my small Dremel drill. It’s not really suitable as the pedal is only rated to handle 80 Watts (0.42 Amps at 240 Volts) while the Dremel can take up to 125 Watts (0.52 Amps) of power, it should be alright for light work.
To control the power the footpedal has two stacks made of loose carbon disks, initially when the pedal is pressed power is passed through these and as more pressure is applied these compress to lower the resistance and allow more power through. When fully on, a metal disc engages across two contacts to allow all the current to pass. I think I would like to have a pedal that could handle more current, possibly something based around how a modern dimmer switch works.
Back in April 2016 I posted details on how a Vacuum Fluorescent Display (VFD) works and a method of driving it from an Arduino type microcontroller using transistors, while this worked well it made for a rather bulky clock. I have now rebuilt the display using proper VFD driver chips, added a rotary switch with LED’s, NeoPixel capability and used a WeMos D1 Mini microcontroller to give me internet access over WiFi so the time and date can be set using the Network Time Protocol (NTP).
This rebuild is much more practical than previously, it is now about 3cm deep and comprises of two boards back to back with pin header plugs/sockets as the interconnects. I have been running the clock for over a year without any problems, apart from having to switch the thing off and back on when Daylight Savings Time changes in the spring and autumn, but that’s a software (lazy programmer) problem.
Three different voltages are needed to operate this clock, 16 Volts for the VFD display anodes, 5 Volts for the microcontroller and NeoPixels and 3.3 Volts for the VFD cathode and other hardware.
From the 12 volt source, I split the power to a XL6009 boost module to supply 16V to the driver IC’s, the 5V power module can supply up to 5A which is enough for around 2 meters of NeoPixels. The 3.3V supply is created using a BA033T fixed voltage regulator running from the 5V module, I have used this to power the VFD cathode, the rotary encoder and VFD driver IC’s as this is the microcontrollers operating voltage.
To drive the anodes on the VFD display I used three MAX6920 Tube Drivers, these have a four wire serial interface and can switch up to 76V on the display.
The physical connections from the driver IC’s to the VFD anodes (grids and elements, see the previous article for definition) are those that are most convenient for the PCB layout without having to use links or through-hole connections as the order of the pins for outputting the display can be mapped later in software.
In this case, the first anode pin on the display is on the right, but the serial data starts with the first MAX6920 chip on the left. I will explain in more detail in the software section below.
The Cathode is run from the 3.3 Volt supply, it has a 3.3V Zenner diode across it to protect against meltdown in case of over-voltage, the cathode is made from of strands of thin tungsten wires and shows as a short when testing in beep mode on the multimeter.
I have used the SparkFun RGB Illuminated Rotary Encoder, as well as the encoding it also includes three LED’s and a push button switch. As the WeMos only has limited number of pins available, I used an MCP23008 I2C I/O Expander. I have covered these encoders in a previous post and have used a similar method here.
In this project it turned out I don’t have much use for the rotary switch, the same result can be achieved with a push button and two LED’s, it goes red when setting the time over WiFi, goes green when the time has finished setting but is off when running normally, the push button displays the date when pushed. I expect the encoder could be used for setting an alarm should I ever have need to add one.
The complete source code I am currently running in the clock can be found on my github repository, it has been written for the Arduino platform and can be considered unfinished and a bit of a mess. In almost all cases this clock will be a custom build with all the scavenged VFD displays being different I’ll be concentrating on how the display is being driven. Note that the software uses Latch and the MAX6920 driver chip uses Load.
To start, here are the physical connections for my VFD display, the display connections are shown looking from the back with pin one on the right, on the anode there are eleven grids and twenty-one elements, each grid illuminates a block of chosen elements. There are three MAX6920 driver chips, the first is on the left data is passed along the serial data line DIN/DOUT to the right:
While I’m not going to worry too much about how the data is sent to the display as this is being handled by the ShiftOutX library it may be useful to have an understanding of the byte order sent to the driver chips.
Breaking out the logic analyser we can see what is going on, in this first image below we can see that between the two markers one and two there are eleven data peaks to match the number of grids on the display the data is processed as a padded eleven character string: ‘ 212848 ‘ the first four contain nothing but the bit selecting grid to be used the next six are busy as they contain the time, and the last is blank. There also appear to be two blank clock cycles after these, this may be a programming error on my part.
the string is iterated so that each grid gets its own character with a very short delay between each this causes the persistence of vision effect and the time appears on the display.
In the next screen capture I have zoomed into more detail, it is showing the fifth grid and is displaying the number two. You can see that there are thirty-two clock cycles sent to the drivers while LOAD is high, this causes the driver chips to direct the data to the outputs. I am not entirely sure why the clock is being run while LOAD is low, I think it may be clearing the buffer before sending the required information.
You can see that 32 bits are sent to the display, but there are 36 pins available on the three MAX6920 drivers, you can also see that the outputs are arranged out of order. How do the drivers know which lines to switch for the display?
First off, an explanation of how the data is constructed then sent to the display. For this example I am sending the number five to the third grid, the data is constructed through the manipulation of a character array in three stages:
1. | blank char array: | 00000000000000000000000000000000 |
2. | set the third grid: | 00000000000000000000000000000100 |
3. | set the number: | 00111000011000011100000000000100 |
Because of the way the bits are ordered when sent to the drivers I have a lookup array VFDlookup to make life easier, in this case grid 3 can be found at position 29 in the VFDdata character array. I start with a blank character array of 32 bits VFDblank and copy this to VFDdata, I then use VFDlookup to find the position of the bit to set: VFDlookup[3] = 29. For the elements the encoding for each character is set in VFDdigits the code for number 5 is 1110000110000111 the code was worked out by experimentation, seeing which element lit up after I had completed the hardware, I have ignored the extra symbols used for the DVD player so I only need to set pins 15-30. Once complete the character array is converted to a unsigned long integer and sent LSBFIRST – Least Significant Bit First via ShiftOut_32.
While that explains the elements encoding to display the correct digit, the grid selection however is a different kettle of fish. I built this clock over a year ago and have forgotten how it works. Grids 1 and 2 are connected to the output pins 1 and 0 on the third driver, pins 2-5 are then skipped, and the rest are connected to the grids, with three more being taken from chip 2. I can’t work out how it knows to skip those pins, or that grid 2 is connected to pin 1. But it is an awesome clock though.
FFmpeg is a command line program to manipulate, convert, record and stream video and audio, it is available for Mac, Linux and Windows. Here is a handy list of commands for reference, these have been tested with version 3.1.12 in a Debian Linux environment. I expect this list to grow over time as needs arise.
• Rescale a 4K video to 1080p
1 2 3 |
ffmpeg -i inputFile.mp4 -c:v libx264 -crf 20 -preset slow -vf scale=1920:1080 outFile.mp4 |
• Convert to H.264 (AVC) codec for use on uploading to YouTube, Vimeo, etc:
1 2 3 |
ffmpeg -i inputFile.mov -f mp4 -vcodec libx264 outputFile.mp4 |
Using this codec reduces the time it takes for the video to be available after upload, however YouTube converts the file again to the VP9 codec and unless you have a popular channel, 100 subscribers or more, then this can take a few days or weeks and in the meantime your video can appear quite poor and blocky even when watching at 1080p, especially when there is a lot of movement like in a car dash-cam video. You can use FFmpeg to encode to VP9 webm format with this bash script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/bin/bash INFILE="$1" PASS1="-pass 1 -b:v 1000K -threads 8 -speed 4 -tile-columns 6 -frame-parallel 1" PASS2="-pass 2 -b:v 1000K -threads 8 -speed 1 -tile-columns 6 -frame-parallel 1 -auto-alt-ref 1" FILENAME=$(basename "$INFILE" | cut -d. -f1) if [[ -z "${FILENAME// }" ]]; then echo "filename missing" exit 1 fi OUTFILE="$FILENAME".webm ffmpeg -y -i "$INFILE" -c:v libvpx-vp9 $PASS1 -g 9999 -aq-mode 0 -an -f webm /dev/null ffmpeg -y -i "$INFILE" -c:v libvpx-vp9 $PASS2 -lag-in-frames 25 -g 9999 -aq-mode 0 -c:a libopus -b:a 64k -f webm "$OUTFILE" |
This script is based on the encoding method shown in the WebM Wiki on my computer it is very slow and takes a quite a few hours to encode just nine minutes of video and the eventual results are so poor you’ll be wondering why you bothered.
• Convert to MP4 for use in Vegas Studio:
1 2 3 |
ffmpeg -i inputFile.mkv -codec copy outputFile.mp4 |
If you have a particularly old/odd video and get lots of pts has no value errors, then try this:
1 2 3 |
ffmpeg -fflags +genpts -i inputFile.mpg -codec copy outputFile.mp4 |
The -fflags +genpts option adds a Presentation Timestamp (PTS) to the frames, this must be before the -i as shown to work. Source.
• Set the video playback speed, this method adjusts the Presentation Timestamp (PTS) on each frame which may not work with older software. To slow down video divide the PTS by your required speed, this example slows the action by two times setpts=PTS/2.0. You can also reduce the number of dropped frames by increasing the frame-rate -r 50, in this case I went from 25fps to 50fps, but depending in the chosen speed frames may still be dropped.
1 2 3 |
ffmpeg -i inputFile.mp4 -r 50 -filter:v "setpts=PTS/2.0" outputfile.mp4 |
Speed up your video by multiplying the PTS, in this case two times faster: setpts=PTS*2.0
1 2 3 |
ffmpeg -i inputFile.mp4 -filter:v "setpts=PTS*2.0" outputfile.mp4 |
• Convert file or extract audio from file into an MP3, the output is set to 128K constant bitrate
1 2 3 |
ffmpeg -i inputFile.mp4 -vn -acodec libmp3lame -b:a 128k outputFile.mp3 |
• Concatenate Video Files
This combines two video files, when using formats such as MP4 or MKV you will need to create intermediate files, otherwise only the first file will be included in the output:
1 2 3 4 5 |
ffmpeg -i file1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts file1_inter.ts ffmpeg -i file2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts file2_inter.ts ffmpeg -i "concat:file1_inter.ts|file2_inter.ts" -f mp4 -vcodec libx265 outputFile.mp4 |
• The opus not found error
When converting a file and you see an error like Could not find tag for codec opus in stream #1… you will need to state the output format
1 2 3 |
ffmpeg -i inputFile.mkv -f mp4 -vcodec libx264 outputFile.mp4 |
Finally, I got around to videoing how I make a PCB. This one is for my Fridge Door Open alarm.
My fridge door tends to rebound when closed staying open a smidgen and letting all the cold out. Rather than just checking that the door is properly shut, I thought it about time to have a microcontroller make a noise when the door has been left open too long.
This circuit uses an Arduino compatible Teensy LC for all the work, it has a phototransistor to sense the state of the fridge door light, a couple of LED’s one to indicate the power and another that comes on when the door is open. There is also Piezo buzzer to make an annoying noise after forty five seconds of door open time. The unit runs of a 3.7v rechargeable Lithium-ion battery and I have added a recharging circuit that takes power via the Teensy’s 5v USB port.
Note: These diagrams show 3.7v as the supply voltage. The Teensy LC can only tolerate a maximum 3.3V on the data pins, so these circuits are driven from the 3V output on the Teensy. They will all work without modification on the 5v Arduino Uno.
I have used a phototransistor to detect the fridge door light, there are two variants of this circuit light activated or dark activated, the 100k resistor can be replaced with a 100k variable if you need to adjust the sensitivity, the 330k resistor provides a weak pull-down on the output. The phototransistor is being used in switch mode to provide a logical output (rather than active mode which provides an output relative to the amount of light), so the output is connected to a digital input on the Arduino. The BC547 transistor is half of the darlington pair to provide extra gain on the output.
I chose the light activated switch, either will do but will provide different logical outputs to your controller. The circuit is enclosed in a small box inside the fridge and connected by ribbon cable to the controller, the ribbon cable is flat and does not upset the magnetic ‘seal’ on the fridge door.
To make some noise I used a piezo buzzer from an old computer, this is driven through a transistor as the Teensy does not provide enough current to drive it directly.
There is also a push button to provide a reset function if the buzzer is sounding while the door is open.
I have also added two LED’s, one to show power and anther that illuminates when the door is opened.
The final circuit if for recharging the battery, it connects to the 5V connection on the Teensy LC so charges the battery when the USB connection is in use. This has been copied from the MCP3831T datasheet.
This uses an interrupt to listen for the light sensor, when the state changes, the door open pin is read to determine if the door is open or not. If it is then a timer is started, this gives you forty five seconds to complete your task before the alarm sounds. With the door closed the timer is stopped and set back to zero. If the sounder goes off while you are rummaging in the fridge the reset button can be pressed, this restarts the timer from zero again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
#define TEST_LED 13 #define FRIDGE_DOOR 6 #define DOOR_LED 8 #define POWER_LED 7 #define RESET_BTN 9 #define BUZZER 10 volatile boolean doorOpen = true; // when the light is on volatile boolean doorTrigger = false; volatile unsigned long lastFridgeMillis = 0; volatile unsigned long fridgeTimeoutMillis = 10; boolean SerialDebug = false; unsigned long doorOpenDelay = 45000; // 45 second timeout before buzzer sounds unsigned long doorOpenTime = 0; unsigned long startTime; unsigned long previousToneMillis = 0; unsigned long toneInterval = 20; boolean soundTheAlarm = false; boolean outputTone = false; int toneMelody[5] = { 1600, 1900, 2000, 2100, 1800 }; const int toneMax = 5; unsigned long lastDoorOpenTime = 1; char pout[80]; // for testing // interrupt function void fridgeDoor() { if ((millis() - lastFridgeMillis) > fridgeTimeoutMillis) { doorOpen = digitalRead(FRIDGE_DOOR); // goes high when dark lastFridgeMillis = millis(); } } // see if the door is open or closed void fridgeDoorOpen() { if (doorOpen == true && doorTrigger == false) { doorTrigger = true; startTime = millis(); if (SerialDebug) { sprintf(pout, "door open: %d", doorOpen); Serial.println(pout); } digitalWrite(DOOR_LED, HIGH); return; } if (doorOpen == false && doorTrigger == true) { doorTrigger = false; if (SerialDebug) { sprintf(pout, "door closed: %d", doorOpen); Serial.println(pout); } startTime = 0; doorOpenTime = 0; soundTheAlarm = false; digitalWrite(DOOR_LED, LOW); return; } } // make some noise void soundAlarm() { if (soundTheAlarm == false) { return; } int ii = 0; while (ii < 6) { if (soundTheAlarm == false) { break; } int i = 0; while (i < toneMax) { if (soundTheAlarm == false) { break; } unsigned long currentMillis = millis(); if((currentMillis - previousToneMillis) > 50) { previousToneMillis = currentMillis; tone(BUZZER,toneMelody[i]-380,60); i++; } } ii++; } } // door open? then increment the timer - sound the alarm void fridgeDoorOpenTimer() { if (doorOpen == false && doorTrigger == false) { return; } if (digitalRead(RESET_BTN) == HIGH) { digitalWrite(POWER_LED, LOW); while (digitalRead(RESET_BTN) == HIGH) { delay(10); } if (SerialDebug) { Serial.println("reset"); } startTime = millis(); soundTheAlarm = false; digitalWrite(POWER_LED, HIGH); } // checking once a second doorOpenTime = millis() - startTime; if (doorOpenTime % 1000 == 0 && doorOpenTime != lastDoorOpenTime) { if (SerialDebug) { Serial.println(doorOpenTime); } if (doorOpenTime > doorOpenDelay) { if (SerialDebug) { Serial.println("Awwooga Awwooga - fridge door open!"); } soundTheAlarm = true; soundAlarm(); } lastDoorOpenTime = doorOpenTime; } } void setup() { pinMode(TEST_LED, OUTPUT); digitalWrite(TEST_LED, HIGH); if (SerialDebug) { Serial.begin(9600); } delay(2000); pinMode(DOOR_LED, OUTPUT); pinMode(POWER_LED, OUTPUT); pinMode(BUZZER, OUTPUT); if (SerialDebug) { Serial.println("ready"); } digitalWrite(POWER_LED, HIGH); pinMode(FRIDGE_DOOR, INPUT); attachInterrupt(digitalPinToInterrupt(FRIDGE_DOOR), fridgeDoor, CHANGE); pinMode(RESET_BTN, INPUT); digitalWrite(TEST_LED, LOW); } void loop() { fridgeDoorOpen(); fridgeDoorOpenTimer(); } |
This all started with wanting to modify a cheap camping light by replacing the LED’s with some colourful NeoPixels. I made a Printed Circuit Board to match the existing PCB disc that held the LED’s, as well as a board for an ATtiny85 microcontroller which incorporated a microphone and vibrating motor of the type used in a old mobile phone. The battery and charging circuit came from a cheap (Poundland) USB powerbank but it turned out that the battery from the powerbank didn’t fit in the camping light how I liked (in a measure twice, cut once sort of thing). Instead I made this:
Here is a small Bash script that converts any supported ffmpeg video format; such as .MKV, .MP4 or .MOV and extracts the audio to an .MP3 file, It will also split that MP3 file into chunks and put them in a convenient directory. You will need to install ffmpeg and mp3splt for your particular platform.
Example Usage:
1 |
./mkv2mp3 "big fat file.mkv" |
This uses ffmpeg to convert “big fat file.mkv” to “big fat file.mp3” and then uses mp3splt to create a directory “big fat file” containing the files 01 – big fat file.mp3, 02 – big fat file.mp3, etc. The MP3 files will be encoded at 128k Constant Bit Rate and each file will be around 50 minutes in length. To install in Debian/Ubuntu use: sudo apt-get install ffmpeg mp3splt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/bin/bash INFILE=$1 BITRATE="128k" LENGTH="50.0" FILENAME=$(basename "$INFILE" | cut -d. -f1) if [[ -z "${FILENAME// }" ]]; then echo "filename missing" exit 1 fi echo "$FILENAME" ffmpeg -i "$INFILE" -vn -acodec libmp3lame -b:a "$BITRATE" "$FILENAME".mp3 mp3splt -t "$LENGTH" -d "$FILENAME" -a -o @n+-+@f "$FILENAME".mp3 |
mp3splt can find the audio in a quiet region near where the split is desired rather than midway through a word, this should make for much cleaner playback across tracks.
This script gives the same results but uses ffmpeg to split the large MP3 file and then adds track numbering metadata using id3v2. To install in Debian/Ubuntu use: sudo apt-get install ffmpeg id3v2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#!/bin/bash INFILE="$1" BITRATE="128k" LENGTH="3000" # 50 minutes in seconds FILENAME=$(basename "$INFILE" | cut -d. -f1) if [[ -z "${FILENAME// }" ]]; then echo "filename missing" exit 1 fi echo "$FILENAME" MP3FILE="$FILENAME".mp3 ffmpeg -y -i "$INFILE" -vn -acodec libmp3lame -b:a "$BITRATE" "$MP3FILE" OUTDIR="$FILENAME" mkdir "$OUTDIR" ffmpeg -y -i "$MP3FILE" -vn -f segment -segment_time "$LENGTH" -c copy "$OUTDIR"/%02d-"$MP3FILE" TRACK=1 IFS=$'\n' FILES=($(find "$OUTDIR" -name "*.mp3" | sort -n)) for file in "${FILES[@]}"; do TN=$(printf %02d $TRACK) id3v2 --track "$TN" --TIT2 "$FILENAME" "$file" TRACK=$((TRACK+1)) done |
Taking this further, I was thinking that it would be nice to have these converted into the M4B Audiobook format for use on my elderly iPod. The script below assumes that you have processed the files as above and have added metadata tags using a tool like mp3tag (yes I know this is for Windows).
To complete this we need to: Combine the multiple MP3 files into one big file, or read the original big file then convert that to M4B format at 96K bit and add chapter marks every ten minutes. For this I have used ffmpeg v3.2.12 and libmp4v2 (for the mp4chaps utility), to install in Debian/Ubuntu use: sudo apt-get install libmp4v2-dev mp4v2-utils ffmpeg
This script works best from a single MP3 file rather than from those that have been re-combined back into a single file, recombining the files caused ffmpeg to exclaim “invalid packet size” and “invalid data” errors. It is able to tell the difference between a directory and a single MP3 and processes the file accordingly, don’t forget to add metadata tags and cover art before you run the script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
#!/bin/bash INFILE="$1" BITRATE="96k" MP3TITLE="" MP3ARTIST="" MP3ALBUM="" MP3COMMENT="" MP3YEAR="" MP3PERFORMER="" #### #### FUNCTIONS #### function setMP3variables() { TAG="$1" VALUE="$2" case "$TAG" in title) MP3TITLE="$VALUE" ;; artist) MP3ARTIST="$VALUE" ;; comment) MP3COMMENT="$VALUE" ;; album_artist) MP3PERFORMER="$VALUE" ;; album) MP3ALBUM="$VALUE" ;; date) MP3YEAR="$VALUE" ;; esac } ## get the tag text from the metadata ## if the line contains an equals (=) then split by the first equals. function getMP3tags() { MP3METADATA="$1" while read -r line do case "$line" in *=*) TAG=${line%%"="*} VALUE=${line#*"="} setMP3variables "$TAG" "$VALUE" ;; esac done < "$MP3METADATA" if [[ ! -z "${MP3TITLE// }" ]]; then OUTFILE="$MP3TITLE".mp3 M4BFILE="$MP3TITLE".m4b fi } #### #### BEGINS #### FILENAME=$(basename "$INFILE" | cut -d. -f1) if [[ -z "${FILENAME// }" ]]; then echo "filename missing" exit 1 fi OLDIFS=$IFS OUTFILE="$FILENAME"_out.mp3 M4BFILE="${OUTFILE:0:-4}".m4b METADATA="${OUTFILE:0:-4}".metadata JPGFILE="${OUTFILE:0:-4}".jpg ## check if input is a diretory if [ -d "$INFILE" ]; then # get the name of the first MP3 file in the directory FIRSTFILE=$(find "$INFILE" -name "*.mp3" | sort -n | head -1) FIRSTFILE=$(basename "$FIRSTFILE") if [[ -z "${FIRSTFILE// }" ]]; then echo "no mp3 files in directory" exit 1 fi ## save the MP3 tag metadata from the first file ffmpeg -y -i "$INFILE/$FIRSTFILE" -f ffmetadata "$METADATA" ## save out the cver art image ffmpeg -y -i "$INFILE/$FIRSTFILE" -an -vcodec copy "$JPGFILE" ## process the tag metadata - put it into the $MP3* strings, set $OUTFILE and $LOGFILE from the title getMP3tags "$METADATA" ## make a bar seperated list of the MP3 files that are to be combined MP3FILES="" IFS=$'\n' FILES=($(find "$INFILE" -name "*.mp3" | sort -n)) for files in "${FILES[@]}"; do MP3FILES+="$files|" done MP3FILES=${MP3FILES:0:-1} ## combine all the MP3 files into one big file ## $OUTFILE is set using either the $FILENAME or $MP3TITLE if that has been used ffmpeg -y -i "concat:$MP3FILES" -vn -vsync 2 -acodec copy "$OUTFILE" else ## process a single MP3 file ## save the MP3 tag metadata from the first file ffmpeg -y -i "$INFILE" -f ffmetadata "$METADATA" ffmpeg -y -i "$INFILE" -an -vcodec copy "$JPGFILE" ## process the tag metadata - put it into the $MP3* strings, set $OUTFILE and $LOGFILE from the title getMP3tags "$METADATA" #ffmpeg -y -i "$INFILE" -vn -vsync 2 -acodec copy "$OUTFILE" OUTFILE="$INFILE" fi ## if there is a cover image, these are are the options for adding it back in WITHJPG=() if [ -e "$JPGFILE" ]; then IFS=$OLDIFS WITHJPG=(-i "$JPGFILE" -metadata:s:v comment="Cover (front)") fi ## create the audiobook M4B file ## and write the metadata into the output file ffmpeg -y -i "$OUTFILE" -i "$METADATA" "${WITHJPG[@]}" -map_metadata 1 -id3v2_version 3 -acodec aac -b:a "$BITRATE" -strict -2 -f mp4 "$M4BFILE" ## add the chapters every 10 minutes mp4chaps -e 600 "$M4BFILE" ## tidy up rm "$MP3METADATA" if [ -e "$JPGFILE" ]; then rm "$JPGFILE" fi #rm "$OUTFILE" echo "finished" |
When encoding to the M4B using a re-combined file I saw a few of these errors from ffmpeg:
1 2 |
[mp3 @ 0x7fffc3bc9ea0] Header missing Error while decoding stream #0:0: Invalid data found when processing input |
These appear to be caused by the mp3splt program from when the original MP3 file was being split into 50 minute chunks, but I can’t hear any effect on the output.
Lots of information about the file can be gotten using mediainfo, to install in Debian/Ubuntu use: sudo apt-get install mediainfo, example use:
1 |
mediainfo --fullscan "big fat file.m4b" |